[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 <Archez@users.noreply.github.com>
Co-authored-by: Caladius <clatini88@gmail.com>
This commit is contained in:
PurpleHato
2025-10-01 04:03:55 +02:00
committed by GitHub
parent 5ed3db0a7e
commit e10b882c08
11 changed files with 212 additions and 5 deletions

View File

@@ -0,0 +1,161 @@
#include <libultraship/bridge.h>
#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<SceneID> 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<SceneID> 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<SkyboxId> 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 });

View File

@@ -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));

View File

@@ -186,6 +186,10 @@ void GameInteractor_ExecuteOnPlayDestroy() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayDestroy>();
}
void GameInteractor_ExecuteOnPlayDrawBegin() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayDrawBegin>();
}
void GameInteractor_ExecuteOnPlayDrawEnd() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayDrawEnd>();
}

View File

@@ -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, ...);

View File

@@ -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

View File

@@ -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"))

View File

@@ -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<LUS::Vertex*>(Ship::Context::GetInstance()

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 <string.h>
#include <assert.h>
@@ -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;

View File

@@ -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,