diff --git a/include/d/d_pane_class.h b/include/d/d_pane_class.h index ffc067e411..29cee0c2ea 100644 --- a/include/d/d_pane_class.h +++ b/include/d/d_pane_class.h @@ -4,6 +4,10 @@ #include "JSystem/JUtility/TColor.h" #include "d/d_pane_class_alpha.h" +#if TARGET_PC +#include +#endif + class JKRHeap; void dPaneClass_showNullPane(J2DScreen*); @@ -26,6 +30,9 @@ public: void setBlackWhite(JUtility::TColor, JUtility::TColor); void paneTrans(f32, f32); void paneScale(f32, f32); +#if TARGET_PC + void presentAnime(); +#endif bool scaleAnime(s16, f32, f32, u8); bool colorAnime(s16, JUtility::TColor, JUtility::TColor, JUtility::TColor, JUtility::TColor, u8); @@ -54,8 +61,14 @@ public: void resize(f32 x, f32 y) { getPanePtr()->resize(x, y); } void move(f32 x, f32 y) { getPanePtr()->move(x, y); } - void scaleAnimeStart(s16 v) { mScaleAnime = v; } - void colorAnimeStart(s16 start) { mColorAnime = start; } + void scaleAnimeStart(DUSK_IF_ELSE(f32, s16) v) { + mScaleAnime = v; + IF_DUSK(mScaleAnimation.active = false;) + } + void colorAnimeStart(DUSK_IF_ELSE(f32, s16) start) { + mColorAnime = start; + IF_DUSK(mColorAnimation.active = false;) + } f32 getPosX() { return getPanePtr()->getBounds().i.x; } f32 getPosY() { return getPanePtr()->getBounds().i.y; } @@ -106,10 +119,14 @@ public: /* 0x5C */ JUtility::TColor mInitBlack; /* 0x60 */ s16 field_0x60; /* 0x62 */ s16 field_0x62; - /* 0x64 */ s16 mScaleAnime; + /* 0x64 */ DUSK_IF_ELSE(f32, s16) mScaleAnime; /* 0x66 */ s16 field_0x66; /* 0x68 */ s16 field_0x68; - /* 0x6A */ s16 mColorAnime; + /* 0x6A */ DUSK_IF_ELSE(f32, s16) mColorAnime; +#if TARGET_PC + Animation mScaleAnimation; + Animation> mColorAnimation; +#endif }; #endif /* D_PANE_D_PANE_CLASS_H */ diff --git a/include/d/d_pane_class_alpha.h b/include/d/d_pane_class_alpha.h index cf38229b97..f55227a3ee 100644 --- a/include/d/d_pane_class_alpha.h +++ b/include/d/d_pane_class_alpha.h @@ -9,6 +9,16 @@ class JKRExpHeap; class CPaneMgrAlpha { public: +#if TARGET_PC + template + struct Animation { + bool active = false; + s16 duration; + u8 curve; + T start, end; + }; +#endif + virtual ~CPaneMgrAlpha(); virtual void setAlpha(u8); @@ -18,9 +28,12 @@ public: void show(); void hide(); bool isVisible(); - f32 rateCalc(s16, s16, u8); + f32 rateCalc(DUSK_IF_ELSE(f32, s16), DUSK_IF_ELSE(f32, s16), u8); void setAlphaRate(f32); f32 getAlphaRate(); +#if TARGET_PC + void presentAlphaAnime(); +#endif bool alphaAnime(s16 timer, u8 startAlpha, u8 endAlpha, u8 calcType); bool alphaAnimeLoop(s16, u8, u8, u8); void childPaneCount(J2DPane*); @@ -29,8 +42,11 @@ public: J2DPane* getPanePtr() { return mPane; } u8 getAlpha() { return getPanePtr()->getAlpha(); } - s16 getAlphaTimer() { return mAlphaTimer; } - void alphaAnimeStart(s16 start) { mAlphaTimer = start; } + DUSK_IF_ELSE(f32, s16) getAlphaTimer() { return mAlphaTimer; } + void alphaAnimeStart(DUSK_IF_ELSE(f32, s16) start) { + mAlphaTimer = start; + IF_DUSK(mAlphaAnimation.active = false;) + } u8 getInitAlpha() { return mInitAlpha; } /* 0x04 */ J2DPane* mPane; @@ -38,9 +54,12 @@ public: /* 0x0C */ void* mpFirstStackAlpha; /* 0x10 */ u8* field_0x10; /* 0x14 */ s16 mChildPaneCount; - /* 0x16 */ s16 mAlphaTimer; + /* 0x16 */ DUSK_IF_ELSE(f32, s16) mAlphaTimer; /* 0x18 */ u8 mInitAlpha; /* 0x19 */ u8 mFlags; +#if TARGET_PC + Animation mAlphaAnimation; +#endif }; class CPaneMgrAlphaMorf : public CPaneMgrAlpha { diff --git a/src/d/actor/d_a_title.cpp b/src/d/actor/d_a_title.cpp index 0d011f9213..ccf03f4a5d 100644 --- a/src/d/actor/d_a_title.cpp +++ b/src/d/actor/d_a_title.cpp @@ -19,6 +19,7 @@ #include "m_Do/m_Do_graphic.h" #if TARGET_PC +#include "dusk/game_clock.h" #include "dusk/interp/frame_interpolation.h" #include "dusk/version.hpp" #endif @@ -380,6 +381,12 @@ int daTitle_c::getDemoPrm() { } int daTitle_c::Draw() { +#if TARGET_PC + if (field_0x600 != NULL && dusk::game_clock::is_presentation_frame()) { + field_0x600->presentAlphaAnime(); + } +#endif + J3DModelData* modelData = mpModel->getModelData(); cMtx_trans(mpModel->getBaseTRMtx(), IREG_F(7), IREG_F(8), IREG_F(9) + -430.0f); mpModel->getBaseScale()->x = -1.0f; diff --git a/src/d/d_pane_class.cpp b/src/d/d_pane_class.cpp index 4d5104ffcf..f833857873 100644 --- a/src/d/d_pane_class.cpp +++ b/src/d/d_pane_class.cpp @@ -13,7 +13,16 @@ #include "m_Do/m_Do_ext.h" #include "m_Do/m_Do_graphic.h" +#if TARGET_PC +#include "dusk/interp/lerp.h" +#include "dusk/interp/user_interface.h" +#endif + CPaneMgr::CPaneMgr() { +#if TARGET_PC + mScaleAnimation.active = false; + mColorAnimation.active = false; +#endif mpFirstStackAlpha = NULL; mpFirstStackSize = NULL; } @@ -45,6 +54,12 @@ void CPaneMgr::setAlpha(u8 alpha) { } void CPaneMgr::reinit() { +#if TARGET_PC + mAlphaAnimation.active = false; + mScaleAnimation.active = false; + mColorAnimation.active = false; +#endif + mInitPos.x = getPanePtr()->getBounds().i.x; mInitPos.y = getPanePtr()->getBounds().i.y; @@ -66,6 +81,12 @@ void CPaneMgr::reinit() { } void CPaneMgr::initiate(J2DPane* p_pane, JKRExpHeap* p_heap) { +#if TARGET_PC + mAlphaAnimation.active = false; + mScaleAnimation.active = false; + mColorAnimation.active = false; +#endif + mPane = p_pane; if (p_heap) { @@ -269,7 +290,29 @@ void CPaneMgr::paneScale(f32 x, f32 y) { } } +#if TARGET_PC +void CPaneMgr::presentAnime() { + presentAlphaAnime(); + dusk::vdt::present_animation(mScaleAnimation, mScaleAnime, *this, [&](f32 rate) { + const f32 scale = dusk::interp::lerp(mScaleAnimation.start, mScaleAnimation.end, rate); + getPanePtr()->scale(getInitScaleX() * scale, getInitScaleY() * scale); + }); + dusk::vdt::present_animation(mColorAnimation, mColorAnime, *this, [&](f32 rate) { + setBlackWhite( + dusk::interp::lerp(mColorAnimation.start.first, mColorAnimation.end.first, rate), + dusk::interp::lerp(mColorAnimation.start.second, mColorAnimation.end.second, rate)); + }); +} +#endif + bool CPaneMgr::scaleAnime(s16 param_0, f32 param_1, f32 param_2, u8 param_3) { +#if TARGET_PC + if (!dusk::vdt::request_animation(mScaleAnimation, mScaleAnime, param_0, param_1, param_2, param_3)) { + return false; + } + getPanePtr()->scale(mInitScale.x * param_2, mInitScale.y * param_2); + return true; +#else if (mScaleAnime < param_0 - 1) { mScaleAnime++; f32 rate = rateCalc(param_0, mScaleAnime, param_3); @@ -281,10 +324,20 @@ bool CPaneMgr::scaleAnime(s16 param_0, f32 param_1, f32 param_2, u8 param_3) { return true; } return false; +#endif } bool CPaneMgr::colorAnime(s16 anmTimer, JUtility::TColor startBlack, JUtility::TColor endBlack, - JUtility::TColor startWhite, JUtility::TColor endWhite, u8 calcType) { + JUtility::TColor startWhite, JUtility::TColor endWhite, u8 calcType) { +#if TARGET_PC + if (!dusk::vdt::request_animation(mColorAnimation, mColorAnime, anmTimer, + {startBlack, startWhite}, {endBlack, endWhite}, calcType)) + { + return false; + } + setBlackWhite(endBlack, endWhite); + return true; +#else if (mColorAnime < anmTimer - 1) { mColorAnime++; f32 rate = rateCalc(anmTimer, mColorAnime, calcType); @@ -349,6 +402,7 @@ bool CPaneMgr::colorAnime(s16 anmTimer, JUtility::TColor startBlack, JUtility::T } return false; +#endif } Vec CPaneMgr::getGlobalVtx(J2DPane* p_pane, Mtx* param_1, u8 param_2, bool param_3, diff --git a/src/d/d_pane_class_alpha.cpp b/src/d/d_pane_class_alpha.cpp index 31bc8bc9b4..d82b77d220 100644 --- a/src/d/d_pane_class_alpha.cpp +++ b/src/d/d_pane_class_alpha.cpp @@ -5,7 +5,13 @@ #include "JSystem/JKernel/JKRExpHeap.h" #include "m_Do/m_Do_ext.h" -CPaneMgrAlpha::CPaneMgrAlpha() {} +#if TARGET_PC +#include "dusk/interp/user_interface.h" +#endif + +CPaneMgrAlpha::CPaneMgrAlpha() { + IF_DUSK(mAlphaAnimation.active = false); +} CPaneMgrAlpha::CPaneMgrAlpha(J2DScreen* p_screen, u64 tag, u8 flags, JKRExpHeap* p_heap) { J2DPane* pane = p_screen->search(tag); @@ -21,6 +27,8 @@ CPaneMgrAlpha::~CPaneMgrAlpha() { } void CPaneMgrAlpha::initiateAlpha(J2DPane* p_pane, JKRExpHeap* p_heap) { + IF_DUSK(mAlphaAnimation.active = false); + mPane = p_pane; if (p_heap != NULL) { @@ -68,7 +76,7 @@ bool CPaneMgrAlpha::isVisible() { return mPane->isVisible(); } -f32 CPaneMgrAlpha::rateCalc(s16 maxTimer, s16 curTimer, u8 calcType) { +f32 CPaneMgrAlpha::rateCalc(DUSK_IF_ELSE(f32, s16) maxTimer, DUSK_IF_ELSE(f32, s16) curTimer, u8 calcType) { if (maxTimer <= curTimer) { return 1.0f; } @@ -105,7 +113,22 @@ f32 CPaneMgrAlpha::getAlphaRate() { return rate; } +#if TARGET_PC +void CPaneMgrAlpha::presentAlphaAnime() { + dusk::vdt::present_animation(mAlphaAnimation, mAlphaTimer, *this, [&](f32 rate) { + setAlpha(mAlphaAnimation.start + rate * (f32)(mAlphaAnimation.end - mAlphaAnimation.start)); + }); +} +#endif + bool CPaneMgrAlpha::alphaAnime(s16 timer, u8 startAlpha, u8 endAlpha, u8 calcType) { +#if TARGET_PC + if (!dusk::vdt::request_animation(mAlphaAnimation, mAlphaTimer, timer, startAlpha, endAlpha, calcType)) { + return false; + } + setAlpha(endAlpha); + return true; +#else if (mAlphaTimer < timer - 1) { mAlphaTimer++; f32 rate = rateCalc(timer, mAlphaTimer, calcType); @@ -117,20 +140,41 @@ bool CPaneMgrAlpha::alphaAnime(s16 timer, u8 startAlpha, u8 endAlpha, u8 calcTyp } return false; +#endif } bool CPaneMgrAlpha::alphaAnimeLoop(s16 param_0, u8 param_1, u8 param_2, u8 param_3) { bool ret = false; s16 temp_r4 = param_0 / 2; +#if TARGET_PC + mAlphaAnimation.active = false; + if (param_0 <= 0) { + mAlphaTimer = 0.0f; + setAlpha(param_1); + return true; + } + const f32 frames = dusk::game_clock::original_frames(); + if (frames <= 0.0f) { + return false; + } + if (mAlphaTimer >= param_0) { + mAlphaTimer = 0.0f; + ret = true; + } else { + ret = mAlphaTimer + frames >= param_0; + dusk::vdt::advance_looping_frame(mAlphaTimer, 1.0f, param_0); + } +#else mAlphaTimer++; if (mAlphaTimer >= param_0) { mAlphaTimer = 0; ret = true; } +#endif - s16 tmp; + DUSK_IF_ELSE(f32, s16) tmp; if (mAlphaTimer < temp_r4) { tmp = mAlphaTimer; } else { diff --git a/src/dusk/interp/lerp.h b/src/dusk/interp/lerp.h index 1f05f070fa..c0820f2439 100644 --- a/src/dusk/interp/lerp.h +++ b/src/dusk/interp/lerp.h @@ -1,5 +1,6 @@ #pragma once +#include "JSystem/JUtility/TColor.h" #include "SSystem/SComponent/c_angle.h" #include "SSystem/SComponent/c_sxyz.h" #include "SSystem/SComponent/c_xyz.h" @@ -49,5 +50,13 @@ inline void lerp(Mtx& out, const Mtx& lhs, const Mtx& rhs, float step) { } } +inline JUtility::TColor lerp(const JUtility::TColor& lhs, const JUtility::TColor& rhs, float step) { + return JUtility::TColor( + lerp(lhs.r, rhs.r, step), + lerp(lhs.g, rhs.g, step), + lerp(lhs.b, rhs.b, step), + lerp(lhs.a, rhs.a, step)); +} + } // namespace dusk::interp #endif diff --git a/src/dusk/interp/user_interface.h b/src/dusk/interp/user_interface.h new file mode 100644 index 0000000000..4d86c2c3f4 --- /dev/null +++ b/src/dusk/interp/user_interface.h @@ -0,0 +1,54 @@ +#pragma once + +#include "dusk/game_clock.h" + +#include + +#include +#include + +namespace dusk::vdt { + +inline void advance_looping_frame(f32& frame, f32 speed, f32 max) { + if (max <= 0.0f) { + return; + } + frame += speed * game_clock::original_frames(); + if (frame >= max) { + frame = fmodf(frame, max); + } +} + +inline void advance_toward_frame(f32& frame, f32 target, f32 speed) { + if (frame == target) { + return; + } + const f32 step = speed * game_clock::original_frames(); + frame = frame < target ? std::min(frame + step, target) : std::max(frame - step, target); +} + +template +bool request_animation(Animation& animation, f32& frame, s16 time, + decltype(animation.start) from, decltype(animation.end) to, u8 curve) { + if (frame < time) { + animation = {true, time, curve, from, to}; + return false; + } + animation.active = false; + frame = time; + return true; +} + +template +void present_animation(Animation& animation, f32& frame, Pane& pane, Apply apply) { + if (!animation.active) { + return; + } + advance_toward_frame(frame, animation.duration, 1.0f); + apply(pane.rateCalc(animation.duration, frame, animation.curve)); + if (frame >= animation.duration) { + animation.active = false; + } +} + +} // namespace dusk::vdt