From f655ab592d1a0dcebd8831c70808ff76de29f3b1 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Sun, 17 Dec 2023 15:42:34 -0500 Subject: [PATCH] Re-implement Pause menu Dungeon map texture effects (#3496) * first pass implement dungeon maps * wrap up map dungeon implementation * add comments and enums, rename vars * bump lus --- .../ovl_kaleido_scope/z_kaleido_map_PAL.c | 13 +- .../misc/ovl_kaleido_scope/z_kaleido_scope.h | 4 + .../ovl_kaleido_scope/z_kaleido_scope_PAL.c | 133 +++++++++++++++++- 3 files changed, 138 insertions(+), 12 deletions(-) diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c index 238a1e4e2..28723d3d0 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c @@ -343,7 +343,7 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { // Offset the U value of each vertex to be in the mirror boundary for the map textures if (mirroredWorld) { for (size_t i = 0; i < 8; i++) { - pauseCtx->mapPageVtx[60 + i].v.tc[0] += 48 << 5; + pauseCtx->mapPageVtx[60 + i].v.tc[0] += MAP_48x85_TEX_WIDTH << 5; } } @@ -353,8 +353,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[0]); gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[1]); - gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, 48, 85, 0, G_TX_WRAP | mirrorMode, - G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, MAP_48x85_TEX_WIDTH, + MAP_48x85_TEX_HEIGHT, 0, G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); // Swap vertices to render left half on the right and vice-versa if (mirroredWorld) { @@ -363,9 +364,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); } - gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[1], G_IM_FMT_CI, 48, 85, 0, - G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, - G_TX_NOLOD); + gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[1], G_IM_FMT_CI, MAP_48x85_TEX_WIDTH, + MAP_48x85_TEX_HEIGHT, 0, G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); if (mirroredWorld) { gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h index 6f4ed7ad2..8c214d9b0 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h @@ -13,6 +13,10 @@ extern u8 gItemAgeReqs[]; extern u8 gAreaGsFlags[]; extern bool gSelectingMask; +#define MAP_48x85_TEX_WIDTH 48 +#define MAP_48x85_TEX_HEIGHT 85 +#define MAP_48x85_TEX_SIZE ((MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT) / 2) // 48x85 CI4 texture + #define AGE_REQ_ADULT LINK_AGE_ADULT #define AGE_REQ_CHILD LINK_AGE_CHILD #define AGE_REQ_NONE 9 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 35490f079..877ea7e6f 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 @@ -1205,6 +1205,8 @@ Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, void** textures) { return gfx; } +static uint8_t mapBlendMask[MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT]; + void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { static Color_RGB8 D_8082ACF4[12] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 255, 255, 0 }, { 0, 0, 0 }, @@ -1373,6 +1375,10 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { } } + // Need to invalidate the blend mask every frame. Ideally this would be done in KaleidoScope_DrawDungeonMap + // but the reference is not shared between files + gSPInvalidateTexCache(POLY_KAL_DISP++, mapBlendMask); + if (pauseCtx->pageIndex) { // pageIndex != PAUSE_ITEM gDPPipeSync(OVERLAY_DISP++); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); @@ -3315,13 +3321,118 @@ void KaleidoScope_UpdateCursorSize(PauseContext* pauseCtx) { pauseCtx->cursorVtx[14].v.ob[1] = pauseCtx->cursorVtx[15].v.ob[1] = pauseCtx->cursorVtx[12].v.ob[1] - 16; } +// Modifed map texture buffers for registered blend effects and the room indicator color +static uint8_t mapLeftTexModified[MAP_48x85_TEX_SIZE]; +static uint8_t mapRightTexModified[MAP_48x85_TEX_SIZE]; +static uint8_t* mapLeftTexModifiedRaw = NULL; +static uint8_t* mapRightTexModifiedRaw = NULL; + +// Load dungeon maps into the interface context +// SoH [General] - Modified to account for our resource system and HD textures void KaleidoScope_LoadDungeonMap(PlayState* play) { InterfaceContext* interfaceCtx = &play->interfaceCtx; + // Free old textures + if (mapLeftTexModifiedRaw != NULL) { + free(mapLeftTexModifiedRaw); + mapLeftTexModifiedRaw = NULL; + } + if (mapRightTexModifiedRaw != NULL) { + free(mapRightTexModifiedRaw); + mapRightTexModifiedRaw = NULL; + } + + // Unload original textures to bypass cache result for lookups + ResourceMgr_UnloadOriginalWhenAltExists(sDungeonMapTexs[R_MAP_TEX_INDEX]); + ResourceMgr_UnloadOriginalWhenAltExists(sDungeonMapTexs[R_MAP_TEX_INDEX + 1]); + interfaceCtx->mapSegmentName[0] = sDungeonMapTexs[R_MAP_TEX_INDEX]; interfaceCtx->mapSegmentName[1] = sDungeonMapTexs[R_MAP_TEX_INDEX + 1]; - interfaceCtx->mapSegment[0] = ResourceGetDataByName(sDungeonMapTexs[R_MAP_TEX_INDEX]); - interfaceCtx->mapSegment[1] = ResourceGetDataByName(sDungeonMapTexs[R_MAP_TEX_INDEX + 1]); + + // When the texture is HD (raw) we need to copy a dynamic amount of data + // Otherwise the original asset has a static size + if (ResourceMgr_TexIsRaw(interfaceCtx->mapSegmentName[0])) { + u32 width = ResourceGetTexWidthByName(interfaceCtx->mapSegmentName[0]); + u32 height = ResourceGetTexHeightByName(interfaceCtx->mapSegmentName[0]); + size_t size = (width * height) / 2; // account for CI4 size + + // Resource size being larger than the calculated CI size means it is most likely not a CI4 texture + // Abort early end undo the blended effect by clearing the mask to avoid crashing + if (size < ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0])) { + if (mapBlendMask[0] != 0) { + for (size_t i = 0; i < ARRAY_COUNT(mapBlendMask); i++) { + mapBlendMask[i] = 0; + } + } + + interfaceCtx->mapSegment[0] = NULL; + interfaceCtx->mapSegment[1] = NULL; + + Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[0], mapBlendMask, NULL); + Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[1], mapBlendMask, NULL); + return; + } + + u8* map1TexRaw = ResourceGetDataByName(interfaceCtx->mapSegmentName[0]); + u8* map2TexRaw = ResourceGetDataByName(interfaceCtx->mapSegmentName[1]); + + mapLeftTexModifiedRaw = malloc(size); + mapRightTexModifiedRaw = malloc(size); + + memcpy(mapLeftTexModifiedRaw, map1TexRaw, size); + memcpy(mapRightTexModifiedRaw, map2TexRaw, size); + + interfaceCtx->mapSegment[0] = mapLeftTexModifiedRaw; + interfaceCtx->mapSegment[1] = mapRightTexModifiedRaw; + } else { + u8* map1Tex = ResourceGetDataByName(interfaceCtx->mapSegmentName[0]); + u8* map2Tex = ResourceGetDataByName(interfaceCtx->mapSegmentName[1]); + + memcpy(mapLeftTexModified, map1Tex, MAP_48x85_TEX_SIZE); + memcpy(mapRightTexModified, map2Tex, MAP_48x85_TEX_SIZE); + + interfaceCtx->mapSegment[0] = mapLeftTexModified; + interfaceCtx->mapSegment[1] = mapRightTexModified; + } + + // Mark and register the blend mask for the copied textures + if (mapBlendMask[0] != 1) { + for (size_t i = 0; i < ARRAY_COUNT(mapBlendMask); i++) { + mapBlendMask[i] = 1; + } + } + + Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[0], mapBlendMask, interfaceCtx->mapSegment[0]); + Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[1], mapBlendMask, interfaceCtx->mapSegment[1]); +} + +static uint8_t registeredDungeonMapTextureHook = false; + +void KaleidoScope_RegisterUpdatedDungeonMapTexture() { + if (gPlayState == NULL) { + return; + } + + PauseContext* pauseCtx = &gPlayState->pauseCtx; + + // Kaleido is not open in a dungeon so there is nothing to do + if (R_PAUSE_MENU_MODE < 3 || pauseCtx->state < 4 || pauseCtx->state > 7 || !sInDungeonScene) { + return; + } + + KaleidoScope_UpdateDungeonMap(gPlayState); + + // KaleidoScope_UpdateDungeonMap will update the palette index for the current floor if the cursor is on the floor + // If the player toggles alt assets while the cursor is not in the floor level, then we handle the palette index here + if (gPlayState->sceneNum >= SCENE_DEKU_TREE && gPlayState->sceneNum <= SCENE_TREASURE_BOX_SHOP && + (VREG(30) + 3) == pauseCtx->dungeonMapSlot && (VREG(30) + 3) != pauseCtx->cursorPoint[PAUSE_MAP]) { + + InterfaceContext* interfaceCtx = &gPlayState->interfaceCtx; + int32_t size = ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0]); + + KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], size, interfaceCtx->mapPaletteIndex, 14); + KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], size, interfaceCtx->mapPaletteIndex, 14); + } } void KaleidoScope_UpdateDungeonMap(PlayState* play) { @@ -3333,19 +3444,29 @@ void KaleidoScope_UpdateDungeonMap(PlayState* play) { KaleidoScope_LoadDungeonMap(play); Map_SetFloorPalettesData(play, pauseCtx->dungeonMapSlot - 3); + s32 size = MAP_48x85_TEX_SIZE; + + if (ResourceMgr_TexIsRaw(interfaceCtx->mapSegmentName[0])) { + size = ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0]); + } + if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) { if ((VREG(30) + 3) == pauseCtx->cursorPoint[PAUSE_MAP]) { - // HDTODO: Handle Runtime Modified Textures (HD) - KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], 2040, interfaceCtx->mapPaletteIndex, 14); + KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], size, interfaceCtx->mapPaletteIndex, 14); } } if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) { if ((VREG(30) + 3) == pauseCtx->cursorPoint[PAUSE_MAP]) { - // HDTODO: Handle Runtime Modified Textures (HD) - KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], 2040, interfaceCtx->mapPaletteIndex, 14); + KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], size, interfaceCtx->mapPaletteIndex, 14); } } + + // Register alt listener to update the blended dungeon map textures on alt toggle + if (!registeredDungeonMapTextureHook) { + registeredDungeonMapTextureHook = true; + GameInteractor_RegisterOnAssetAltChange(KaleidoScope_RegisterUpdatedDungeonMapTexture); + } } void KaleidoScope_Update(PlayState* play)