diff --git a/mm/2s2h/framebuffer_effects.c b/mm/2s2h/framebuffer_effects.c index c2109263f..aade26fda 100644 --- a/mm/2s2h/framebuffer_effects.c +++ b/mm/2s2h/framebuffer_effects.c @@ -10,8 +10,8 @@ s32 gBlurFrameBuffer = -1; // i.e. the VisMono and VisFbuf effects s32 gReusableFrameBuffer = -1; -// Picto box buffer is unscaled at 320x240 -s32 gPictoBoxFrameBuffer = -1; +// N64 resolution sized buffer (320x240), used by picto box and deku bubble +s32 gN64ResFrameBuffer = -1; void FB_CreateFramebuffers(void) { if (gPauseFrameBuffer == -1) { @@ -26,8 +26,8 @@ void FB_CreateFramebuffers(void) { gReusableFrameBuffer = gfx_create_framebuffer(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, true); } - if (gPictoBoxFrameBuffer == -1) { - gPictoBoxFrameBuffer = gfx_create_framebuffer(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, false); + if (gN64ResFrameBuffer == -1) { + gN64ResFrameBuffer = gfx_create_framebuffer(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, false); } } @@ -65,6 +65,47 @@ void FB_CopyToFramebuffer(Gfx** gfxp, s32 fb_src, s32 fb_dest, u8 oncePerFrame, *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); + + 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 = (OTRGetCurrentWidth() - 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++); + + *gfxp = gfx; +} + /** * Draws the texture data from the specified frame buffer as a full screen image */ diff --git a/mm/2s2h/framebuffer_effects.h b/mm/2s2h/framebuffer_effects.h index 5ee8bb99e..8e363b6ca 100644 --- a/mm/2s2h/framebuffer_effects.h +++ b/mm/2s2h/framebuffer_effects.h @@ -6,10 +6,11 @@ extern s32 gPauseFrameBuffer; extern s32 gBlurFrameBuffer; extern s32 gReusableFrameBuffer; -extern s32 gPictoBoxFrameBuffer; +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); diff --git a/mm/src/code/z_play.c b/mm/src/code/z_play.c index 2b79690b5..55878714b 100644 --- a/mm/src/code/z_play.c +++ b/mm/src/code/z_play.c @@ -1476,37 +1476,8 @@ void Play_DrawMain(PlayState* this) { // this->pauseBgPreRender.cvgSave = this->unk_18E58; this->pauseBgPreRender.cvgSave = NULL; - // #region 2S2H [Port] Custom handling for picto box capture - // Copy to our reusable buffer first - FB_CopyToFramebuffer(&sp74, 0, gReusableFrameBuffer, false, NULL); - - // Set the picto framebuffer as the draw target (320x240) - gsSPSetFB(sp74++, gPictoBoxFrameBuffer); - - int16_t s0 = 0, t0 = 0; - int16_t s1 = OTRGetGameRenderWidth(); - int16_t t1 = OTRGetGameRenderHeight(); - - float aspectRatio = OTRGetAspectRatio(); - float fourByThree = 4.0f / 3.0f; - - // Adjsut the texture coordinates so that only a 4:3 region from the center is drawn - // to the picto buffer. Currently ratios smaller than 4:3 will just stretch to fill. - if (aspectRatio > fourByThree) { - int16_t adjustedWidth = OTRGetGameRenderWidth() / (aspectRatio / fourByThree); - s0 = (OTRGetCurrentWidth() - adjustedWidth) / 2; - s1 -= s0; - } - - gDPSetTextureImageFB(sp74++, 0, 0, 0, gReusableFrameBuffer); - gDPImageRectangle(sp74++, 0 << 2, 0 << 2, s0, t0, SCREEN_WIDTH << 2, SCREEN_HEIGHT << 2, s1, t1, - G_TX_RENDERTILE, OTRGetGameRenderWidth(), OTRGetGameRenderHeight()); - - // Read the picto box framebuffer back as a rgba16 buffer - gDPReadFB(sp74++, gPictoBoxFrameBuffer, this->pauseBgPreRender.fbufSave, 0, 0, SCREEN_WIDTH, - SCREEN_HEIGHT, false); - - gsSPResetFB(sp74++); + // #region 2S2H [Port] Custom handling for picto box capture to CPU + FB_WriteFramebufferSliceToCPU(&sp74, this->pauseBgPreRender.fbufSave, false); // #endregion } else { gTransitionTileState = TRANS_TILE_PROCESS; diff --git a/mm/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c b/mm/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c index 32c0edf7f..4c3ed2896 100644 --- a/mm/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c +++ b/mm/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c @@ -8,6 +8,8 @@ #include "overlays/effects/ovl_Effect_Ss_Sbn/z_eff_ss_sbn.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "2s2h/framebuffer_effects.h" + #define FLAGS (ACTOR_FLAG_10 | ACTOR_FLAG_20) #define THIS ((EnArrow*)thisx) @@ -672,6 +674,17 @@ void EnArrow_Draw(Actor* thisx, PlayState* play) { if (this->actor.speed == 0.0f) { func_800B8118(&this->actor, play, 0); + // #region 2S2H [Port] Capture framebuffer to current frame, byteswaped, so that it will + // be read from seg 0x0F in gameplay_keep_DL_06F380 + Gfx* gfx = POLY_XLU_DISP; + FB_WriteFramebufferSliceToCPU(&gfx, play->state.gfxCtx->curFrameBuffer, true); + POLY_XLU_DISP = gfx; + // Only a portion of the buffer is used as a texture, so we need to invalidate by the same offset + // Offset derived from ((ult * width + uls) * 2), with values taken from the DL directly + gSPInvalidateTexCache(POLY_XLU_DISP++, + (uintptr_t)(play->state.gfxCtx->curFrameBuffer) + ((104 * 320 + 144) * 2)); + // #endregion + gSPDisplayList(POLY_XLU_DISP++, gameplay_keep_DL_06F380); gDPSetRenderMode(POLY_XLU_DISP++, G_RM_FOG_SHADE_A, G_RM_AA_ZB_XLU_SURF2); gDPSetCombineLERP(POLY_XLU_DISP++, TEXEL1, 0, PRIM_LOD_FRAC, TEXEL0, TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, @@ -686,6 +699,17 @@ void EnArrow_Draw(Actor* thisx, PlayState* play) { } else { func_800B8050(&this->actor, play, 0); + // #region 2S2H [Port] Capture framebuffer to current frame, byteswaped, so that it will + // be read from seg 0x0F in gameplay_keep_DL_06F380 + Gfx* gfx = POLY_OPA_DISP; + FB_WriteFramebufferSliceToCPU(&gfx, play->state.gfxCtx->curFrameBuffer, true); + POLY_OPA_DISP = gfx; + // Only a portion of the buffer is used as a texture, so we need to invalidate by the same offset + // Offset derived from ((ult * width + uls) * 2), with values taken from the DL directly + gSPInvalidateTexCache(POLY_OPA_DISP++, + (uintptr_t)(play->state.gfxCtx->curFrameBuffer) + ((104 * 320 + 144) * 2)); + // #endregion + gSPDisplayList(POLY_OPA_DISP++, gameplay_keep_DL_06F380); gDPSetCombineLERP(POLY_OPA_DISP++, TEXEL1, 0, PRIM_LOD_FRAC, TEXEL0, TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, COMBINED, 0, PRIMITIVE, 0, 0, 0, 0, COMBINED);