From 6aa7f36a6d21cad25f6b3cf7c2e86648ff1ca350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo?= Date: Thu, 1 Jan 2026 18:43:12 -0300 Subject: [PATCH] Add pillarboxing to cutscenes. (#35) * Update RT64 to fix scissor origin leaking bug. * Add pillarboxing to cutscenes. --- lib/rt64 | 2 +- patches/graphics.h | 1 + patches/pillarbox_patches.c | 109 +++++++++++++++++++++++++ patches/projection_transform_tagging.c | 5 ++ patches/syms.ld | 1 + patches/transform_ids.h | 4 + src/game/recomp_api.cpp | 5 ++ src/main/main.cpp | 1 + 8 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 patches/pillarbox_patches.c diff --git a/lib/rt64 b/lib/rt64 index c29698d..aec97ec 160000 --- a/lib/rt64 +++ b/lib/rt64 @@ -1 +1 @@ -Subproject commit c29698d15377f8a42c403c2efb0103290843a282 +Subproject commit aec97ec57995526c79243974bd3422beaf8b70bd diff --git a/patches/graphics.h b/patches/graphics.h index 31837f8..d68aa79 100644 --- a/patches/graphics.h +++ b/patches/graphics.h @@ -8,5 +8,6 @@ DECLARE_FUNC(float, recomp_get_target_aspect_ratio, float); DECLARE_FUNC(s32, recomp_get_target_framerate, s32); DECLARE_FUNC(s32, recomp_high_precision_fb_enabled); DECLARE_FUNC(float, recomp_get_resolution_scale); +DECLARE_FUNC(float, recomp_get_cutscene_aspect_ratio); #endif diff --git a/patches/pillarbox_patches.c b/patches/pillarbox_patches.c new file mode 100644 index 0000000..349adb9 --- /dev/null +++ b/patches/pillarbox_patches.c @@ -0,0 +1,109 @@ +#include "patches.h" +#include "transform_ids.h" +#include "functions.h" +#include "graphics.h" + +#define ANIMATED_PILLARBOX_SCALE 0 + +extern enum map_e map_get(void); + +#if ANIMATED_PILLARBOX_SCALE +f32 pillarbox_scale = 0.0f; +#else +f32 pillarbox_scale = 1.0f; +#endif + +bool pillarbox_active() { + switch (map_get()) { + case MAP_1E_CS_START_NINTENDO: + case MAP_1F_CS_START_RAREWARE: + case MAP_20_CS_END_NOT_100: + case MAP_7B_CS_INTRO_GL_DINGPOT_1: + case MAP_7C_CS_INTRO_BANJOS_HOUSE_1: + case MAP_7D_CS_SPIRAL_MOUNTAIN_1: + case MAP_7E_CS_SPIRAL_MOUNTAIN_2: + case MAP_81_CS_INTRO_GL_DINGPOT_2: + case MAP_82_CS_ENTERING_GL_MACHINE_ROOM: + case MAP_83_CS_GAME_OVER_MACHINE_ROOM: + case MAP_84_CS_UNUSED_MACHINE_ROOM: + case MAP_85_CS_SPIRAL_MOUNTAIN_3: + case MAP_86_CS_SPIRAL_MOUNTAIN_4: + case MAP_87_CS_SPIRAL_MOUNTAIN_5: + case MAP_88_CS_SPIRAL_MOUNTAIN_6: + case MAP_89_CS_INTRO_BANJOS_HOUSE_2: + case MAP_8A_CS_INTRO_BANJOS_HOUSE_3: + case MAP_94_CS_INTRO_SPIRAL_7: + case MAP_95_CS_END_ALL_100: + case MAP_96_CS_END_BEACH_1: + case MAP_97_CS_END_BEACH_2: + case MAP_98_CS_END_SPIRAL_MOUNTAIN_1: + case MAP_99_CS_END_SPIRAL_MOUNTAIN_2: + return TRUE; + default: + return FALSE; + } +} + +void pillarbox_draw(Gfx **gfx, Mtx **mtx, Vtx **vtx) { +#if ANIMATED_PILLARBOX_SCALE + const f32 pillarbox_scale_speed = 1.0f * time_getDelta(); + if (pillarbox_active()) { + pillarbox_scale = MIN(pillarbox_scale + pillarbox_scale_speed, 1.0f); + } + else { + pillarbox_scale = MAX(pillarbox_scale - pillarbox_scale_speed, 0.0f); + } +#else + if (!pillarbox_active()) { + return; + } +#endif + + float original_aspect = (float)DEFAULT_FRAMEBUFFER_WIDTH / DEFAULT_FRAMEBUFFER_HEIGHT; + float cur_aspect = recomp_get_target_aspect_ratio(original_aspect); + float target_aspect = MIN(recomp_get_cutscene_aspect_ratio(), cur_aspect); + float target_width = DEFAULT_FRAMEBUFFER_HEIGHT * target_aspect; + float wide_width = DEFAULT_FRAMEBUFFER_HEIGHT * cur_aspect; + float pillar_width = wide_width - target_width * pillarbox_scale; + if (pillar_width >= 0.0f) { + u32 prev_ortho_id = cur_ortho_projection_transform_id; + cur_ortho_projection_transform_id = PROJECTION_PILLARBOX_TRANSFORM_ID; + viewport_setRenderViewportAndOrthoMatrix(gfx, mtx); + + Vtx *verts = *vtx; + for (s32 ix = -1; ix < 2; ix += 2) { + for (s32 iy = 0; iy < 2; iy++) { + (*vtx)->v.ob[0] = ix * pillar_width * 2; + (*vtx)->v.ob[1] = iy * DEFAULT_FRAMEBUFFER_HEIGHT * 4 - DEFAULT_FRAMEBUFFER_HEIGHT * 2; + (*vtx)->v.ob[2] = -0x14; + (*vtx)++; + } + } + + gEXPushOtherMode((*gfx)++); + gEXPushCombineMode((*gfx)++); + gEXPushGeometryMode((*gfx)++); + gSPClearGeometryMode((*gfx)++, G_CULL_BOTH); + gDPSetRenderMode((*gfx)++, G_RM_OPA_SURF, G_RM_OPA_SURF2); + gDPSetCycleType((*gfx)++, G_CYC_1CYCLE); + gDPSetCombineLERP((*gfx)++, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + u32 id = PILLARBOX_RECTANGLE_TRANSFORM_ID_START; + for (s32 ix = -1; ix < 2; ix += 2) { + guTranslate(*mtx, ix * wide_width * 2, 0.0f, 0.0f); + gSPMatrix((*gfx)++, OS_K0_TO_PHYSICAL(*mtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + (*mtx)++; + + gEXMatrixGroupSimpleVerts((*gfx)++, id++, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + gSPVertex((*gfx)++, verts, 4, 0); + gSP1Quadrangle((*gfx)++, 0, 1, 3, 2, 0); + gEXPopMatrixGroup((*gfx)++, G_MTX_MODELVIEW); + } + + gEXPopOtherMode((*gfx)++); + gEXPopCombineMode((*gfx)++); + gEXPopGeometryMode((*gfx)++); + + cur_ortho_projection_transform_id = prev_ortho_id; + } +} \ No newline at end of file diff --git a/patches/projection_transform_tagging.c b/patches/projection_transform_tagging.c index e1c35a2..39d6ecd 100644 --- a/patches/projection_transform_tagging.c +++ b/patches/projection_transform_tagging.c @@ -85,6 +85,7 @@ extern void printbuffer_draw(Gfx **gfx, Mtx **mtx, Vtx **vtx); extern u32 cur_pushed_text_transform_id; extern u32 cur_pushed_text_transform_origin; +extern void pillarbox_draw(Gfx **gdl, Mtx **mptr, Vtx **vptr); // @recomp Patched to set the projection transform ID for the main projection. RECOMP_PATCH void func_802E39D0(Gfx **gdl, Mtx **mptr, Vtx **vptr, s32 framebuffer_idx, s32 arg4) { @@ -173,6 +174,10 @@ RECOMP_PATCH void func_802E39D0(Gfx **gdl, Mtx **mptr, Vtx **vptr, s32 framebuff ) { gctransition_draw(gdl, mptr, vptr); } + + // @recomp Draw a pillarbox over the current scene to hide the extended widescreen area if active (e.g. cutscenes). + pillarbox_draw(gdl, mptr, vptr); + finishFrame(gdl); osWritebackDCache(m_start, sizeof(Mtx) * (*mptr - m_start)); osWritebackDCache(v_start, sizeof(Vtx) * (*vptr - v_start)); diff --git a/patches/syms.ld b/patches/syms.ld index d142bfd..e05d34e 100644 --- a/patches/syms.ld +++ b/patches/syms.ld @@ -47,3 +47,4 @@ recomp_set_right_analog_suppressed = 0x8F0000A8; osContGetReadData_recomp = 0x8F0000AC; bcopy_recomp = 0x8F0000B0; recomp_get_note_saving_enabled = 0x8F0000B4; +recomp_get_cutscene_aspect_ratio = 0x8F0000B8; diff --git a/patches/transform_ids.h b/patches/transform_ids.h index 1e304ca..3e76677 100644 --- a/patches/transform_ids.h +++ b/patches/transform_ids.h @@ -16,6 +16,7 @@ #define PROJECTION_COPYRIGHT_TRANSFORM_ID 0x00001006 #define PROJECTION_GAME_OVER_TRANSFORM_ID 0x00001007 #define PROJECTION_THE_END_TRANSFORM_ID 0x00001008 +#define PROJECTION_PILLARBOX_TRANSFORM_ID 0x00001009 #define PROJECTION_PORTRAIT_TRANSFORM_ID_START 0x00001100 // 1 for each portrait ID // Map models: 0x00400000 - 0x00EFFFFF @@ -107,6 +108,9 @@ #define RAIN_PARTICLE_ID_COUNT 256 #define RAIN_PARTICLE_ID_MAX 0x8000 +// Pillarbox Rectangles: 0x31000000 - 0x31000010 +#define PILLARBOX_RECTANGLE_TRANSFORM_ID_START 0x31000000 + // Markers: 0x10000000 - 0x1FFFFFFF #define BANJO_TRANSFORM_ID_START 0x10000000 #define MARKER_TRANSFORM_ID_START (BANJO_TRANSFORM_ID_START + MARKER_TRANSFORM_ID_COUNT) diff --git a/src/game/recomp_api.cpp b/src/game/recomp_api.cpp index 3f6e161..cc1b89e 100644 --- a/src/game/recomp_api.cpp +++ b/src/game/recomp_api.cpp @@ -101,6 +101,11 @@ extern "C" void recomp_get_target_aspect_ratio(uint8_t* rdram, recomp_context* c } } +extern "C" void recomp_get_cutscene_aspect_ratio(uint8_t *rdram, recomp_context *ctx) { + float ar = 16.0f / 9.0f; + _return(ctx, ar); +} + extern "C" void recomp_get_bgm_volume(uint8_t* rdram, recomp_context* ctx) { _return(ctx, banjo::get_bgm_volume() / 100.0f); } diff --git a/src/main/main.cpp b/src/main/main.cpp index f5b0715..94d253b 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -623,6 +623,7 @@ int main(int argc, char** argv) { REGISTER_FUNC(recomp_get_window_resolution); REGISTER_FUNC(recomp_get_target_aspect_ratio); REGISTER_FUNC(recomp_get_target_framerate); + REGISTER_FUNC(recomp_get_cutscene_aspect_ratio); REGISTER_FUNC(recomp_get_analog_cam_enabled); REGISTER_FUNC(recomp_get_camera_inputs); REGISTER_FUNC(recomp_get_bgm_volume);