From e10b882c0811c1d2c08ea400f4e9313c765c0757 Mon Sep 17 00:00:00 2001 From: PurpleHato Date: Wed, 1 Oct 2025 04:03:55 +0200 Subject: [PATCH] [ADD] - 3D Scenes for pre-render areas (#5804) * Hard removal 2D ones for now * override some scene values for prerender tests (#46) * Adult Link Cloudy * Fix: Child Link Night ToT Exterior * Fix for real now * Tweak: Skybox castle courtyard * WIP - adjusting for patterns * Hooked all code, needs testing * Hookified * Clean up * Update Fog Control * clang * Fix skybox override to only affect 3D pre-rendered scenes Prevent the 3D scene renderer from overriding skyboxes on all scenes Previously, the function was applying skybox changes to every scene, overriding the intended skyboxes throughout the game (Example: Kokiri Forest with a blue sky instead of it's original "greyish" one) Now it only applies custom skybox settings to scenes in the skyboxControlList, preserving original skyboxes for other scenes * Remove commentary and forgot to add Zelda's courtyard skybox * Move code to shipInit. * Early return for VB_SHOULD. * clang * Fix missing ! * Feedback fixes. * clang * Fix CVAR * Modify Skybox for scenes with multiple viewpoints. * setting position change to blend in the "modder stuff can do" * Adressed review * tooltip space missing, oops * InitFunc --------- Co-authored-by: Archez Co-authored-by: Caladius --- .../Graphics/Disable2DBackgrounds.cpp | 161 ++++++++++++++++++ .../GameInteractor_HookTable.h | 1 + .../game-interactor/GameInteractor_Hooks.cpp | 4 + .../game-interactor/GameInteractor_Hooks.h | 1 + .../vanilla-behavior/GIVanillaBehavior.h | 15 ++ soh/soh/SohGui/SohMenuEnhancements.cpp | 7 +- soh/soh/z_play_otr.cpp | 1 + soh/soh/z_scene_otr.cpp | 4 + soh/src/code/z_play.c | 2 + soh/src/code/z_room.c | 4 + soh/src/code/z_vr_box.c | 17 +- 11 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 soh/soh/Enhancements/Graphics/Disable2DBackgrounds.cpp diff --git a/soh/soh/Enhancements/Graphics/Disable2DBackgrounds.cpp b/soh/soh/Enhancements/Graphics/Disable2DBackgrounds.cpp new file mode 100644 index 000000000..9e9faafac --- /dev/null +++ b/soh/soh/Enhancements/Graphics/Disable2DBackgrounds.cpp @@ -0,0 +1,161 @@ +#include +#include "soh/Enhancements/enhancementTypes.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +extern SaveContext gSaveContext; +extern PlayState* gPlayState; +#include "macros.h" +#include "variables.h" +} + +#define CVAR_NAME CVAR_ENHANCEMENT("3DSceneRender") +#define CVAR_VALUE CVarGetInteger(CVAR_NAME, 0) + +std::vector fogControlList = { + SCENE_MARKET_ENTRANCE_DAY, + SCENE_MARKET_ENTRANCE_NIGHT, + SCENE_MARKET_ENTRANCE_RUINS, + SCENE_BACK_ALLEY_DAY, + SCENE_BACK_ALLEY_NIGHT, + SCENE_MARKET_DAY, + SCENE_MARKET_NIGHT, + SCENE_MARKET_RUINS, + SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY, + SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT, + SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS, + SCENE_KNOW_IT_ALL_BROS_HOUSE, + SCENE_TWINS_HOUSE, + SCENE_MIDOS_HOUSE, + SCENE_SARIAS_HOUSE, + SCENE_BACK_ALLEY_HOUSE, + SCENE_BAZAAR, + SCENE_KOKIRI_SHOP, + SCENE_GORON_SHOP, + SCENE_ZORA_SHOP, + SCENE_POTION_SHOP_KAKARIKO, + SCENE_POTION_SHOP_MARKET, + SCENE_BOMBCHU_SHOP, + SCENE_HAPPY_MASK_SHOP, + SCENE_LINKS_HOUSE, + SCENE_DOG_LADY_HOUSE, + SCENE_STABLE, + SCENE_IMPAS_HOUSE, + SCENE_CARPENTERS_TENT, + SCENE_GRAVEKEEPERS_HUT, +}; + +std::vector skyboxSceneControlList = { + SCENE_MARKET_ENTRANCE_DAY, + SCENE_MARKET_ENTRANCE_NIGHT, + SCENE_MARKET_ENTRANCE_RUINS, + SCENE_BACK_ALLEY_DAY, + SCENE_BACK_ALLEY_NIGHT, + SCENE_MARKET_DAY, + SCENE_MARKET_NIGHT, + SCENE_MARKET_RUINS, + SCENE_CASTLE_COURTYARD_ZELDA, + SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY, + SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT, + SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS, + SCENE_FOREST_TEMPLE, +}; + +std::vector skyboxIdControlList = { + SKYBOX_BAZAAR, + SKYBOX_HOUSE_LINK, + SKYBOX_MARKET_ADULT, + SKYBOX_MARKET_CHILD_DAY, + SKYBOX_MARKET_CHILD_NIGHT, + SKYBOX_HAPPY_MASK_SHOP, + SKYBOX_HOUSE_KNOW_IT_ALL_BROTHERS, + SKYBOX_HOUSE_OF_TWINS, + SKYBOX_STABLES, + SKYBOX_HOUSE_KAKARIKO, + SKYBOX_KOKIRI_SHOP, + SKYBOX_GORON_SHOP, + SKYBOX_ZORA_SHOP, + SKYBOX_POTION_SHOP_KAKARIKO, + SKYBOX_POTION_SHOP_MARKET, + SKYBOX_HOUSE_RICHARD, + SKYBOX_HOUSE_IMPA, + SKYBOX_TENT, + SKYBOX_HOUSE_MIDO, + SKYBOX_HOUSE_SARIA, + SKYBOX_HOUSE_ALLEY, +}; + +void Register3DPreRenderedScenes() { + COND_HOOK(AfterSceneCommands, CVAR_VALUE, [](int16_t sceneNum) { + // Check if this scene is in the skyboxControlList + bool shouldControlSkybox = false; + for (const auto& scene : skyboxSceneControlList) { + if (sceneNum == scene) { + shouldControlSkybox = true; + break; + } + } + + if (shouldControlSkybox) { + // Add a skybox on scenes from skyboxSceneControlList + gPlayState->envCtx.skyboxDisabled = false; + + // Replace skybox with normal sky + gPlayState->skyboxId = SKYBOX_NORMAL_SKY; + // Apply the always cloudy skybox as an adult for Temple of Time and the Market + if (sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS || sceneNum == SCENE_MARKET_RUINS || + sceneNum == SCENE_MARKET_ENTRANCE_RUINS) { + gWeatherMode = 3; + } + } + }); + + COND_HOOK(OnPlayDrawBegin, CVAR_VALUE, []() { + if (!CVarGetInteger(CVAR_ENHANCEMENT("3DSceneRender"), 0)) { + return; + } + + for (auto& scene : fogControlList) { + if (scene == gPlayState->sceneNum) { + if ((HREG(80) != 10) || (HREG(82) != 0)) { + // Furthest possible fog and zFar + gPlayState->view.zFar = 12800; + gPlayState->lightCtx.fogNear = 996; // Set to 1000 to complete disable fog entirely + gPlayState->lightCtx.fogFar = 12800; + // General gray fog color + gPlayState->lightCtx.fogColor[0] = 100; + gPlayState->lightCtx.fogColor[1] = 100; + gPlayState->lightCtx.fogColor[2] = 100; + } + break; + } + } + }); + REGISTER_VB_SHOULD(VB_DRAW_2D_BACKGROUND, { + if (CVAR_VALUE) { + *should = false; + return; + } + }); + + REGISTER_VB_SHOULD(VB_LOAD_SKYBOX, { + if (!gPlayState) { + return; + } + + if (!CVAR_VALUE) { + return; + } + + for (auto& skybox : skyboxIdControlList) { + if (gPlayState->skyboxCtx.skyboxId == skybox) { + gPlayState->skyboxCtx.unk_140 = 0; + *should = false; + return; + } + } + }); +} + +static RegisterShipInitFunc PreRender3DInitFunc(Register3DPreRenderedScenes, { CVAR_NAME }); \ No newline at end of file diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 2b28198d6..b5b1cfbfe 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -42,6 +42,7 @@ DEFINE_HOOK(OnPlayerFirstPersonControl, (Player * player)); DEFINE_HOOK(OnPlayerProcessStick, ()); DEFINE_HOOK(OnPlayerShieldControl, (float_t * sp50, float_t* sp54)); DEFINE_HOOK(OnPlayDestroy, ()); +DEFINE_HOOK(OnPlayDrawBegin, ()); DEFINE_HOOK(OnPlayDrawEnd, ()); DEFINE_HOOK(OnVanillaBehavior, (GIVanillaBehavior flag, bool* result, va_list originalArgs)); DEFINE_HOOK(OnSaveFile, (int32_t fileNum)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index f69cc29af..f374aa2d7 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -186,6 +186,10 @@ void GameInteractor_ExecuteOnPlayDestroy() { GameInteractor::Instance->ExecuteHooks(); } +void GameInteractor_ExecuteOnPlayDrawBegin() { + GameInteractor::Instance->ExecuteHooks(); +} + void GameInteractor_ExecuteOnPlayDrawEnd() { GameInteractor::Instance->ExecuteHooks(); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index a6f1563f2..e22d12dbd 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -45,6 +45,7 @@ void GameInteractor_ExecuteOnPlayerShieldControl(float_t* sp50, float_t* sp54); void GameInteractor_ExecuteOnPlayerProcessStick(); void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price); void GameInteractor_ExecuteOnPlayDestroy(); +void GameInteractor_ExecuteOnPlayDrawBegin(); void GameInteractor_ExecuteOnPlayDrawEnd(); bool GameInteractor_Should(GIVanillaBehavior flag, uint32_t result, ...); diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 6eada52f6..cc32829b7 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -2257,6 +2257,20 @@ typedef enum { // #### `result` // ```c + // CVarGetInteger(CVAR_ENHANCEMENT("3DSceneRender"), 0) + // ``` + // #### `args` + // - None + VB_DRAW_2D_BACKGROUND, + + // #### `result` + // ```c + // CVarGetInteger(CVAR_ENHANCEMENT("3DSceneRender"), 0) + // ``` + // #### `args` + // - None + VB_LOAD_SKYBOX, + // true // ``` // #### `args` @@ -2270,6 +2284,7 @@ typedef enum { // #### `args` // - `*Player` VB_SET_STATIC_FLOOR_TYPE, + } GIVanillaBehavior; #endif diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 6e967951d..2f37291af 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -555,12 +555,17 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().Tooltip( "Disables Grottos rotating with the Camera. To be used in conjuction with mods that want to " "replace grottos with 3D objects.")); + AddWidget(path, "Disable 2D Pre-Rendered Scenes", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("3DSceneRender")) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip("Disables 2D pre-rendered backgrounds. Enable this when using a mod that " + "implements 3D backdrops for these areas.\n" + "Requires Scene Change to alter.")); AddWidget(path, "Ingame Text Spacing: %d", WIDGET_CVAR_SLIDER_INT) .CVar(CVAR_ENHANCEMENT("TextSpacing")) .RaceDisable(false) .Options(IntSliderOptions().Min(4).Max(6).DefaultValue(6).Tooltip( "Space between text characters (useful for HD font textures).")); - AddWidget(path, "Models & Textures", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Disable LOD", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableLOD")) diff --git a/soh/soh/z_play_otr.cpp b/soh/soh/z_play_otr.cpp index 23e61261c..5055b969b 100644 --- a/soh/soh/z_play_otr.cpp +++ b/soh/soh/z_play_otr.cpp @@ -79,6 +79,7 @@ void OTRPlay_InitScene(PlayState* play, s32 spawn) { YREG(15) = 0; gSaveContext.worldMapArea = 0; OTRScene_ExecuteCommands(play, (SOH::Scene*)play->sceneSegment); + GameInteractor_ExecuteAfterSceneCommands(play->sceneNum); Play_InitEnvironment(play, play->skyboxId); /* auto data = static_cast(Ship::Context::GetInstance() diff --git a/soh/soh/z_scene_otr.cpp b/soh/soh/z_scene_otr.cpp index b4f8a29e8..1baf6151d 100644 --- a/soh/soh/z_scene_otr.cpp +++ b/soh/soh/z_scene_otr.cpp @@ -472,6 +472,10 @@ extern "C" s32 OTRfunc_800973FC(PlayState* play, RoomContext* roomCtx) { gSegments[3] = VIRTUAL_TO_PHYSICAL(roomCtx->unk_34); OTRScene_ExecuteCommands(play, (SOH::Scene*)roomCtx->roomToLoad); + if (!GameInteractor_Should(VB_DRAW_2D_BACKGROUND, true)) { + play->envCtx.skyboxDisabled = false; + } + Player_SetBootData(play, GET_PLAYER(play)); Actor_SpawnTransitionActors(play, &play->actorCtx); diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index dfc832fc7..b1a714dcb 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -1390,6 +1390,8 @@ void Play_Draw(PlayState* play) { Gfx_SetupFrame(gfxCtx, 0, 0, 0); if ((HREG(80) != 10) || (HREG(82) != 0)) { + GameInteractor_ExecuteOnPlayDrawBegin(); + POLY_OPA_DISP = Play_SetFog(play, POLY_OPA_DISP); POLY_XLU_DISP = Play_SetFog(play, POLY_XLU_DISP); diff --git a/soh/src/code/z_room.c b/soh/src/code/z_room.c index 60fa3f0ff..493752555 100644 --- a/soh/src/code/z_room.c +++ b/soh/src/code/z_room.c @@ -5,6 +5,7 @@ #include "global.h" #include "vt.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include #include @@ -256,6 +257,9 @@ s32 swapAndConvertJPEG(void* data) { void Room_DrawBackground2D(Gfx** gfxP, void* tex, void* tlut, u16 width, u16 height, u8 fmt, u8 siz, u16 tlutMode, u16 tlutCount, f32 offsetX, f32 offsetY) { + if (!GameInteractor_Should(VB_DRAW_2D_BACKGROUND, true)) { + return; + } Gfx* gfx = *gfxP; uObjBg* bg; diff --git a/soh/src/code/z_vr_box.c b/soh/src/code/z_vr_box.c index c11db1605..f03f9dc7f 100644 --- a/soh/src/code/z_vr_box.c +++ b/soh/src/code/z_vr_box.c @@ -71,6 +71,9 @@ #include "assets/textures/skyboxes/vr_holy1_static.h" #include "assets/textures/skyboxes/vr_holy1_pal_static.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" + u32 D_8012AC90[4] = { 0x00000000, 0x00010000, @@ -449,17 +452,23 @@ void func_800AF178(SkyboxContext* skyboxCtx, s32 arg1) { void LoadSkyboxTex(SkyboxContext* skyboxCtx, int segmentIndex, int imageIndex, char* tex, int width, int height, int offsetW, int offsetH) { - skyboxCtx->textures[segmentIndex][imageIndex] = tex; + if (GameInteractor_Should(VB_LOAD_SKYBOX, true)) { + skyboxCtx->textures[segmentIndex][imageIndex] = tex; + } } void LoadSkyboxTexAtOffset(SkyboxContext* skyboxCtx, int segmentIndex, int imageIndex, char* tex, int width, int height, int offset) { - skyboxCtx->textures[segmentIndex][imageIndex] = tex; + if (GameInteractor_Should(VB_LOAD_SKYBOX, true)) { + skyboxCtx->textures[segmentIndex][imageIndex] = tex; + } } void LoadSkyboxPalette(SkyboxContext* skyboxCtx, int paletteIndex, char* palTex, int width, int height) { - skyboxCtx->palettes[paletteIndex] = palTex; - skyboxCtx->palette_size = width * height; + if (GameInteractor_Should(VB_LOAD_SKYBOX, true)) { + skyboxCtx->palettes[paletteIndex] = palTex; + skyboxCtx->palette_size = width * height; + } } static const char* sSBVRFine0Tex[] = { gSunriseSkybox1Tex, gSunriseSkybox2Tex, gSunriseSkybox3Tex, gSunriseSkybox4Tex,