diff --git a/asm/MetroidPrime/CInGameGuiManager.s b/asm/MetroidPrime/CInGameGuiManager.s index c863f17d..2e86ea0e 100644 --- a/asm/MetroidPrime/CInGameGuiManager.s +++ b/asm/MetroidPrime/CInGameGuiManager.s @@ -1957,7 +1957,7 @@ lbl_80108464: /* 80108470 001053D0 7F A4 EB 78 */ mr r4, r29 /* 80108474 001053D4 7C 00 00 34 */ cntlzw r0, r0 /* 80108478 001053D8 54 05 D9 7E */ srwi r5, r0, 5 -/* 8010847C 001053DC 48 15 53 F9 */ bl Update__16CPauseScreenBlurfRC13CStateManagerb +/* 8010847C 001053DC 48 15 53 F9 */ bl Update__16CPauseScreenBlurFfRC13CStateManagerb /* 80108480 001053E0 80 7C 00 4C */ lwz r3, 0x4c(r28) /* 80108484 001053E4 28 03 00 00 */ cmplwi r3, 0 /* 80108488 001053E8 41 82 00 3C */ beq lbl_801084C4 diff --git a/asm/MetroidPrime/CPauseScreenBlur.s b/asm/MetroidPrime/CPauseScreenBlur.s index a45bb088..2e0fcf1d 100644 --- a/asm/MetroidPrime/CPauseScreenBlur.s +++ b/asm/MetroidPrime/CPauseScreenBlur.s @@ -3,8 +3,8 @@ .section .data .balign 8 -.global lbl_803E9A00 -lbl_803E9A00: +.global __vt__16CPauseScreenBlur +__vt__16CPauseScreenBlur: # ROM: 0x3E6A00 .4byte 0 .4byte 0 @@ -135,8 +135,8 @@ lbl_8025D710: /* 8025D724 0025A684 38 21 00 20 */ addi r1, r1, 0x20 /* 8025D728 0025A688 4E 80 00 20 */ blr -.global sub_8025d72c -sub_8025d72c: +.global OnBlurComplete__16CPauseScreenBlurFb +OnBlurComplete__16CPauseScreenBlurFb: /* 8025D72C 0025A68C 80 03 00 14 */ lwz r0, 0x14(r3) /* 8025D730 0025A690 2C 00 00 00 */ cmpwi r0, 0 /* 8025D734 0025A694 40 82 00 0C */ bne lbl_8025D740 @@ -226,8 +226,8 @@ lbl_8025D858: /* 8025D86C 0025A7CC 38 21 00 30 */ addi r1, r1, 0x30 /* 8025D870 0025A7D0 4E 80 00 20 */ blr -.global Update__16CPauseScreenBlurfRC13CStateManagerb -Update__16CPauseScreenBlurfRC13CStateManagerb: +.global Update__16CPauseScreenBlurFfRC13CStateManagerb +Update__16CPauseScreenBlurFfRC13CStateManagerb: /* 8025D874 0025A7D4 94 21 FF E0 */ stwu r1, -0x20(r1) /* 8025D878 0025A7D8 7C 08 02 A6 */ mflr r0 /* 8025D87C 0025A7DC 90 01 00 24 */ stw r0, 0x24(r1) @@ -283,7 +283,7 @@ lbl_8025D910: lbl_8025D92C: /* 8025D92C 0025A88C 7F C3 F3 78 */ mr r3, r30 /* 8025D930 0025A890 7F E4 FB 78 */ mr r4, r31 -/* 8025D934 0025A894 4B FF FD F9 */ bl sub_8025d72c +/* 8025D934 0025A894 4B FF FD F9 */ bl OnBlurComplete__16CPauseScreenBlurFb lbl_8025D938: /* 8025D938 0025A898 C0 22 BA 18 */ lfs f1, lbl_805AD738@sda21(r2) /* 8025D93C 0025A89C C0 1E 00 18 */ lfs f0, 0x18(r30) @@ -377,9 +377,9 @@ __dt__16CPauseScreenBlurFv: /* 8025DA5C 0025A9BC 93 C1 00 08 */ stw r30, 8(r1) /* 8025DA60 0025A9C0 7C 7E 1B 79 */ or. r30, r3, r3 /* 8025DA64 0025A9C4 41 82 00 78 */ beq lbl_8025DADC -/* 8025DA68 0025A9C8 3C 60 80 3F */ lis r3, lbl_803E9A00@ha +/* 8025DA68 0025A9C8 3C 60 80 3F */ lis r3, __vt__16CPauseScreenBlur@ha /* 8025DA6C 0025A9CC 34 1E 00 1C */ addic. r0, r30, 0x1c -/* 8025DA70 0025A9D0 38 03 9A 00 */ addi r0, r3, lbl_803E9A00@l +/* 8025DA70 0025A9D0 38 03 9A 00 */ addi r0, r3, __vt__16CPauseScreenBlur@l /* 8025DA74 0025A9D4 90 1E 00 00 */ stw r0, 0(r30) /* 8025DA78 0025A9D8 41 82 00 38 */ beq lbl_8025DAB0 /* 8025DA7C 0025A9DC 34 1E 00 1C */ addic. r0, r30, 0x1c @@ -422,9 +422,9 @@ lbl_8025DADC: __ct__16CPauseScreenBlurFv: /* 8025DAF8 0025AA58 94 21 FF E0 */ stwu r1, -0x20(r1) /* 8025DAFC 0025AA5C 7C 08 02 A6 */ mflr r0 -/* 8025DB00 0025AA60 3C 80 80 3F */ lis r4, lbl_803E9A00@ha +/* 8025DB00 0025AA60 3C 80 80 3F */ lis r4, __vt__16CPauseScreenBlur@ha /* 8025DB04 0025AA64 90 01 00 24 */ stw r0, 0x24(r1) -/* 8025DB08 0025AA68 38 04 9A 00 */ addi r0, r4, lbl_803E9A00@l +/* 8025DB08 0025AA68 38 04 9A 00 */ addi r0, r4, __vt__16CPauseScreenBlur@l /* 8025DB0C 0025AA6C 93 E1 00 1C */ stw r31, 0x1c(r1) /* 8025DB10 0025AA70 93 C1 00 18 */ stw r30, 0x18(r1) /* 8025DB14 0025AA74 7C 7E 1B 78 */ mr r30, r3 diff --git a/configure.py b/configure.py index ccb022a8..6c8f43f0 100755 --- a/configure.py +++ b/configure.py @@ -356,7 +356,7 @@ LIBS = [ "MetroidPrime/Enemies/CRidley", ["MetroidPrime/Enemies/CPuffer", False], ["MetroidPrime/ScriptObjects/CFire", False], - "MetroidPrime/CPauseScreenBlur", + ["MetroidPrime/CPauseScreenBlur", True], "MetroidPrime/Enemies/CTryclops", "MetroidPrime/Weapons/CNewFlameThrower", "MetroidPrime/Cameras/CInterpolationCamera", diff --git a/include/Kyoto/Graphics/CGraphics.hpp b/include/Kyoto/Graphics/CGraphics.hpp index 48606bf5..e8c6c34c 100644 --- a/include/Kyoto/Graphics/CGraphics.hpp +++ b/include/Kyoto/Graphics/CGraphics.hpp @@ -151,6 +151,8 @@ public: static void SetExternalTimeProvider(CTimeProvider* provider); static void DisableAllLights(); + static void SetAmbientColor(const CColor&); + static void SetModelMatrix(const CTransform4f& xf); static void SetAlphaCompare(ERglAlphaFunc comp0, u8 ref0, ERglAlphaOp op, ERglAlphaFunc comp1, u8 ref1); diff --git a/include/MetroidPrime/CPauseScreenBlur.hpp b/include/MetroidPrime/CPauseScreenBlur.hpp new file mode 100644 index 00000000..b5c40184 --- /dev/null +++ b/include/MetroidPrime/CPauseScreenBlur.hpp @@ -0,0 +1,51 @@ +#ifndef _CPAUSESCREENBLUR +#define _CPAUSESCREENBLUR + +#include "MetroidPrime/Cameras/CCameraBlurPass.hpp" + +#include "Kyoto/TToken.hpp" + +class CTexture; +class CStateManager; + +// TODO: move elsewhere? +enum EInGameGuiState { + kIGS_Zero, + kIGS_InGame, + kIGS_MapScreen, + kIGS_PauseGame, + kIGS_PauseLogBook, + kIGS_PauseSaveGame, + kIGS_PauseHUDMessage +}; + +class CPauseScreenBlur { +public: + enum EState { kS_InGame, kS_MapScreen, kS_SaveGame, kS_HUDMessage, kS_Pause }; + + CPauseScreenBlur(); + virtual ~CPauseScreenBlur(); + + void OnNewInGameGuiState(EInGameGuiState state, CStateManager& stateMgr); + bool IsGameDraw() const { return x50_25_gameDraw; } + void Update(float dt, const CStateManager& stateMgr, bool); + void Draw(const CStateManager& stateMgr); + float GetBlurAmt() const; + bool IsNotTransitioning() const { return x10_prevState == x14_nextState; } + +private: + TLockedToken< CTexture > x4_mapLightQuarter; + EState x10_prevState; + EState x14_nextState; + float x18_blurAmt; + CCameraBlurPass x1c_camBlur; + bool x50_24_blurring : 1; + bool x50_25_gameDraw : 1; + + void OnBlurComplete(bool); + void SetState(EState state); + float GetBlurAmtInline() const { return fabs(x18_blurAmt); } +}; + + +#endif // _CPAUSESCREENBLUR diff --git a/include/MetroidPrime/Cameras/CCameraBlurPass.hpp b/include/MetroidPrime/Cameras/CCameraBlurPass.hpp index b79324eb..5d734b06 100644 --- a/include/MetroidPrime/Cameras/CCameraBlurPass.hpp +++ b/include/MetroidPrime/Cameras/CCameraBlurPass.hpp @@ -21,10 +21,16 @@ public: kBT_HiBlur, kBT_XRay, }; + CCameraBlurPass(); void SetBlur(EBlurType type, float amount, float duration, bool usePersistentFb); void DisableBlur(float duration); + void Draw() const; + static void DrawWideScreen(const CColor& color, const CTexture* tex, float v); + + EBlurType GetCurrType() const { return x10_curType; } + bool GetNoPersistentCopy() const { return x2d_noPersistentCopy; } private: rstl::optional_object< TLockedToken< CTexture > > x0_paletteTex; diff --git a/src/MetroidPrime/CPauseScreenBlur.cpp b/src/MetroidPrime/CPauseScreenBlur.cpp new file mode 100644 index 00000000..2040ae9a --- /dev/null +++ b/src/MetroidPrime/CPauseScreenBlur.cpp @@ -0,0 +1,148 @@ +#include "MetroidPrime/CPauseScreenBlur.hpp" + +#include "MetroidPrime/Cameras/CCameraFilterPass.hpp" +#include "MetroidPrime/SFX/UI.h" +#include "MetroidPrime/Tweaks/CTweakGui.hpp" +#include "MetroidPrime/Tweaks/CTweakGuiColors.hpp" + +#include "Kyoto/Audio/CSfxManager.hpp" +#include "Kyoto/Graphics/CGraphics.hpp" + +#include "float.h" +#include "rstl/math.hpp" + +CPauseScreenBlur::CPauseScreenBlur() +: x4_mapLightQuarter(gpSimplePool->GetObj("TXTR_MapLightQuarter")) +, x10_prevState(kS_InGame) +, x14_nextState(kS_InGame) +, x18_blurAmt(0.f) +, x50_24_blurring(false) +, x50_25_gameDraw(true) {} + +CPauseScreenBlur::~CPauseScreenBlur() {} + +void CPauseScreenBlur::OnNewInGameGuiState(EInGameGuiState state, CStateManager& stateMgr) { + bool valid = false; + if (state >= 0 && state <= 1) { + valid = true; + } + if (valid) { + SetState(kS_InGame); + return; + } + + switch (state) { + case kIGS_MapScreen: + SetState(kS_MapScreen); + break; + case kIGS_PauseSaveGame: + SetState(kS_SaveGame); + break; + case kIGS_PauseHUDMessage: + SetState(kS_HUDMessage); + break; + case kIGS_PauseGame: + case kIGS_PauseLogBook: + SetState(kS_Pause); + break; + } +} + +void CPauseScreenBlur::Update(float dt, const CStateManager& stateMgr, bool b) { + if (x10_prevState == x14_nextState) + return; + + if (x18_blurAmt < 0.f) + x18_blurAmt = rstl::min_val(0.f, 2.f * dt + x18_blurAmt); + else if (x18_blurAmt > 0.f) + x18_blurAmt = rstl::min_val(1.f, 2.f * dt + x18_blurAmt); + + if (x18_blurAmt == 0.f || x18_blurAmt == 1.f) + OnBlurComplete(b); + + if (x18_blurAmt == 0.f && b) { + x1c_camBlur.DisableBlur(0.f); + } else { + x1c_camBlur.SetBlur(CCameraBlurPass::kBT_HiBlur, gpTweakGui->GetPauseBlurFactor() * GetBlurAmtInline(), 0.f, true); + x50_24_blurring = true; + } +} + +void CPauseScreenBlur::Draw(const CStateManager&) { + x1c_camBlur.Draw(); + CGraphics::DisableAllLights(); + CGraphics::SetAmbientColor(CColor(0xffffffff)); + const float t = fabs(x18_blurAmt); + if (x1c_camBlur.GetCurrType() != CCameraBlurPass::kBT_NoBlur) { + const CColor& white = CColor::White(); + CCameraFilterPass::DrawFilter( + CCameraFilterPass::kFT_Multiply, CCameraFilterPass::kFS_FullscreenQuarters, + CColor::Lerp(white, gpTweakGuiColors->GetPauseBlurFilterColor(), t), + *x4_mapLightQuarter, t); + CCameraFilterPass::DrawFilter(CCameraFilterPass::kFT_Multiply, + CCameraFilterPass::kFS_ScanLinesEven, + CColor::Lerp(CColor::White(), CColor(0xc0c0c0ff), t), nullptr, t); + } + + if (x50_24_blurring && x1c_camBlur.GetNoPersistentCopy()) { + x50_24_blurring = false; + x50_25_gameDraw = false; + } +} + +void CPauseScreenBlur::OnBlurComplete(bool b) { + if (x14_nextState == kS_InGame && !b) + return; + x10_prevState = x14_nextState; + if (x10_prevState == kS_InGame) + x50_25_gameDraw = true; +} + +void CPauseScreenBlur::SetState(EState state) { + switch (x10_prevState) { + case kS_InGame: + if (state != kS_InGame) { + CSfxManager::SetChannel(CSfxManager::kSC_PauseScreen); + if (state == kS_HUDMessage) + CSfxManager::SfxStart(SFXui_into_hud_message, 0x7f, 0x40, false, CSfxManager::kMedPriority); + else if (state == kS_MapScreen) + CSfxManager::SfxStart(SFXui_into_map_screen, 0x7f, 0x40, false, CSfxManager::kMedPriority); + x18_blurAmt = FLT_EPSILON; + } + break; + case kS_MapScreen: + case kS_SaveGame: + case kS_HUDMessage: + case kS_Pause: + break; + } + + switch (state) { + case kS_InGame: + bool valid = true; + if (x10_prevState == kS_InGame && x14_nextState == kS_InGame) { + valid = false; + } + if (valid) { + CSfxManager::SetChannel(CSfxManager::kSC_Game); + + if (x10_prevState == kS_HUDMessage) + CSfxManager::SfxStart(SFXui_outof_hud_message, 0x7f, 0x40, false, + CSfxManager::kMedPriority); + else if (x10_prevState == kS_MapScreen) + CSfxManager::SfxStart(SFXui_outof_map_screen, 0x7f, 0x40, false, CSfxManager::kMedPriority); + + x18_blurAmt = -1.f; + } + break; + case kS_MapScreen: + case kS_SaveGame: + case kS_HUDMessage: + case kS_Pause: + break; + } + + x14_nextState = state; +} + +float CPauseScreenBlur::GetBlurAmt() const { return GetBlurAmtInline(); }