From 8370b9a9a90db232acda96c6bc152a9cb622396d Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sun, 28 Jan 2024 02:00:04 -0500 Subject: [PATCH] Add patch to fix texture sampling seams in pause menu --- patches/fixes.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 patches/fixes.c diff --git a/patches/fixes.c b/patches/fixes.c new file mode 100644 index 0000000..da76518 --- /dev/null +++ b/patches/fixes.c @@ -0,0 +1,217 @@ +#include "patches.h" +#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" + +#define PAGE_BG_WIDTH (PAGE_BG_COLS * PAGE_BG_QUAD_WIDTH) +#define PAGE_BG_HEIGHT (PAGE_BG_ROWS * PAGE_BG_QUAD_HEIGHT) + +#define RECOMP_PAGE_ROW_HEIGHT 14 +#define RECOMP_PAGE_ROW_COUNT ((PAGE_BG_HEIGHT + RECOMP_PAGE_ROW_HEIGHT - 1) / RECOMP_PAGE_ROW_HEIGHT) + +extern s16* sVtxPageQuadsX[VTX_PAGE_MAX]; +extern s16* sVtxPageQuadsWidth[VTX_PAGE_MAX]; +extern s16* sVtxPageQuadsY[VTX_PAGE_MAX]; +extern s16* sVtxPageQuadsHeight[VTX_PAGE_MAX]; + +s16 sVtxPageGameOverSaveQuadsY[VTX_PAGE_SAVE_QUADS] = { + 14, // promptPageVtx[60] QUAD_PROMPT_MESSAGE + -2, // promptPageVtx[64] QUAD_PROMPT_CURSOR_LEFT + -2, // promptPageVtx[68] QUAD_PROMPT_CURSOR_RIGHT + -18, // promptPageVtx[72] QUAD_PROMPT_CHOICE_YES + -18, // promptPageVtx[76] QUAD_PROMPT_CHOICE_NO +}; + +// @recomp patched to draw as strips with bilerp compensation instead of tiles. +s16 KaleidoScope_SetPageVertices(PlayState* play, Vtx* vtx, s16 vtxPage, s16 numQuads) { + PauseContext* pauseCtx = &play->pauseCtx; + GameOverContext* gameOverCtx = &play->gameOverCtx; + s16* quadsX; + s16* quadsWidth; + s16* quadsY; + s16* quadsHeight; + s32 cur_y; + u32 row; + + gSegments[0x0D] = OS_K0_TO_PHYSICAL(play->pauseCtx.iconItemLangSegment); + + cur_y = PAGE_BG_HEIGHT / 2; + + // 2 verts per row plus 2 extra verts at the start and the end. + for (row = 0; row < RECOMP_PAGE_ROW_COUNT + 2; row++) { + s32 next_y = MAX(cur_y - RECOMP_PAGE_ROW_HEIGHT, -PAGE_BG_HEIGHT / 2); + + vtx[4 * row + 0].v.ob[0] = -PAGE_BG_WIDTH / 2; + vtx[4 * row + 1].v.ob[0] = PAGE_BG_WIDTH / 2; + vtx[4 * row + 2].v.ob[0] = -PAGE_BG_WIDTH / 2; + vtx[4 * row + 3].v.ob[0] = PAGE_BG_WIDTH / 2; + + vtx[4 * row + 0].v.ob[1] = cur_y + pauseCtx->offsetY; + vtx[4 * row + 1].v.ob[1] = cur_y + pauseCtx->offsetY; + vtx[4 * row + 2].v.ob[1] = next_y + pauseCtx->offsetY; + vtx[4 * row + 3].v.ob[1] = next_y + pauseCtx->offsetY; + + vtx[4 * row + 0].v.ob[2] = vtx[4 * row + 1].v.ob[2] = vtx[4 * row + 2].v.ob[2] = vtx[4 * row + 3].v.ob[2] = 0; + + vtx[4 * row + 0].v.flag = vtx[4 * row + 1].v.flag = vtx[4 * row + 2].v.flag = vtx[4 * row + 3].v.flag = 0; + + #define PIXEL_OFFSET ((1 << 4)) + + vtx[4 * row + 0].v.tc[0] = PIXEL_OFFSET; + vtx[4 * row + 0].v.tc[1] = (1 << 5) + PIXEL_OFFSET; + vtx[4 * row + 1].v.tc[0] = PAGE_BG_WIDTH * (1 << 5) + PIXEL_OFFSET; + vtx[4 * row + 1].v.tc[1] = (1 << 5) + PIXEL_OFFSET; + vtx[4 * row + 2].v.tc[0] = PIXEL_OFFSET; + vtx[4 * row + 2].v.tc[1] = (cur_y - next_y + 1) * (1 << 5) + PIXEL_OFFSET; + vtx[4 * row + 3].v.tc[0] = PAGE_BG_WIDTH * (1 << 5) + PIXEL_OFFSET; + vtx[4 * row + 3].v.tc[1] = (cur_y - next_y + 1) * (1 << 5) + PIXEL_OFFSET; + + vtx[4 * row + 0].v.cn[0] = vtx[4 * row + 1].v.cn[0] = vtx[4 * row + 2].v.cn[0] = vtx[4 * row + 3].v.cn[0] = 0; + vtx[4 * row + 0].v.cn[1] = vtx[4 * row + 1].v.cn[1] = vtx[4 * row + 2].v.cn[1] = vtx[4 * row + 3].v.cn[1] = 0; + vtx[4 * row + 0].v.cn[2] = vtx[4 * row + 1].v.cn[2] = vtx[4 * row + 2].v.cn[2] = vtx[4 * row + 3].v.cn[2] = 0; + vtx[4 * row + 0].v.cn[3] = vtx[4 * row + 1].v.cn[3] = vtx[4 * row + 2].v.cn[3] = vtx[4 * row + 3].v.cn[3] = pauseCtx->alpha; + + cur_y = next_y; + } + + // These are overlay symbols, so their addresses need to be offset to get their actual loaded vram address. + // TODO remove this once the recompiler is able to handle overlay symbols automatically for patch functions. + s16** sVtxPageQuadsXRelocated = (s16**)((u8*)&sVtxPageQuadsX[0] + gKaleidoMgrOverlayTable[0].offset); + s16** sVtxPageQuadsWidthRelocated = (s16**)((u8*)&sVtxPageQuadsWidth[0] + gKaleidoMgrOverlayTable[0].offset); + s16** sVtxPageQuadsYRelocated = (s16**)((u8*)&sVtxPageQuadsY[0] + gKaleidoMgrOverlayTable[0].offset); + s16** sVtxPageQuadsHeightRelocated = (s16**)((u8*)&sVtxPageQuadsHeight[0] + gKaleidoMgrOverlayTable[0].offset); + + s16 k = 60; + + if (numQuads != 0) { + quadsX = sVtxPageQuadsXRelocated[vtxPage]; + quadsWidth = sVtxPageQuadsWidthRelocated[vtxPage]; + quadsY = sVtxPageQuadsYRelocated[vtxPage]; + quadsHeight = sVtxPageQuadsHeightRelocated[vtxPage]; + s16 i; + + for (i = 0; i < numQuads; i++, k += 4) { + vtx[k + 2].v.ob[0] = vtx[k + 0].v.ob[0] = quadsX[i]; + + vtx[k + 1].v.ob[0] = vtx[k + 3].v.ob[0] = vtx[k + 0].v.ob[0] + quadsWidth[i]; + + if (!IS_PAUSE_STATE_GAMEOVER) { + vtx[k + 0].v.ob[1] = vtx[k + 1].v.ob[1] = quadsY[i] + pauseCtx->offsetY; + } else if (gameOverCtx->state == GAMEOVER_INACTIVE) { + vtx[k + 0].v.ob[1] = vtx[k + 1].v.ob[1] = quadsY[i] + pauseCtx->offsetY; + } else { + vtx[k + 0].v.ob[1] = vtx[k + 1].v.ob[1] = sVtxPageGameOverSaveQuadsY[i] + pauseCtx->offsetY; + } + + vtx[k + 2].v.ob[1] = vtx[k + 3].v.ob[1] = vtx[k + 0].v.ob[1] - quadsHeight[i]; + + vtx[k + 0].v.ob[2] = vtx[k + 1].v.ob[2] = vtx[k + 2].v.ob[2] = vtx[k + 3].v.ob[2] = 0; + + vtx[k + 0].v.flag = vtx[k + 1].v.flag = vtx[k + 2].v.flag = vtx[k + 3].v.flag = 0; + + vtx[k + 0].v.tc[0] = vtx[k + 0].v.tc[1] = vtx[k + 1].v.tc[1] = vtx[k + 2].v.tc[0] = 0; + vtx[k + 1].v.tc[0] = vtx[k + 3].v.tc[0] = quadsWidth[i] << 5; + vtx[k + 2].v.tc[1] = vtx[k + 3].v.tc[1] = quadsHeight[i] << 5; + + vtx[k + 0].v.cn[0] = vtx[k + 2].v.cn[0] = vtx[k + 0].v.cn[1] = vtx[k + 2].v.cn[1] = vtx[k + 0].v.cn[2] = + vtx[k + 2].v.cn[2] = 255; + + vtx[k + 1].v.cn[0] = vtx[k + 3].v.cn[0] = vtx[k + 1].v.cn[1] = vtx[k + 3].v.cn[1] = vtx[k + 1].v.cn[2] = + vtx[k + 3].v.cn[2] = 255; + + vtx[k + 0].v.cn[3] = vtx[k + 2].v.cn[3] = vtx[k + 1].v.cn[3] = vtx[k + 3].v.cn[3] = pauseCtx->alpha; + } + } + return k; +} + +// There's one extra row and column of padding on each side, so the size is +2 in each dimension. +typedef u8 bg_image_t[(2 + PAGE_BG_WIDTH) * (2 + PAGE_BG_HEIGHT)]; + +#define BG_IMAGE_COUNT 4 +TexturePtr* bg_pointers[BG_IMAGE_COUNT]; +bg_image_t bg_images[BG_IMAGE_COUNT]; +u32 bg_image_count = 0; + +void assemble_image(TexturePtr* textures, bg_image_t* image_out) { + u8* pixels_out_start = *image_out; + // Skip a row, it'll be filled in later. + u8* pixels_out = pixels_out_start + PAGE_BG_WIDTH + 2; + for (u32 row = 0; row < PAGE_BG_ROWS; row++) { + u8* texture0 = Lib_SegmentedToVirtual(textures[row + 0]); + u8* texture1 = Lib_SegmentedToVirtual(textures[row + 5]); + u8* texture2 = Lib_SegmentedToVirtual(textures[row + 10]); + for (u32 tile_row = 0; tile_row < PAGE_BG_QUAD_HEIGHT; tile_row++) { + // Write the first column, setting alpha to 0. + *pixels_out = (*texture0) & 0xF0; + pixels_out++; + // Copy a row from each of the tiles into the output texture. + Lib_MemCpy(pixels_out, texture0, PAGE_BG_QUAD_WIDTH * sizeof(u8)); + pixels_out += PAGE_BG_QUAD_WIDTH; + texture0 += PAGE_BG_QUAD_WIDTH; + Lib_MemCpy(pixels_out, texture1, PAGE_BG_QUAD_WIDTH * sizeof(u8)); + pixels_out += PAGE_BG_QUAD_WIDTH; + texture1 += PAGE_BG_QUAD_WIDTH; + Lib_MemCpy(pixels_out, texture2, PAGE_BG_QUAD_WIDTH * sizeof(u8)); + pixels_out += PAGE_BG_QUAD_WIDTH; + texture2 += PAGE_BG_QUAD_WIDTH; + // Write the last column, setting alpha to 0. + *pixels_out = (*(texture2 - 1)) & 0xF0; + pixels_out++; + } + } + // Fill in the padding rows with duplicates of the first and last row but with zero alpha. + for (u32 col = 0; col < PAGE_BG_WIDTH + 2; col++) { + pixels_out_start[col] = pixels_out_start[col + PAGE_BG_WIDTH + 2] & 0xF0; + pixels_out[col] = pixels_out[col - PAGE_BG_WIDTH - 2] & 0xF0; + } +} + +// @recomp patched to fix bilerp seams. +Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, TexturePtr* textures) { + s32 i; + s32 j; + + bg_image_t* cur_image = NULL; + + // Check if this texture set has already been assembled into an image. + u32 image_index; + for (image_index = 0; image_index < bg_image_count; image_index++) { + if (bg_pointers[image_index] == textures) { + cur_image = &bg_images[image_index]; + } + } + + // If no image was found and there's a free image slot, assemble the image. + if (cur_image == NULL && image_index < BG_IMAGE_COUNT) { + assemble_image(textures, &bg_images[image_index]); + bg_pointers[image_index] = textures; + cur_image = &bg_images[image_index]; + bg_image_count++; + } + + if (cur_image == NULL) { + // No image was found and there are no free slots. + return gfx; + } + + // Draw the rows. + for (u32 bg_row = 0; bg_row < RECOMP_PAGE_ROW_COUNT; bg_row++) { + gDPLoadTextureTile(gfx++, *cur_image, + G_IM_FMT_IA, G_IM_SIZ_8b, // fmt, siz + PAGE_BG_WIDTH + 2, PAGE_BG_HEIGHT + 2, // width, height + 0, (bg_row + 0) * RECOMP_PAGE_ROW_HEIGHT, // uls, ult + PAGE_BG_WIDTH + 2, (bg_row + 1) * RECOMP_PAGE_ROW_HEIGHT + 2, // lrs, lrt + 0, // pal + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + gDPSetTileSize(gfx++, G_TX_RENDERTILE, + 0 << G_TEXTURE_IMAGE_FRAC, + 0 << G_TEXTURE_IMAGE_FRAC, + (PAGE_BG_WIDTH + 2) <