From bd7155179ed27a9c453338ede6de6a947f62ecbd Mon Sep 17 00:00:00 2001 From: Archez Date: Thu, 10 Oct 2024 19:45:11 -0400 Subject: [PATCH] Port pause menu framebuffer handling from 2Ship (#4346) * bring over framebuffer effects methods * Implement framebuffer capture and drawing for pause menu * revert hookshot and title cards to draw using original buffers * remove old game over crash fix * Adjust mirror mode handling for kaleido * Avoid flashing the hud when pausing --- soh/include/macros.h | 1 - soh/include/z64.h | 3 - soh/soh/OTRGlobals.cpp | 10 + soh/soh/OTRGlobals.h | 2 + soh/soh/framebuffer_effects.c | 177 ++++++++++++++++++ soh/soh/framebuffer_effects.h | 17 ++ soh/src/code/graph.c | 5 +- soh/src/code/sys_cfb.c | 4 + soh/src/code/z_actor.c | 9 +- soh/src/code/z_kaleido_scope_call.c | 2 +- soh/src/code/z_lights.c | 6 +- soh/src/code/z_play.c | 67 ++++--- soh/src/code/z_player_lib.c | 32 ++-- .../ovl_kaleido_scope/z_kaleido_scope_PAL.c | 8 - 14 files changed, 284 insertions(+), 59 deletions(-) create mode 100644 soh/soh/framebuffer_effects.c create mode 100644 soh/soh/framebuffer_effects.h diff --git a/soh/include/macros.h b/soh/include/macros.h index 93f7e17c6..6288efe59 100644 --- a/soh/include/macros.h +++ b/soh/include/macros.h @@ -184,7 +184,6 @@ extern GraphicsContext* __gfxCtx; #define POLY_XLU_DISP __gfxCtx->polyXlu.p // #region SOH [General] // Upstream TODO: Document reasoning for these only existing in SoH -#define WORLD_OVERLAY_DISP __gfxCtx->worldOverlay.p #define POLY_KAL_DISP __gfxCtx->polyKal.p // #endregion #define OVERLAY_DISP __gfxCtx->overlay.p diff --git a/soh/include/z64.h b/soh/include/z64.h index 36fe91d69..6d79cbc04 100644 --- a/soh/include/z64.h +++ b/soh/include/z64.h @@ -92,7 +92,6 @@ typedef struct { /* 0x00000 */ u16 headMagic; // GFXPOOL_HEAD_MAGIC /* 0x00008 */ Gfx polyOpaBuffer[0x2FC0]; /* 0x0BF08 */ Gfx polyXluBuffer[0x1000]; - /* 0xXXXXX */ Gfx worldOverlayBuffer[0x1000]; /* 0x0BF08 */ Gfx polyKalBuffer[0x1000]; /* 0x0FF08 */ Gfx overlayBuffer[0x800]; /* 0x11F08 */ Gfx workBuffer[0x100]; @@ -140,7 +139,6 @@ typedef struct OSScTask { typedef struct GraphicsContext { /* 0x0000 */ Gfx* polyOpaBuffer; // Pointer to "Zelda 0" /* 0x0004 */ Gfx* polyXluBuffer; // Pointer to "Zelda 1" - /* 0xXXX */ Gfx* worldOverlayBuffer; // Pointer to "Paris" /* 0xXXX */ Gfx* polyKalBuffer; // Pointer to "Rome" /* 0x0008 */ char unk_008[0x08]; // Unused, could this be pointers to "Zelda 2" / "Zelda 3" /* 0x0010 */ Gfx* overlayBuffer; // Pointer to "Zelda 4" @@ -160,7 +158,6 @@ typedef struct GraphicsContext { /* 0x02A8 */ TwoHeadGfxArena overlay; // "Zelda 4" /* 0x02B8 */ TwoHeadGfxArena polyOpa; // "Zelda 0" /* 0x02C8 */ TwoHeadGfxArena polyXlu; // "Zelda 1" - /* 0x0XXX */ TwoHeadGfxArena worldOverlay; // When in Paris... /* 0x0XXX */ TwoHeadGfxArena polyKal; // When in Rome... /* 0x02D8 */ u32 gfxPoolIdx; /* 0x02DC */ u16* curFrameBuffer; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 6dbb3f99f..b7f45f076 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2283,6 +2283,16 @@ extern "C" float OTRGetDimensionFromRightEdge(float v) { return (SCREEN_WIDTH / 2 + SCREEN_HEIGHT / 2 * OTRGetAspectRatio() - (SCREEN_WIDTH - v)); } +// Gets the width of the current render target area +extern "C" uint32_t OTRGetGameRenderWidth() { + return gfx_current_dimensions.width; +} + +// Gets the height of the current render target area +extern "C" uint32_t OTRGetGameRenderHeight() { + return gfx_current_dimensions.height; +} + f32 floorf(f32 x);// RANDOTODO False positive error "allowing all exceptions is incompatible with previous function" f32 ceilf(f32 x); // This gets annoying diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index e392af4be..fefcaff70 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -154,6 +154,8 @@ float OTRGetDimensionFromLeftEdge(float v); float OTRGetDimensionFromRightEdge(float v); int16_t OTRGetRectDimensionFromLeftEdge(float v); int16_t OTRGetRectDimensionFromRightEdge(float v); +uint32_t OTRGetGameRenderWidth(); +uint32_t OTRGetGameRenderHeight(); int AudioPlayer_Buffered(void); int AudioPlayer_GetDesiredBuffered(void); void AudioPlayer_Play(const uint8_t* buf, uint32_t len); diff --git a/soh/soh/framebuffer_effects.c b/soh/soh/framebuffer_effects.c new file mode 100644 index 000000000..0eba6b25b --- /dev/null +++ b/soh/soh/framebuffer_effects.c @@ -0,0 +1,177 @@ +#include "framebuffer_effects.h" +#include "global.h" +#include "OTRGlobals.h" + +int gfx_create_framebuffer(uint32_t width, uint32_t height, uint32_t native_width, uint32_t native_height, + uint8_t resize); + +s32 gPauseFrameBuffer = -1; +s32 gBlurFrameBuffer = -1; +// A framebuffer that should only be used for drawing in the same frame that it is copied too +// i.e. the VisMono and VisFbuf effects +s32 gReusableFrameBuffer = -1; + +// N64 resolution sized buffer (320x240), used by picto box and deku bubble +s32 gN64ResFrameBuffer = -1; + +void FB_CreateFramebuffers(void) { + if (gPauseFrameBuffer == -1) { + gPauseFrameBuffer = gfx_create_framebuffer(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, true); + } + + if (gBlurFrameBuffer == -1) { + gBlurFrameBuffer = gfx_create_framebuffer(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, true); + } + + if (gReusableFrameBuffer == -1) { + gReusableFrameBuffer = gfx_create_framebuffer(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, true); + } + + if (gN64ResFrameBuffer == -1) { + gN64ResFrameBuffer = gfx_create_framebuffer(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, false); + } +} + +/** + * Copies the current texture data from the source frame buffer to the destination frame buffer + * Setting oncePerFrame ensures that the copy will only happen once every game frame. This + * is important for effects that could be affected by increased frame interpolation (like motion blur). + * A pointer to a boolean is passed to the render for the render to set once the copy has been performed. + * This function uses opcodes from f3dex2 but may be called when s2dex is loaded, such as during shrink window. Make + * sure f3dex2 is loaded before this function is called. + */ +void FB_CopyToFramebuffer(Gfx** gfxp, s32 fb_src, s32 fb_dest, u8 oncePerFrame, u8* hasCopied) { + Gfx* gfx = *gfxp; + + gSPMatrix(gfx++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPSetOtherMode(gfx++, + G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_OPA_SURF | G_RM_OPA_SURF2); + + gSPClearGeometryMode(gfx++, G_FOG | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR); + gSPSetGeometryMode(gfx++, G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH); + + gDPSetBlendColor(gfx++, 255, 255, 255, 8); + gDPSetPrimDepth(gfx++, 0xFFFF, 0xFFFF); + + gDPSetEnvColor(gfx++, 255, 255, 255, 255); + gDPSetCombineLERP(gfx++, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, ENVIRONMENT, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, + ENVIRONMENT); + + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + gDPCopyFB(gfx++, fb_dest, fb_src, oncePerFrame, hasCopied); + + *gfxp = gfx; +} + +/** + * Copies a 4:3 slice of the current framebuffer scaled down to 320x240 to a CPU buffer address. + * The buffer output will be in RGBA16 format. + * Specify the byteswap flag to force the buffer data to be written as BigEndian, which is + * required if the buffer is being used as a texture in F3D. + */ +void FB_WriteFramebufferSliceToCPU(Gfx** gfxp, void* buffer, u8 byteSwap) { + Gfx* gfx = *gfxp; + + FB_CopyToFramebuffer(&gfx, 0, gReusableFrameBuffer, false, NULL); + + // Set the N64 resolution framebuffer as the draw target (320x240) + gsSPSetFB(gfx++, gN64ResFrameBuffer); + // Reset scissor for new framebuffer + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + int16_t s0 = 0, t0 = 0; + int16_t s1 = OTRGetGameRenderWidth(); + int16_t t1 = OTRGetGameRenderHeight(); + + float aspectRatio = OTRGetAspectRatio(); + float fourByThree = 4.0f / 3.0f; + + // Adjust the texture coordinates so that only a 4:3 region from the center is drawn + // to the N64 resolution buffer. Currently ratios smaller than 4:3 will just stretch to fill. + if (aspectRatio > fourByThree) { + int16_t adjustedWidth = OTRGetGameRenderWidth() / (aspectRatio / fourByThree); + s0 = (OTRGetGameRenderWidth() - adjustedWidth) / 2; + s1 -= s0; + } + + gDPSetTextureImageFB(gfx++, 0, 0, 0, gReusableFrameBuffer); + gDPImageRectangle(gfx++, 0 << 2, 0 << 2, s0, t0, SCREEN_WIDTH << 2, SCREEN_HEIGHT << 2, s1, t1, G_TX_RENDERTILE, + OTRGetGameRenderWidth(), OTRGetGameRenderHeight()); + + // Read the final N64 framebuffer back as rgba16 into the CPU-side buffer + gDPReadFB(gfx++, gN64ResFrameBuffer, buffer, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, byteSwap); + + gsSPResetFB(gfx++); + // Reset scissor for original framebuffer + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + *gfxp = gfx; +} + +/** + * Draws the texture data from the specified frame buffer as a full screen image + */ +void FB_DrawFromFramebuffer(Gfx** gfxp, s32 fb, u8 alpha) { + Gfx* gfx = *gfxp; + + gSPMatrix(gfx++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPSetEnvColor(gfx++, 255, 255, 255, alpha); + + gDPSetOtherMode(gfx++, + G_AD_NOISE | G_CD_NOISE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE | G_TD_CLAMP | + G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_CLD_SURF | G_RM_CLD_SURF2); + + gSPClearGeometryMode(gfx++, G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR); + gSPSetGeometryMode(gfx++, G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH); + + gDPSetCombineLERP(gfx++, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, ENVIRONMENT, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, + ENVIRONMENT); + + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + gDPSetTextureImageFB(gfx++, 0, 0, 0, fb); + gDPImageRectangle(gfx++, OTRGetRectDimensionFromLeftEdge(0) << 2, 0 << 2, 0, 0, + OTRGetRectDimensionFromRightEdge(SCREEN_WIDTH) << 2, SCREEN_HEIGHT << 2, OTRGetGameRenderWidth(), + OTRGetGameRenderHeight(), G_TX_RENDERTILE, OTRGetGameRenderWidth(), OTRGetGameRenderHeight()); + + *gfxp = gfx; +} + +/** + * Similar to FB_DrawFromFramebuffer, but scales the image relative to the center of the screen. + * This function uses opcodes from f3dex2 but may be called when s2dex is loaded, such as during shrink window. Make + * sure f3dex2 is loaded before this function is called. + */ +void FB_DrawFromFramebufferScaled(Gfx** gfxp, s32 fb, u8 alpha, float scaleX, float scaleY) { + Gfx* gfx = *gfxp; + + gDPSetEnvColor(gfx++, 255, 255, 255, alpha); + + gDPSetOtherMode(gfx++, + G_AD_NOISE | G_CD_NOISE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE | G_TD_CLAMP | + G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_CLD_SURF | G_RM_CLD_SURF2); + + gDPSetCombineLERP(gfx++, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, ENVIRONMENT, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, + ENVIRONMENT); + + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + gDPSetTextureImageFB(gfx++, 0, 0, 0, fb); + + float x0 = gScreenWidth * 0.5f * scaleX; + float y0 = gScreenHeight * 0.5f * scaleY; + + gDPImageRectangle(gfx++, OTRGetRectDimensionFromLeftEdge(x0) << 2, (int)(y0) << 2, 0, 0, + OTRGetRectDimensionFromRightEdge((float)(gScreenWidth - x0)) << 2, + (int)((float)(gScreenHeight - y0)) << 2, OTRGetGameRenderWidth(), OTRGetGameRenderHeight(), + G_TX_RENDERTILE, OTRGetGameRenderWidth(), OTRGetGameRenderHeight()); + + *gfxp = gfx; +} diff --git a/soh/soh/framebuffer_effects.h b/soh/soh/framebuffer_effects.h new file mode 100644 index 000000000..badbc3858 --- /dev/null +++ b/soh/soh/framebuffer_effects.h @@ -0,0 +1,17 @@ +#ifndef FRAMEBUFFER_EFFECTS_H +#define FRAMEBUFFER_EFFECTS_H + +#include + +extern s32 gPauseFrameBuffer; +extern s32 gBlurFrameBuffer; +extern s32 gReusableFrameBuffer; +extern s32 gN64ResFrameBuffer; + +void FB_CreateFramebuffers(void); +void FB_CopyToFramebuffer(Gfx** gfxp, s32 fb_src, s32 fb_dest, u8 oncePerFrame, u8* hasCopied); +void FB_WriteFramebufferSliceToCPU(Gfx** gfxp, void* buffer, u8 byteSwap); +void FB_DrawFromFramebuffer(Gfx** gfxp, s32 fb, u8 alpha); +void FB_DrawFromFramebufferScaled(Gfx** gfxp, s32 fb, u8 alpha, float scaleX, float scaleY); + +#endif // FRAMEBUFFER_EFFECTS_H diff --git a/soh/src/code/graph.c b/soh/src/code/graph.c index db0e8bca3..3b9487f55 100644 --- a/soh/src/code/graph.c +++ b/soh/src/code/graph.c @@ -104,14 +104,12 @@ void Graph_InitTHGA(GraphicsContext* gfxCtx) { pool->tailMagic = GFXPOOL_TAIL_MAGIC; THGA_Ct(&gfxCtx->polyOpa, pool->polyOpaBuffer, sizeof(pool->polyOpaBuffer)); THGA_Ct(&gfxCtx->polyXlu, pool->polyXluBuffer, sizeof(pool->polyXluBuffer)); - THGA_Ct(&gfxCtx->worldOverlay, pool->worldOverlayBuffer, sizeof(pool->worldOverlayBuffer)); THGA_Ct(&gfxCtx->polyKal, pool->polyKalBuffer, sizeof(pool->polyKalBuffer)); THGA_Ct(&gfxCtx->overlay, pool->overlayBuffer, sizeof(pool->overlayBuffer)); THGA_Ct(&gfxCtx->work, pool->workBuffer, sizeof(pool->workBuffer)); gfxCtx->polyOpaBuffer = pool->polyOpaBuffer; gfxCtx->polyXluBuffer = pool->polyXluBuffer; - gfxCtx->worldOverlayBuffer = pool->worldOverlayBuffer; gfxCtx->polyKalBuffer = pool->polyKalBuffer; gfxCtx->overlayBuffer = pool->overlayBuffer; gfxCtx->workBuffer = pool->workBuffer; @@ -324,8 +322,7 @@ void Graph_Update(GraphicsContext* gfxCtx, GameState* gameState) { gSPBranchList(WORK_DISP++, gfxCtx->polyOpaBuffer); gSPBranchList(POLY_OPA_DISP++, gfxCtx->polyXluBuffer); - gSPBranchList(POLY_XLU_DISP++, gfxCtx->worldOverlayBuffer); - gSPBranchList(WORLD_OVERLAY_DISP++, gfxCtx->polyKalBuffer); + gSPBranchList(POLY_XLU_DISP++, gfxCtx->polyKalBuffer); gSPBranchList(POLY_KAL_DISP++, gfxCtx->overlayBuffer); gDPPipeSync(OVERLAY_DISP++); gDPFullSync(OVERLAY_DISP++); diff --git a/soh/src/code/sys_cfb.c b/soh/src/code/sys_cfb.c index 39106c49b..5a1aaba81 100644 --- a/soh/src/code/sys_cfb.c +++ b/soh/src/code/sys_cfb.c @@ -1,5 +1,6 @@ #include "global.h" #include +#include "soh/framebuffer_effects.h" uintptr_t sSysCfbFbPtr[2]; uintptr_t sSysCfbEnd; @@ -40,6 +41,9 @@ void SysCfb_Init(s32 n64dd) { // "Frame buffer addresses are %08x and %08x" //osSyncPrintf("フレームバッファのアドレスは %08x と %08x です\n", sSysCfbFbPtr[0], sSysCfbFbPtr[1]); + + // SOH [Port] Inform LUS on resolution changes + FB_CreateFramebuffers(); } void SysCfb_Reset() { diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index 5b8aa1eca..7c3914095 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1147,17 +1147,16 @@ void TitleCard_Draw(PlayState* play, TitleCardContext* titleCtx) { OPEN_DISPS(play->state.gfxCtx); - // WORLD_OVERLAY_DISP Goes over POLY_XLU_DISP but under POLY_KAL_DISP - WORLD_OVERLAY_DISP = Gfx_SetupDL_52NoCD(WORLD_OVERLAY_DISP); + OVERLAY_DISP = Gfx_SetupDL_52NoCD(OVERLAY_DISP); - gDPSetPrimColor(WORLD_OVERLAY_DISP++, 0, 0, (u8)titleCtx->intensityR, (u8)titleCtx->intensityG, (u8)titleCtx->intensityB, + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, (u8)titleCtx->intensityR, (u8)titleCtx->intensityG, (u8)titleCtx->intensityB, (u8)titleCtx->alpha); - gDPLoadTextureBlock(WORLD_OVERLAY_DISP++, (uintptr_t)titleCtx->texture, G_IM_FMT_IA, + gDPLoadTextureBlock(OVERLAY_DISP++, (uintptr_t)titleCtx->texture, G_IM_FMT_IA, G_IM_SIZ_8b, width, height, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); - gSPWideTextureRectangle(WORLD_OVERLAY_DISP++, titleX, titleY, ((doubleWidth * 2) + titleX) - 4, titleY + (height * 4), + gSPWideTextureRectangle(OVERLAY_DISP++, titleX, titleY, ((doubleWidth * 2) + titleX) - 4, titleY + (height * 4), G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); height = titleCtx->height - height; diff --git a/soh/src/code/z_kaleido_scope_call.c b/soh/src/code/z_kaleido_scope_call.c index 82922acd0..d95819187 100644 --- a/soh/src/code/z_kaleido_scope_call.c +++ b/soh/src/code/z_kaleido_scope_call.c @@ -69,7 +69,7 @@ void KaleidoScopeCall_Update(PlayState* play) { if (ShrinkWindow_GetCurrentVal() == 0) { HREG(80) = 7; HREG(82) = 3; - R_PAUSE_MENU_MODE = 3; + R_PAUSE_MENU_MODE = 1; pauseCtx->unk_1E4 = 0; pauseCtx->unk_1EC = 0; pauseCtx->state = (pauseCtx->state & 0xFFFF) + 1; diff --git a/soh/src/code/z_lights.c b/soh/src/code/z_lights.c index 740a0d9c8..aed55eda0 100644 --- a/soh/src/code/z_lights.c +++ b/soh/src/code/z_lights.c @@ -162,11 +162,7 @@ void Lights_BindAll(Lights* lights, LightNode* listHead, Vec3f* vec) { while (listHead != NULL) { info = listHead->info; - // OTRTODO: we do not know the root cause of the info->type value being invalid - // but this prevents it from crashing the game on the game over screen - if (info->type < 3) { - bindFuncs[info->type](lights, &info->params, vec); - } + bindFuncs[info->type](lights, &info->params, vec); listHead = listHead->next; } } diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index 50416be1f..d85ff6063 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -12,6 +12,7 @@ #include #include "soh/Enhancements/enhancementTypes.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/framebuffer_effects.h" #include @@ -1341,6 +1342,24 @@ void Play_Draw(PlayState* play) { Lights* sp228; Vec3f sp21C; + // #region SOH [Port] Frame buffer effects for pause menu + // Track render size when paused and that a copy was performed + static u32 lastPauseWidth; + static u32 lastPauseHeight; + static u8 hasCapturedPauseBuffer; + u8 recapturePauseBuffer = false; + + // If the size has changed or dropped frames leading to the buffer not being copied, + // set the prerender state back to setup to copy a new frame. + // This requires not rendering kaleido during this copy to avoid kaleido being copied + if ((R_PAUSE_MENU_MODE == 2 || R_PAUSE_MENU_MODE == 3) && + (lastPauseWidth != OTRGetGameRenderWidth() || lastPauseHeight != OTRGetGameRenderHeight() || + !hasCapturedPauseBuffer)) { + R_PAUSE_MENU_MODE = 1; + recapturePauseBuffer = true; + } + // #endregion + OPEN_DISPS(gfxCtx); gSegments[4] = VIRTUAL_TO_PHYSICAL(play->objectCtx.status[play->objectCtx.mainKeepIndex].segment); @@ -1374,8 +1393,8 @@ void Play_Draw(PlayState* play) { func_800AAA50(&play->view, 15); // Flip the projections and invert culling for the OPA and XLU display buffers - // These manage the world and effects - if (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0)) { + // These manage the world and effects when we are not drawing kaleido + if (R_PAUSE_MENU_MODE <= 1 && CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0)) { gSPSetExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); gSPSetExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); gSPMatrix(POLY_OPA_DISP++, play->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); @@ -1455,13 +1474,13 @@ void Play_Draw(PlayState* play) { if (R_PAUSE_MENU_MODE == 3) { Gfx* sp84 = POLY_OPA_DISP; + // SOH [Port] Draw game framebuffer using our custom handling //func_800C24BC(&play->pauseBgPreRender, &sp84); + FB_DrawFromFramebuffer(&sp84, gPauseFrameBuffer, 255); POLY_OPA_DISP = sp84; - //goto Play_Draw_DrawOverlayElements; - } - //else - { + goto Play_Draw_DrawOverlayElements; + } else { s32 sp80; if ((HREG(80) != 10) || (HREG(83) != 0)) { @@ -1535,10 +1554,6 @@ void Play_Draw(PlayState* play) { Environment_FillScreen(gfxCtx, 0, 0, 0, play->unk_11E18, FILL_SCREEN_OPA); } - if ((play->pauseCtx.state != 0) && (HREG(80) != 10) || (HREG(89) != 0)) { - Play_DrawOverlayElements(play); - } - if ((HREG(80) != 10) || (HREG(85) != 0)) { func_800315AC(play, &play->actorCtx); } @@ -1586,17 +1601,36 @@ void Play_Draw(PlayState* play) { play->pauseBgPreRender.fbuf = gfxCtx->curFrameBuffer; play->pauseBgPreRender.fbufSave = (u16*)gZBuffer; - func_800C1F20(&play->pauseBgPreRender, &sp70); + // SOH [Port] Use our custom copy method instead of the prerender system + // func_800C1F20(&play->pauseBgPreRender, &sp70); if (R_PAUSE_MENU_MODE == 1) { play->pauseBgPreRender.cvgSave = (u8*)gfxCtx->curFrameBuffer; - func_800C20B4(&play->pauseBgPreRender, &sp70); + // func_800C20B4(&play->pauseBgPreRender, &sp70); R_PAUSE_MENU_MODE = 2; + + // #region SOH [Port] Custom handling for pause prerender background capture + lastPauseWidth = OTRGetGameRenderWidth(); + lastPauseHeight = OTRGetGameRenderHeight(); + hasCapturedPauseBuffer = false; + + FB_CopyToFramebuffer(&sp70, 0, gPauseFrameBuffer, false, &hasCapturedPauseBuffer); + + // Set the state back to ready after the recapture is done + if (recapturePauseBuffer) { + R_PAUSE_MENU_MODE = 3; + } + // #endregion } else { gTrnsnUnkState = 2; } OVERLAY_DISP = sp70; play->unk_121C7 = 2; SREG(33) |= 1; + + // 2S2H [Port] Continue to render the post world for pausing to avoid flashing the HUD + if (!(gTrnsnUnkState == 1)) { + goto Play_Draw_DrawOverlayElements; + } } else if (R_PAUSE_MENU_MODE != 3) { Play_Draw_DrawOverlayElements: if ((HREG(80) != 10) || (HREG(89) != 0)) { @@ -1627,15 +1661,6 @@ void Play_Draw(PlayState* play) { Camera_Finish(GET_ACTIVE_CAM(play)); - { - Gfx* prevDisplayList = POLY_OPA_DISP; - Gfx* gfxP = Graph_GfxPlusOne(POLY_OPA_DISP); - gSPDisplayList(OVERLAY_DISP++, gfxP); - gSPEndDisplayList(gfxP++); - Graph_BranchDlist(prevDisplayList, gfxP); - POLY_OPA_DISP = gfxP; - } - CLOSE_DISPS(gfxCtx); Interface_DrawTotalGameplayTimer(play); diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index 94538c229..bde5ea431 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -1672,7 +1672,7 @@ void Player_DrawHookshotReticle(PlayState* play, Player* this, f32 hookshotRange if (BgCheck_AnyLineTest3(&play->colCtx, &hookshotStart, &hookshotEnd, &firstHit, &colPoly, 1, 1, 1, 1, &bgId)) { OPEN_DISPS(play->state.gfxCtx); - WORLD_OVERLAY_DISP = Gfx_SetupDL(WORLD_OVERLAY_DISP, 0x07); + OVERLAY_DISP = Gfx_SetupDL(OVERLAY_DISP, 0x07); SkinMatrix_Vec3fMtxFMultXYZW(&play->viewProjectionMtxF, &firstHit, &sp68, &sp64); @@ -1681,22 +1681,22 @@ void Player_DrawHookshotReticle(PlayState* play, Player* this, f32 hookshotRange Matrix_Translate(firstHit.x, firstHit.y, firstHit.z, MTXMODE_NEW); Matrix_Scale(sp60, sp60, sp60, MTXMODE_APPLY); - gSPMatrix(WORLD_OVERLAY_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); - gSPMatrix(WORLD_OVERLAY_DISP++, SEG_ADDR(1, 0), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); - gSPTexture(WORLD_OVERLAY_DISP++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); - gDPLoadTextureBlock(WORLD_OVERLAY_DISP++, gLinkAdultHookshotReticleTex, G_IM_FMT_I, G_IM_SIZ_8b, 64, 64, 0, + gSPMatrix(OVERLAY_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPMatrix(OVERLAY_DISP++, SEG_ADDR(1, 0), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + gSPTexture(OVERLAY_DISP++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); + gDPLoadTextureBlock(OVERLAY_DISP++, gLinkAdultHookshotReticleTex, G_IM_FMT_I, G_IM_SIZ_8b, 64, 64, 0, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD); if (SurfaceType_IsHookshotSurface(&play->colCtx, colPoly, bgId) && CVarGetInteger(CVAR_ENHANCEMENT("HookshotableReticle"), false)) { const Color_RGBA8 defaultColor = { .r = 0, .g = 255, .b = 0, .a = 255 }; const Color_RGBA8 color = CVarGetColor(CVAR_COSMETIC("HookshotReticle.Target.Value"), defaultColor); - gDPSetPrimColor(WORLD_OVERLAY_DISP++, 0, 0, color.r, color.g, color.b, color.a); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, color.r, color.g, color.b, color.a); } else { const Color_RGBA8 defaultColor = { .r = 255, .g = 0, .b = 0, .a = 255 }; const Color_RGBA8 color = CVarGetColor(CVAR_COSMETIC("HookshotReticle.NonTarget.Value"), defaultColor); - gDPSetPrimColor(WORLD_OVERLAY_DISP++, 0, 0, color.r, color.g, color.b, color.a); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, color.r, color.g, color.b, color.a); } - gSPVertex(WORLD_OVERLAY_DISP++, (uintptr_t)gLinkAdultHookshotReticleVtx, 3, 0); - gSP1Triangle(WORLD_OVERLAY_DISP++, 0, 1, 2, 0); + gSPVertex(OVERLAY_DISP++, (uintptr_t)gLinkAdultHookshotReticleVtx, 3, 0); + gSP1Triangle(OVERLAY_DISP++, 0, 1, 2, 0); CLOSE_DISPS(play->state.gfxCtx); } @@ -2095,8 +2095,14 @@ void Player_DrawPauseImpl(PlayState* play, void* gameplayKeep, void* linkObject, Mtx* perspMtx = Graph_Alloc(play->state.gfxCtx, sizeof(Mtx)); Mtx* lookAtMtx = Graph_Alloc(play->state.gfxCtx, sizeof(Mtx)); + u8 mirrorWorldActive = CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0); + OPEN_DISPS(play->state.gfxCtx); + if (mirrorWorldActive) { + gSPSetExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); + } + gSPSegment(POLY_OPA_DISP++, 0x00, NULL); gDPPipeSync(POLY_OPA_DISP++); @@ -2148,7 +2154,7 @@ void Player_DrawPauseImpl(PlayState* play, void* gameplayKeep, void* linkObject, Matrix_SetTranslateRotateYXZ(pos->x - ((CVarGetInteger(CVAR_ENHANCEMENT("PauseLiveLink"), 0) && LINK_AGE_IN_YEARS == YEARS_ADULT) ? 25 : 0), pos->y - (CVarGetInteger(CVAR_GENERAL("PauseTriforce"), 0) ? 16 : 0), pos->z, rot); - Matrix_Scale(scale * (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? -1 : 1), scale, scale, MTXMODE_APPLY); + Matrix_Scale(scale * (mirrorWorldActive ? -1 : 1), scale, scale, MTXMODE_APPLY); gSPSegment(POLY_OPA_DISP++, 0x04, gameplayKeep); gSPSegment(POLY_OPA_DISP++, 0x06, linkObject); @@ -2170,7 +2176,7 @@ void Player_DrawPauseImpl(PlayState* play, void* gameplayKeep, void* linkObject, Matrix_SetTranslateRotateYXZ(pos->x - (LINK_AGE_IN_YEARS == YEARS_ADULT ? 25 : 0), pos->y + 280 + (LINK_AGE_IN_YEARS == YEARS_ADULT ? 48 : 0), pos->z, rot); - Matrix_Scale(scale * (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? -1 : 1), scale * 1, scale * 1, MTXMODE_APPLY); + Matrix_Scale(scale * (mirrorWorldActive ? -1 : 1), scale * 1, scale * 1, MTXMODE_APPLY); Gfx* ohNo = POLY_XLU_DISP; POLY_XLU_DISP = POLY_OPA_DISP; @@ -2181,6 +2187,10 @@ void Player_DrawPauseImpl(PlayState* play, void* gameplayKeep, void* linkObject, POLY_XLU_DISP = ohNo; } + if (mirrorWorldActive) { + gSPClearExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); + } + POLY_OPA_DISP = Play_SetFog(play, POLY_OPA_DISP++); CLOSE_DISPS(play->state.gfxCtx); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index 48b25325b..8b88c4171 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -3272,14 +3272,6 @@ void KaleidoScope_Draw(PlayState* play) { func_800AAA50(&play->view, 15); - // Flip the OPA and XLU projections again as the set view call above reset the original flips from z_play - if (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0)) { - gSPMatrix(POLY_OPA_DISP++, play->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); - gSPMatrix(POLY_XLU_DISP++, play->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); - gSPMatrix(POLY_OPA_DISP++, play->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); - gSPMatrix(POLY_XLU_DISP++, play->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); - } - CLOSE_DISPS(play->state.gfxCtx); }