diff --git a/files.cmake b/files.cmake index 2f14dd49f0..d810568896 100644 --- a/files.cmake +++ b/files.cmake @@ -1340,6 +1340,7 @@ set(DUSK_FILES src/dusk/config.cpp src/dusk/settings.cpp src/dusk/logging.cpp + src/dusk/frame_interpolation.cpp src/dusk/layout.cpp src/dusk/stubs.cpp src/dusk/endian.cpp @@ -1362,6 +1363,8 @@ set(DUSK_FILES src/dusk/imgui/ImGuiMenuEnhancements.hpp src/dusk/imgui/ImGuiPreLaunchWindow.cpp src/dusk/imgui/ImGuiPreLaunchWindow.hpp + src/dusk/imgui/ImGuiFirstRunPreset.hpp + src/dusk/imgui/ImGuiFirstRunPreset.cpp src/dusk/imgui/ImGuiProcessOverlay.cpp src/dusk/imgui/ImGuiCameraOverlay.cpp src/dusk/imgui/ImGuiHeapOverlay.cpp diff --git a/include/d/actor/d_a_e_fb.h b/include/d/actor/d_a_e_fb.h index 3e8734b8b6..a4494c5e7a 100644 --- a/include/d/actor/d_a_e_fb.h +++ b/include/d/actor/d_a_e_fb.h @@ -71,10 +71,16 @@ private: /* 0xA30 */ dCcD_Sph mSphere2; /* 0xB68 */ dCcD_Sph mAtSph; /* 0xCA0 */ dCcU_AtInfo mAtInfo; +#if AVOID_UB + /* 0xCC4 */ u32 mKeys[8]; + /* 0xCE4 */ u8 mHIOInit; + /* 0xCE5 */ u8 field_0xce5[0xCE8 - 0xCE5]; +#else // !@bug Seems that the orig developers didn't size mKeys correctly, as ideally it should be len=8, not len=7: /* 0xCC4 */ u32 mKeys[7]; /* 0xCE0 */ u8 mHIOInit; /* 0xCE1 */ u8 field_0xce1[0xCE8 - 0xCE1]; +#endif }; STATIC_ASSERT(sizeof(daE_FB_c) == 0xce8); diff --git a/include/d/actor/d_a_e_tk2.h b/include/d/actor/d_a_e_tk2.h index e4d29cf674..12b0fef3a3 100644 --- a/include/d/actor/d_a_e_tk2.h +++ b/include/d/actor/d_a_e_tk2.h @@ -39,8 +39,7 @@ public: /* 0x680 */ s16 mPlayerAngleY; /* 0x684 */ f32 mPlayerDistanceLimit; /* 0x688 */ u8 field24_0x688[2]; - /* 0x68A */ s16 mActionTimer[3]; - /* 0x690 */ s16 mExecuteState; + /* 0x68A */ s16 mActionTimer[4]; /* 0x692 */ s16 mInvincibilityTimer; /* 0x694 */ s8 mAttentionOFF; /* 0x695 */ s8 mTKBallSpawned; diff --git a/include/d/actor/d_a_e_vt.h b/include/d/actor/d_a_e_vt.h index 5d6bf3dfd2..4b6082adae 100644 --- a/include/d/actor/d_a_e_vt.h +++ b/include/d/actor/d_a_e_vt.h @@ -159,8 +159,7 @@ private: /* 0x00958 */ mDoExt_3DlineMat1_c mRope; /* 0x00994 */ cXyz field_0x994[100]; /* 0x00E44 */ csXyz field_0xe44[100]; - /* 0x0109C */ u8 mRopeCutStatus[10]; - /* 0x010A6 */ u8 field_0x10A6[0x01100 - 0x010A6]; + /* 0x0109C */ u8 mRopeCutStatus[100]; /* 0x01100 */ u8 mDrawRopes; /* 0x01101 */ u8 field_0x1101[0x01104 - 0x01101]; /* 0x01104 */ f32 field_0x1104[10]; diff --git a/include/d/actor/d_a_horse.h b/include/d/actor/d_a_horse.h index 37e4bac70f..7e8ef8c343 100644 --- a/include/d/actor/d_a_horse.h +++ b/include/d/actor/d_a_horse.h @@ -196,6 +196,9 @@ public: void copyReinPos(); void setReinPosHandSubstance(int); void setReinPosNormalSubstance(); +#if TARGET_PC + void lerpControlPoints(f32 alpha); +#endif void bgCheck(); bool checkSpecialWallHitSubstance(cXyz const&) const; void setServiceWaitTimer(); diff --git a/include/d/actor/d_a_mg_rod.h b/include/d/actor/d_a_mg_rod.h index 3786fbafcb..e8ef9a6128 100644 --- a/include/d/actor/d_a_mg_rod.h +++ b/include/d/actor/d_a_mg_rod.h @@ -128,10 +128,10 @@ public: /* 0x0760 */ f32 field_0x760; /* 0x0764 */ cXyz field_0x764; /* 0x0770 */ mg_line_s mg_line; // below to mg_hook_s part of mg_line_s? - /* 0x0C20 */ f32 field_0xc20[98]; - /* 0x0DA8 */ u8 field_0xDA8[0x0DB0 - 0x0DA8]; - /* 0x0DB0 */ f32 field_0xdb0[98]; - /* 0x0F38 */ u8 field_0xF38[0x0F40 - 0x0F38]; + /* 0x0C20 */ f32 field_0xc20[99]; + /* 0x0DA8 */ u8 field_0xDAC[0x0DB0 - 0x0DAC]; + /* 0x0DB0 */ f32 field_0xdb0[99]; + /* 0x0F3C */ u8 field_0xF38[0x0F40 - 0x0F3C]; /* 0x0F40 */ mDoExt_3DlineMat0_c linemat; /* 0x0F5C */ f32 field_0xf5c; /* 0x0F60 */ f32 field_0xf60; @@ -183,9 +183,8 @@ public: /* 0x102F */ u8 field_0x102f; /* 0x1030 */ J3DModel* esa_model[2]; /* 0x1038 */ u8 field_0x1038[0x103C - 0x1038]; - /* 0x103C */ s16 field_0x103c[8]; - /* 0x104C */ u8 field_0x104C[0x104E - 0x104C]; - /* 0x104E */ s16 field_0x104e[8]; + /* 0x103C */ s16 field_0x103c[9]; + /* 0x104E */ s16 field_0x104e[9]; /* 0x1060 */ cXyz hook_pos; /* 0x106C */ cXyz field_0x106c; /* 0x1078 */ cXyz field_0x1078; diff --git a/include/d/actor/d_a_npc_coach.h b/include/d/actor/d_a_npc_coach.h index d5ee60e2cf..3917d6f11c 100644 --- a/include/d/actor/d_a_npc_coach.h +++ b/include/d/actor/d_a_npc_coach.h @@ -25,10 +25,7 @@ public: /* 0x1C */ f32 max_speed; // 最大速度 - Maximum Speed /* 0x20 */ f32 spring_constant; // バネ係数 - Spring Constant /* 0x24 */ f32 damp_coeff[1]; // 減衰係数 - Damp Coefficient - /* 0x28 */ f32 field_0x28[1]; - /* 0x2C */ f32 field_0x2c; - /* 0x30 */ f32 field_0x30; - /* 0x34 */ f32 field_0x34; + /* 0x28 */ f32 field_0x28[4]; /* 0x38 */ f32 vert_swing_width; // 縦揺れ幅 - Vertical Swing Width /* 0x3C */ f32 shake_dist; // 揺れ距離 - Shake Distance /* 0x40 */ f32 jump_dist; // 跳ね距離 - Jump Distance diff --git a/include/d/actor/d_a_npc_henna.h b/include/d/actor/d_a_npc_henna.h index 73d11c5269..e9a13d13fc 100644 --- a/include/d/actor/d_a_npc_henna.h +++ b/include/d/actor/d_a_npc_henna.h @@ -32,8 +32,7 @@ public: /* 0x620 */ s16 field_0x620; /* 0x624 */ mDoExt_McaMorf* mpMorf; /* 0x628 */ mDoExt_btkAnm* mpBtkAnms[3]; - /* 0x634 */ mDoExt_btpAnm* mpBtpAnms[3]; - /* 0x640 */ u8 field_0x640[0x654 - 0x640]; + /* 0x634 */ mDoExt_btpAnm* mpBtpAnms[8]; /* 0x654 */ s32 field_0x654; /* 0x658 */ s32 field_0x658; /* 0x65C */ s32 mAnmResIndex; diff --git a/include/d/actor/d_a_npc_lf.h b/include/d/actor/d_a_npc_lf.h index 73f66c286f..4b86f24686 100644 --- a/include/d/actor/d_a_npc_lf.h +++ b/include/d/actor/d_a_npc_lf.h @@ -24,8 +24,7 @@ struct lf_s { /* 0x44 */ f32 field_0x44; /* 0x48 */ f32 field_0x48; /* 0x4C */ s16 field_0x4c[2]; - /* 0x50 */ s16 field_0x50[2]; - /* 0x54 */ u8 field_0x54[4]; + /* 0x50 */ s16 field_0x50[4]; /* 0x58 */ f32 field_0x58; /* 0x5C */ f32 field_0x5c; /* 0x60 */ f32 field_0x60; diff --git a/include/d/actor/d_a_obj_maki.h b/include/d/actor/d_a_obj_maki.h index c15b0b9ac1..aad8028e9c 100644 --- a/include/d/actor/d_a_obj_maki.h +++ b/include/d/actor/d_a_obj_maki.h @@ -28,11 +28,9 @@ public: /* 0x580 */ s16 field_0x580[2]; /* 0x584*/ dCcD_Stts mStts; /* 0x5c0 */ dCcD_Sph mSph; - /* 0x6f8 */ u32 field_0x6f8[4]; - /* 0x6fc */ u8 field_0x6fc[0x70c - 0x708]; // Undefined + /* 0x6f8 */ u32 field_0x6f8[5]; /* 0x70c */ LIGHT_INFLUENCE mLightObj; /* 0x72c */ u8 field_0x72c; - /* 0x72d */ u8 field_0x72d[0x730 - 0x72d]; //Undefined }; STATIC_ASSERT(sizeof(obj_maki_class) == 0x730); diff --git a/include/d/actor/d_a_obj_tombo.h b/include/d/actor/d_a_obj_tombo.h index 3dacbe0dad..5dfdcb6451 100644 --- a/include/d/actor/d_a_obj_tombo.h +++ b/include/d/actor/d_a_obj_tombo.h @@ -55,8 +55,7 @@ private: /* 0x710 */ bool mIsHitByBoomerang; /* 0x711 */ u8 mAction; /* 0x712 */ u8 field_0x712; - /* 0x714 */ s16 field_0x714[2]; - /* 0x718 */ s16 field_0x718; + /* 0x714 */ s16 field_0x714[3]; /* 0x71C */ f32 field_0x71c; /* 0x720 */ f32 field_0x720; /* 0x724 */ s16 field_0x724; diff --git a/include/d/d_com_inf_game.h b/include/d/d_com_inf_game.h index 78ab356500..d22d08cd10 100644 --- a/include/d/d_com_inf_game.h +++ b/include/d/d_com_inf_game.h @@ -4833,12 +4833,26 @@ inline void dComIfGd_drawXluListDark() { inline void dComIfGd_drawXluListInvisible() { ZoneScoped; - g_dComIfG_gameInfo.drawlist.drawXluListInvisible(); +#ifdef TARGET_PC + if (dusk::getSettings().game.enableWaterRefraction && + !dusk::getSettings().game.enableFrameInterpolation) { +#endif + g_dComIfG_gameInfo.drawlist.drawXluListInvisible(); +#ifdef TARGET_PC + } +#endif } inline void dComIfGd_drawOpaListInvisible() { ZoneScoped; - g_dComIfG_gameInfo.drawlist.drawOpaListInvisible(); +#ifdef TARGET_PC + if (dusk::getSettings().game.enableWaterRefraction && + !dusk::getSettings().game.enableFrameInterpolation) { +#endif + g_dComIfG_gameInfo.drawlist.drawOpaListInvisible(); +#ifdef TARGET_PC + } +#endif } inline void dComIfGd_drawXluListZxlu() { diff --git a/include/d/d_drawlist.h b/include/d/d_drawlist.h index 80dc9035df..9eecac2d61 100644 --- a/include/d/d_drawlist.h +++ b/include/d/d_drawlist.h @@ -435,6 +435,10 @@ public: m3DLineMatSortPacket[param_1->getMaterialID()].setMatDark(param_1); } +#if TARGET_PC + void refresh3DlineMats(const cXyz& eye); +#endif + void peekZdata() { mPeekZ.peekData(); } void entryZSortListZxlu(J3DPacket* i_packet, cXyz& param_1) { entryZSortXluDrawList(mDrawBuffers[DB_LIST_Z_XLU], i_packet, param_1); diff --git a/include/d/d_menu_fmap2D.h b/include/d/d_menu_fmap2D.h index c0344c7903..9b237f2771 100644 --- a/include/d/d_menu_fmap2D.h +++ b/include/d/d_menu_fmap2D.h @@ -152,6 +152,9 @@ public: void setRegionCursor(u8 i_value) { mRegionCursor = i_value; } void setMapDrawFlag(bool i_flag) { mMapDrawFlag = i_flag; } void resetDrug() { field_0x1238 = 0; } +#if TARGET_PC + void resetScrollArrowMask() { field_0x122d = 0; } +#endif void offArrowDrawFlag() { mArrowDrawFlag = false; } void onArrowDrawFlag() { mArrowDrawFlag = true; } diff --git a/include/d/d_meter_HIO.h b/include/d/d_meter_HIO.h index c0b6b247c5..e52051095f 100644 --- a/include/d/d_meter_HIO.h +++ b/include/d/d_meter_HIO.h @@ -104,12 +104,8 @@ public: /* 0x18 */ f32 mWindowPosX; /* 0x1C */ f32 mWindowPosY; /* 0x20 */ f32 mWindowScale; - /* 0x24 */ f32 mOptionTypeBGPosX[4]; - /* 0x34 */ f32 mTVsettingPosX; - /* 0x38 */ f32 mPointerCalibrationPosX; - /* 0x3C */ f32 mOptionTypeBGPosY[4]; - /* 0x4C */ f32 mTVsettingPosY; - /* 0x50 */ f32 mPointerCalibrationPosY; + /* 0x24 */ f32 mOptionTypeBGPosX[6]; + /* 0x3C */ f32 mOptionTypeBGPosY[6]; /* 0x54 */ f32 mBarScale[2]; // 0: mSelectBarScale | 1: mUnselectBarScale /* 0x5C */ f32 mBackgroundPosY; /* 0x60 */ f32 mArrowOffsetX; @@ -547,8 +543,7 @@ public: /* 0x1C */ f32 mVesselTalkPosY; /* 0x20 */ f32 mVesselPosX_4x3; /* 0x24 */ f32 mVesselPosY_4x3; - /* 0x28 */ f32 mVesselAlpha[3]; - /* 0x34 */ f32 mDropAlpha; + /* 0x28 */ f32 mVesselAlpha[4]; /* 0x38 */ JUtility::TColor mDropOnColorWhite; /* 0x3C */ JUtility::TColor mDropOnColorBlack; /* 0x40 */ JUtility::TColor mDropOffColorWhite; diff --git a/include/dusk/frame_interpolation.h b/include/dusk/frame_interpolation.h new file mode 100644 index 0000000000..98495d18db --- /dev/null +++ b/include/dusk/frame_interpolation.h @@ -0,0 +1,39 @@ +#ifndef DUSK_FRAME_INTERP_H +#define DUSK_FRAME_INTERP_H + +#include +#include +#include +#include + +struct cXyz; + +#ifdef __cplusplus +namespace dusk { +namespace frame_interp { + +void ensure_initialized(); + +void begin_record(); +void end_record(); +void interpolate(float step); +float get_interpolation_step(); +void notify_sim_tick_complete(); +uint32_t begin_presentation_ui_pass(); +uint32_t get_presentation_ui_advance_ticks(); +void end_presentation_ui_pass(); + +void open_child(const void* key, int32_t id); +void close_child(); +void record_final_mtx_raw(const Mtx* dest, const Mtx src); + +bool lookup_replacement(const void* source, Mtx out); +bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out); + +void camera_eye_from_view_mtx(MtxP view_mtx, cXyz* o_eye); + +} // namespace frame_interp +} // namespace dusk +#endif + +#endif diff --git a/include/dusk/gx_helper.h b/include/dusk/gx_helper.h index bf81424c99..8f71cbdf26 100644 --- a/include/dusk/gx_helper.h +++ b/include/dusk/gx_helper.h @@ -5,6 +5,7 @@ #include #include +#include "tracy/Tracy.hpp" #define GX_DEBUG_GROUP(name, ...) \ do { \ @@ -51,4 +52,6 @@ struct GXScopedDebugGroup { } }; +#define GX_AND_TRACY_SCOPED(name) GXScopedDebugGroup scope(name); ZoneScopedN(name); + #endif // DUSK_GX_HELPER_H diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 7724948fd5..8418602251 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -52,7 +52,9 @@ struct UserSettings { // Graphics ConfigVar enableBloom; - ConfigVar useWaterProjectionOffset; + ConfigVar enableWaterRefraction; + ConfigVar enableFrameInterpolation; + ConfigVar shadowResolutionMultiplier; // Audio ConfigVar noLowHpSound; @@ -76,6 +78,7 @@ struct UserSettings { ConfigVar graphicsBackend; ConfigVar skipPreLaunchUI; ConfigVar showPipelineCompilation; + ConfigVar wasPresetChosen; } backend; }; diff --git a/include/dusk/time.h b/include/dusk/time.h index 94f819f76b..948a2fc171 100644 --- a/include/dusk/time.h +++ b/include/dusk/time.h @@ -15,12 +15,10 @@ #include #include #include +#else +#include "SDL3/SDL_timer.h" #endif -#include "dusk/logging.h" - -constexpr auto DUSK_FRAME_PERIOD = std::chrono::duration_cast(std::chrono::duration(1001.0 / 30000.0)); - class Limiter { using delta_clock = std::chrono::high_resolution_clock; using duration_t = std::chrono::nanoseconds; @@ -101,7 +99,7 @@ private: } while (current.QuadPart - start.QuadPart < ticksToWait); } #else - void NanoSleep(const duration_t duration) { std::this_thread::sleep_for(duration); } + void NanoSleep(const duration_t duration) { SDL_DelayPrecise(duration.count()); } #endif }; diff --git a/include/m_Do/m_Do_ext.h b/include/m_Do/m_Do_ext.h index e6973dc782..77d046f0de 100644 --- a/include/m_Do/m_Do_ext.h +++ b/include/m_Do/m_Do_ext.h @@ -541,6 +541,9 @@ public: virtual int getMaterialID() = 0; virtual void setMaterial() = 0; virtual void draw() = 0; +#if TARGET_PC + virtual void refreshGeometryForPresentationEye(const cXyz& eye) {} +#endif /* 0x4 */ mDoExt_3DlineMat_c* field_0x4; }; @@ -582,11 +585,19 @@ class dKy_tevstr_c; class mDoExt_3DlineMat1_c : public mDoExt_3DlineMat_c { public: int init(u16, u16, ResTIMG*, int); +#if TARGET_PC + void update(int, GXColor&, dKy_tevstr_c*, const cXyz* presentationEye = nullptr); + void update(int, f32, GXColor&, u16, dKy_tevstr_c*, const cXyz* presentationEye = nullptr); +#else void update(int, GXColor&, dKy_tevstr_c*); void update(int, f32, GXColor&, u16, dKy_tevstr_c*); +#endif int getMaterialID() { return 1; } void setMaterial(); void draw(); +#if TARGET_PC + void refreshGeometryForPresentationEye(const cXyz& eye) override; +#endif cXyz* getPos(int i_idx) { return mpLines[i_idx].field_0x0; } f32* getSize(int i_idx) { return mpLines[i_idx].field_0x4; } @@ -600,6 +611,11 @@ private: /* 0x34 */ u16 field_0x34; /* 0x36 */ u8 mIsDrawn; /* 0x38 */ mDoExt_3Dline_c* mpLines; +#if TARGET_PC + u8 mInterpLineKind; + f32 mInterpLineF; + u16 mInterpLineU16; +#endif }; class mDoExt_3DlineMat2_c : public mDoExt_3DlineMat1_c { @@ -616,6 +632,9 @@ public: void setMatDark(mDoExt_3DlineMat_c* i_mat) { setMat(i_mat); } void setMat(mDoExt_3DlineMat_c*); +#if TARGET_PC + mDoExt_3DlineMat_c* getFirstMat() const { return mp3DlineMat; } +#endif virtual void draw(); virtual ~mDoExt_3DlineMatSortPacket() {} diff --git a/include/m_Do/m_Do_graphic.h b/include/m_Do/m_Do_graphic.h index 421e1ae47f..5a97f4a326 100644 --- a/include/m_Do/m_Do_graphic.h +++ b/include/m_Do/m_Do_graphic.h @@ -8,12 +8,14 @@ #include #endif +#define FB_WIDTH_BASE (608) +#define FB_HEIGHT_BASE (448) #if WIDESCREEN_SUPPORT && !TARGET_PC #define FB_WIDTH (640) #define FB_HEIGHT (456) #else -#define FB_WIDTH (608) -#define FB_HEIGHT (448) +#define FB_WIDTH FB_WIDTH_BASE +#define FB_HEIGHT FB_HEIGHT_BASE #endif int mDoGph_Create(); @@ -237,6 +239,7 @@ public: static void* getZbufferTex() { return mZbufferTex; } static void setFadeRate(f32 rate) { mFadeRate = rate; } static f32 getFadeRate() { return mFadeRate; } + static f32 getFadeSpeed() { return mFadeSpeed; } static bloom_c* getBloom() { return &m_bloom; } static GXColor& getFadeColor() { return mFadeColor; } static GXColor& getBackColor() { return mBackColor; } @@ -275,7 +278,7 @@ public: #if TARGET_PC static void onWide(f32 width, f32 height); - #else + #else static void onWide(); #endif diff --git a/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DModel.h b/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DModel.h index 5ec2aa5f2e..a27db4fa00 100644 --- a/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DModel.h +++ b/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DModel.h @@ -3,6 +3,7 @@ #include "JSystem/J3DGraphAnimator/J3DSkinDeform.h" #include "JSystem/J3DGraphBase/J3DPacket.h" +#include "dusk/frame_interpolation.h" #include enum J3DMdlFlag { @@ -101,7 +102,14 @@ public: void setUserArea(uintptr_t area) { mUserArea = area; } uintptr_t getUserArea() const { return mUserArea; } Vec* getBaseScale() { return &mBaseScale; } - void setAnmMtx(int jointNo, Mtx m) { mMtxBuffer->setAnmMtx(jointNo, m); } + void setAnmMtx(int jointNo, Mtx m) { + mMtxBuffer->setAnmMtx(jointNo, m); +#ifdef TARGET_PC + dusk::frame_interp::record_final_mtx_raw( + reinterpret_cast(mMtxBuffer->getAnmMtx(jointNo)), + mMtxBuffer->getAnmMtx(jointNo)); +#endif + } MtxP getAnmMtx(int jointNo) { return mMtxBuffer->getAnmMtx(jointNo); } MtxP getWeightAnmMtx(int i) { return mMtxBuffer->getWeightAnmMtx(i); } J3DSkinDeform* getSkinDeform() { return mSkinDeform; } diff --git a/libs/JSystem/include/JSystem/J3DGraphBase/J3DStruct.h b/libs/JSystem/include/JSystem/J3DGraphBase/J3DStruct.h index d970e14048..b565ad78f8 100644 --- a/libs/JSystem/include/JSystem/J3DGraphBase/J3DStruct.h +++ b/libs/JSystem/include/JSystem/J3DGraphBase/J3DStruct.h @@ -113,9 +113,6 @@ struct J3DFogInfo { bool operator==(J3DFogInfo&) const; J3DFogInfo& operator=(const J3DFogInfo&); - // TODO: Fog data should be converted from big endian (probably?) - // Not sure TP uses it. - /* 0x00 */ u8 mType; /* 0x01 */ u8 mAdjEnable; /* 0x02 */ u16 mCenter; diff --git a/libs/JSystem/include/JSystem/J3DGraphBase/J3DSys.h b/libs/JSystem/include/JSystem/J3DGraphBase/J3DSys.h index b3c4f1eb16..8c4bbd06e3 100644 --- a/libs/JSystem/include/JSystem/J3DGraphBase/J3DSys.h +++ b/libs/JSystem/include/JSystem/J3DGraphBase/J3DSys.h @@ -6,6 +6,7 @@ #include "JSystem/J3DAssert.h" #include "JSystem/JMath/JMath.h" +#include "dusk/frame_interpolation.h" #include "dusk/endian.h" enum J3DSysDrawBuf { @@ -188,7 +189,15 @@ struct J3DSys { Mtx& getModelDrawMtx(u16 no) { return mModelDrawMtx[no]; } J3DShapePacket* getShapePacket() { return mShapePacket; } - void setViewMtx(const Mtx m) { MTXCopy(m, mViewMtx); } + void setViewMtx(const Mtx m) { +#ifdef TARGET_PC + Mtx patched; + if (dusk::frame_interp::lookup_replacement(m, patched)) { + m = patched; + } +#endif + MTXCopy(m, mViewMtx); + } J3DModel* getModel() { return mModel; } diff --git a/libs/JSystem/include/JSystem/JFramework/JFWDisplay.h b/libs/JSystem/include/JSystem/JFramework/JFWDisplay.h index 28967cc0a2..fab39a296b 100644 --- a/libs/JSystem/include/JSystem/JFramework/JFWDisplay.h +++ b/libs/JSystem/include/JSystem/JFramework/JFWDisplay.h @@ -101,6 +101,10 @@ public: void setDrawDoneMethod(EDrawDone drawDone) { mDrawDoneMethod = drawDone; } void setFader(JUTFader* fader) { mFader = fader; } +#ifdef TARGET_PC + // For frame interpolation + void setFaderSimSteps(u32 steps); +#endif void resetFader() { setFader(NULL); } JUTFader* getFader() const { return mFader; } void setClearColor(JUtility::TColor color) { mClearColor = color; } diff --git a/libs/JSystem/src/J2DGraph/J2DMatBlock.cpp b/libs/JSystem/src/J2DGraph/J2DMatBlock.cpp index 6fbf8444be..bae96551be 100644 --- a/libs/JSystem/src/J2DGraph/J2DMatBlock.cpp +++ b/libs/JSystem/src/J2DGraph/J2DMatBlock.cpp @@ -1418,7 +1418,7 @@ bool J2DTevBlock8::insertTexture(u32 param_0, ResTIMG const* p_timg, JUTPalette* } else { texture->storeTIMG(p_timg, p_tlut); } - bool local_38[4]; + bool local_38[8]; for (u8 i = 0; i < 8; i++) { local_38[i] = (mUndeleteFlag & 1 << i) != 0; } diff --git a/libs/JSystem/src/J2DGraph/J2DPicture.cpp b/libs/JSystem/src/J2DGraph/J2DPicture.cpp index 8072c32d41..774b3d31af 100644 --- a/libs/JSystem/src/J2DGraph/J2DPicture.cpp +++ b/libs/JSystem/src/J2DGraph/J2DPicture.cpp @@ -511,6 +511,7 @@ void J2DPicture::drawSelf(f32 param_0, f32 param_1) { } void J2DPicture::drawSelf(f32 param_0, f32 param_1, Mtx* param_2) { + GX_AND_TRACY_SCOPED("J2DPicture::drawSelf") if (mTexture[0] != NULL && mTextureNum != 0) { drawFullSet(mGlobalBounds.i.x + param_0, mGlobalBounds.i.y + param_1, getWidth(), getHeight(), param_2); @@ -527,6 +528,7 @@ void J2DPicture::drawFullSet(f32 param_0, f32 param_1, f32 param_2, f32 param_3, void J2DPicture::draw(f32 x, f32 y, f32 width, f32 height, bool mirrorX, bool mirrorY, bool rotate90) { + GX_AND_TRACY_SCOPED("J2DPicture::draw") if (isVisible() && mTextureNum != 0 && mTexture[0] != NULL) { f32 x2 = x + width; f32 y2 = y + height; diff --git a/libs/JSystem/src/J2DGraph/J2DPictureEx.cpp b/libs/JSystem/src/J2DGraph/J2DPictureEx.cpp index 6c4048ba31..ba69048a4b 100644 --- a/libs/JSystem/src/J2DGraph/J2DPictureEx.cpp +++ b/libs/JSystem/src/J2DGraph/J2DPictureEx.cpp @@ -78,8 +78,21 @@ bool J2DPictureEx::prepareTexture(u8 param_0) { return true; } +#if TARGET_PC +bool checkAlphaCull(const J2DPictureEx* pic) { + return pic->mColorAlpha == 0; +} +#endif + void J2DPictureEx::drawSelf(f32 param_0, f32 param_1, f32 (*param_2)[3][4]) { + GX_AND_TRACY_SCOPED("J2DPictureEx::drawSelf") if (mMaterial != NULL) { +#if TARGET_PC + if (checkAlphaCull(this)) { + return; + } +#endif + mMaterial->setGX(); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); diff --git a/libs/JSystem/src/J2DGraph/J2DScreen.cpp b/libs/JSystem/src/J2DGraph/J2DScreen.cpp index eec5ef057d..c44859046d 100644 --- a/libs/JSystem/src/J2DGraph/J2DScreen.cpp +++ b/libs/JSystem/src/J2DGraph/J2DScreen.cpp @@ -278,6 +278,8 @@ J2DPane* J2DScreen::searchUserInfo(u64 tag) { } void J2DScreen::drawSelf(f32 param_0, f32 param_1, Mtx* param_2) { + GX_AND_TRACY_SCOPED("J2DScreen::drawSelf") + JUtility::TColor color(mColor); u8 alpha = (color.a * mAlpha) / 255; diff --git a/libs/JSystem/src/J2DGraph/J2DTextBox.cpp b/libs/JSystem/src/J2DGraph/J2DTextBox.cpp index 86b36bdadf..970f120140 100644 --- a/libs/JSystem/src/J2DGraph/J2DTextBox.cpp +++ b/libs/JSystem/src/J2DGraph/J2DTextBox.cpp @@ -391,6 +391,8 @@ void J2DTextBox::drawSelf(f32 param_0, f32 param_1) { } void J2DTextBox::drawSelf(f32 param_0, f32 param_1, Mtx* p_mtx) { + GX_AND_TRACY_SCOPED("J2DTextBox::drawSelf") + Mtx m; J2DPrint print(mFont, mCharSpacing, mLineSpacing, mCharColor, mGradientColor, mBlackColor, diff --git a/libs/JSystem/src/J2DGraph/J2DTextBoxEx.cpp b/libs/JSystem/src/J2DGraph/J2DTextBoxEx.cpp index f615c44c8c..0911740898 100644 --- a/libs/JSystem/src/J2DGraph/J2DTextBoxEx.cpp +++ b/libs/JSystem/src/J2DGraph/J2DTextBoxEx.cpp @@ -93,6 +93,7 @@ J2DTextBoxEx::~J2DTextBoxEx() { } void J2DTextBoxEx::drawSelf(f32 param_0, f32 param_1, Mtx* p_mtx) { + GX_AND_TRACY_SCOPED("J2DTextBoxEx::drawSelf") Mtx m; JUTFont* font = NULL; diff --git a/libs/JSystem/src/J2DGraph/J2DWindow.cpp b/libs/JSystem/src/J2DGraph/J2DWindow.cpp index 35de369709..655dff4938 100644 --- a/libs/JSystem/src/J2DGraph/J2DWindow.cpp +++ b/libs/JSystem/src/J2DGraph/J2DWindow.cpp @@ -410,6 +410,8 @@ void J2DWindow::drawSelf(f32 param_0, f32 param_1) { } void J2DWindow::drawSelf(f32 param_0, f32 param_1, Mtx* param_2) { + GX_AND_TRACY_SCOPED("J2DWindow::drawSelf") + JGeometry::TBox2 stack_50(mBounds); stack_50.addPos(JGeometry::TVec2(param_0, param_1)); if (stack_50.getWidth() >= field_0x140 && stack_50.getHeight() >= field_0x142) { diff --git a/libs/JSystem/src/J2DGraph/J2DWindowEx.cpp b/libs/JSystem/src/J2DGraph/J2DWindowEx.cpp index b8c4e5f79e..fe4c5df9e6 100644 --- a/libs/JSystem/src/J2DGraph/J2DWindowEx.cpp +++ b/libs/JSystem/src/J2DGraph/J2DWindowEx.cpp @@ -122,6 +122,7 @@ J2DWindowEx::~J2DWindowEx() { } void J2DWindowEx::drawSelf(f32 param_0, f32 param_1, f32 (*param_2)[3][4]) { + GX_AND_TRACY_SCOPED("J2DWindowEx::drawSelf") JGeometry::TBox2 aTStack_50(mBounds); Mtx auStack_40; aTStack_50.addPos(JGeometry::TVec2(param_0, param_1)); diff --git a/libs/JSystem/src/J3DGraphAnimator/J3DModel.cpp b/libs/JSystem/src/J3DGraphAnimator/J3DModel.cpp index c0f6dda58b..6233d6dff7 100644 --- a/libs/JSystem/src/J3DGraphAnimator/J3DModel.cpp +++ b/libs/JSystem/src/J3DGraphAnimator/J3DModel.cpp @@ -8,6 +8,7 @@ #include "JSystem/J3DGraphBase/J3DShapeMtx.h" #include "JSystem/J3DGraphBase/J3DSys.h" #include "JSystem/JKernel/JKRHeap.h" +#include "dusk/frame_interpolation.h" #define J3D_ASSERTMSG(LINE, COND, MSG) JUT_ASSERT_MSG(LINE, (COND) != 0, MSG) #define J3D_WARN1(LINE, MSG, ARG1) JUT_WARN(LINE, MSG, ARG1) @@ -448,6 +449,16 @@ void J3DModel::calc() { if (mCalcCallBack != NULL) { mCalcCallBack(this, 0); } + +#ifdef TARGET_PC + for (u16 i = 0; i < mModelData->getJointNum(); ++i) { + dusk::frame_interp::record_final_mtx_raw(reinterpret_cast(getAnmMtx(i)), getAnmMtx(i)); + } + + for (u16 i = 0; i < mModelData->getWEvlpMtxNum(); ++i) { + dusk::frame_interp::record_final_mtx_raw(reinterpret_cast(getWeightAnmMtx(i)), getWeightAnmMtx(i)); + } +#endif } void J3DModel::entry() { @@ -484,11 +495,17 @@ void J3DModel::viewCalc() { if (getMtxCalcMode() == 2) { J3DCalcViewBaseMtx(j3dSys.getViewMtx(), mBaseScale, mBaseTransformMtx, (MtxP)&mInternalView); +#ifdef TARGET_PC + dusk::frame_interp::record_final_mtx_raw(&mInternalView, mInternalView); +#endif } } else if (isCpuSkinningOn()) { if (getMtxCalcMode() == 2) { J3DCalcViewBaseMtx(j3dSys.getViewMtx(), mBaseScale, mBaseTransformMtx, (MtxP)&mInternalView); +#ifdef TARGET_PC + dusk::frame_interp::record_final_mtx_raw(&mInternalView, mInternalView); +#endif } } else if (checkFlag(J3DMdlFlag_SkinPosCpu)) { mMtxBuffer->calcDrawMtx(getMtxCalcMode(), mBaseScale, mBaseTransformMtx); @@ -509,6 +526,12 @@ void J3DModel::viewCalc() { DCStoreRange(getNrmMtxPtr(), mModelData->getDrawMtxNum() * sizeof(Mtx33)); } +#ifdef TARGET_PC + for (u16 i = 0; i < mModelData->getDrawMtxNum(); ++i) { + dusk::frame_interp::record_final_mtx_raw(&getDrawMtxPtr()[i], getDrawMtxPtr()[i]); + } +#endif + prepareShapePackets(); } diff --git a/libs/JSystem/src/J3DGraphBase/J3DShapeMtx.cpp b/libs/JSystem/src/J3DGraphBase/J3DShapeMtx.cpp index 6048ea45ee..d9af135999 100644 --- a/libs/JSystem/src/J3DGraphBase/J3DShapeMtx.cpp +++ b/libs/JSystem/src/J3DGraphBase/J3DShapeMtx.cpp @@ -6,9 +6,20 @@ #include "JSystem/J3DGraphBase/J3DMatBlock.h" #include "JSystem/J3DGraphBase/J3DSys.h" #include "JSystem/J3DGraphBase/J3DTexture.h" +#include "dusk/frame_interpolation.h" u16 J3DShapeMtx::sMtxLoadCache[10]; +#ifdef TARGET_PC +static void J3DFrameInterpConcat(MtxP lhs, MtxP rhs, Mtx out) { + if (!dusk::frame_interp::lookup_concat_replacement(lhs, rhs, out)) { + MTXConcat(lhs, rhs, out); + } +} +#else +#define J3DFrameInterpConcat MTXConcat +#endif + void J3DShapeMtx::resetMtxLoadCache() { sMtxLoadCache[0] = sMtxLoadCache[1] = @@ -290,7 +301,7 @@ void J3DDifferedTexMtx::loadExecute(f32 const (*param_0)[4]) { void J3DShapeMtxConcatView::loadMtxConcatView_PNGP(int slot, u16 drw) const { Mtx m; - MTXConcat(*j3dSys.getShapePacket()->getBaseMtxPtr(), j3dSys.getModelDrawMtx(drw), m); + J3DFrameInterpConcat(*j3dSys.getShapePacket()->getBaseMtxPtr(), j3dSys.getModelDrawMtx(drw), m); J3DDifferedTexMtx::load(m); J3DFifoLoadPosMtxImm(m, slot * 3); loadNrmMtx(slot, drw, m); @@ -298,7 +309,7 @@ void J3DShapeMtxConcatView::loadMtxConcatView_PNGP(int slot, u16 drw) const { void J3DShapeMtxConcatView::loadMtxConcatView_PCPU(int slot, u16 drw) const { Mtx m; - MTXConcat(*j3dSys.getShapePacket()->getBaseMtxPtr(), j3dSys.getModelDrawMtx(drw), m); + J3DFrameInterpConcat(*j3dSys.getShapePacket()->getBaseMtxPtr(), j3dSys.getModelDrawMtx(drw), m); J3DDifferedTexMtx::load(m); J3DFifoLoadPosMtxImm(*j3dSys.getShapePacket()->getBaseMtxPtr(), slot * 3); loadNrmMtx(slot, drw, m); @@ -306,7 +317,7 @@ void J3DShapeMtxConcatView::loadMtxConcatView_PCPU(int slot, u16 drw) const { void J3DShapeMtxConcatView::loadMtxConcatView_NCPU(int slot, u16 drw) const { Mtx m; - MTXConcat(*j3dSys.getShapePacket()->getBaseMtxPtr(), j3dSys.getModelDrawMtx(drw), m); + J3DFrameInterpConcat(*j3dSys.getShapePacket()->getBaseMtxPtr(), j3dSys.getModelDrawMtx(drw), m); J3DDifferedTexMtx::load(m); J3DFifoLoadPosMtxImm(m, slot * 3); J3DFifoLoadNrmMtxImm(*j3dSys.getShapePacket()->getBaseMtxPtr(), slot * 3); @@ -318,7 +329,7 @@ void J3DShapeMtxConcatView::loadMtxConcatView_NCPU(int slot, u16 drw) const { void J3DShapeMtxConcatView::loadMtxConcatView_PNCPU(int slot, u16 drw) const { if (J3DDifferedTexMtx::sTexGenBlock != NULL) { Mtx m; - MTXConcat(*j3dSys.getShapePacket()->getBaseMtxPtr(), j3dSys.getModelDrawMtx(drw), m); + J3DFrameInterpConcat(*j3dSys.getShapePacket()->getBaseMtxPtr(), j3dSys.getModelDrawMtx(drw), m); J3DDifferedTexMtx::loadExecute(m); } @@ -331,7 +342,7 @@ void J3DShapeMtxConcatView::loadMtxConcatView_PNCPU(int slot, u16 drw) const { void J3DShapeMtxConcatView::loadMtxConcatView_PNGP_LOD(int slot, u16 drw) const { Mtx m; - MTXConcat(*j3dSys.getShapePacket()->getBaseMtxPtr(), j3dSys.getModelDrawMtx(drw), m); + J3DFrameInterpConcat(*j3dSys.getShapePacket()->getBaseMtxPtr(), j3dSys.getModelDrawMtx(drw), m); Mtx copy; j3dSys.getModel()->getModelData()->getInvJointMtx(drw).to_host(copy); MTXConcat(m, copy, m); @@ -490,9 +501,11 @@ void J3DShapeMtxBBoardConcatView::load() const { u16 draw_mtx_index = j3dSys.getModel()->getModelData()->getDrawMtxIndex(mUseMtxIndex); if (j3dSys.getModel()->getModelData()->getDrawMtxFlag(mUseMtxIndex) == 0) { - MTXConcat(j3dSys.getViewMtx(), j3dSys.getModel()->getMtxBuffer()->getUserAnmMtx(draw_mtx_index), mtx); + J3DFrameInterpConcat(j3dSys.getViewMtx(), + j3dSys.getModel()->getMtxBuffer()->getUserAnmMtx(draw_mtx_index), mtx); } else { - MTXConcat(j3dSys.getViewMtx(), j3dSys.getModel()->getWeightAnmMtx(draw_mtx_index), mtx); + J3DFrameInterpConcat(j3dSys.getViewMtx(), + j3dSys.getModel()->getWeightAnmMtx(draw_mtx_index), mtx); } J3DCalcBBoardMtx(mtx); @@ -521,9 +534,11 @@ void J3DShapeMtxYBBoardConcatView::load() const { u16 draw_mtx_index = j3dSys.getModel()->getModelData()->getDrawMtxIndex(mUseMtxIndex); if (j3dSys.getModel()->getModelData()->getDrawMtxFlag(mUseMtxIndex) == 0) { - MTXConcat(j3dSys.getViewMtx(), j3dSys.getModel()->getMtxBuffer()->getUserAnmMtx(draw_mtx_index), mtx1); + J3DFrameInterpConcat(j3dSys.getViewMtx(), + j3dSys.getModel()->getMtxBuffer()->getUserAnmMtx(draw_mtx_index), mtx1); } else { - MTXConcat(j3dSys.getViewMtx(), j3dSys.getModel()->getWeightAnmMtx(draw_mtx_index), mtx1); + J3DFrameInterpConcat(j3dSys.getViewMtx(), + j3dSys.getModel()->getWeightAnmMtx(draw_mtx_index), mtx1); } J3DCalcYBBoardMtx(mtx1); diff --git a/libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory.cpp b/libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory.cpp index 4383632da7..7aa4d31cd4 100644 --- a/libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory.cpp +++ b/libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory.cpp @@ -9,6 +9,7 @@ #include "JSystem/JMath/JMath.h" #include "JSystem/JSupport/JSupport.h" #include "JSystem/JUtility/JUTAssert.h" +#include "dusk/logging.h" J3DMaterialFactory::J3DMaterialFactory(J3DMaterialBlock const& i_block) { mMaterialNum = i_block.mMaterialNum; @@ -656,7 +657,13 @@ J3DIndTexOrder J3DMaterialFactory::newIndTexOrder(int i_idx, int i_no) const { J3DIndTexMtx J3DMaterialFactory::newIndTexMtx(int i_idx, int i_no) const { J3DIndTexMtx dflt; if (mpIndInitData[i_idx].mEnabled == true) { +#if TARGET_LITTLE_ENDIAN + J3DIndTexMtxInfo indTexMtxInfo = mpIndInitData[i_idx].mIndTexMtxInfo[i_no]; + be_swap(indTexMtxInfo.field_0x0); + return indTexMtxInfo; +#else return J3DIndTexMtx(mpIndInitData[i_idx].mIndTexMtxInfo[i_no]); +#endif } else { return dflt; } @@ -684,7 +691,19 @@ J3DFog J3DMaterialFactory::newFog(int i_idx) const { J3DFog fog; J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]]; if (mtl_init_data->mFogIdx != 0xffff) { +#if TARGET_LITTLE_ENDIAN + J3DFogInfo fogInfo = mpFogInfo[mtl_init_data->mFogIdx]; + be_swap(fogInfo.mCenter); + be_swap(fogInfo.mStartZ); + be_swap(fogInfo.mEndZ); + be_swap(fogInfo.mNearZ); + be_swap(fogInfo.mFarZ); + for (int i = 0; i < 10; i++) + be_swap(fogInfo.mFogAdjTable.r[i]); + fog.setFogInfo(fogInfo); +#else fog.setFogInfo(mpFogInfo[mtl_init_data->mFogIdx]); +#endif } return fog; } diff --git a/libs/JSystem/src/JFramework/JFWDisplay.cpp b/libs/JSystem/src/JFramework/JFWDisplay.cpp index 27873ecbe3..d3e7abf0a1 100644 --- a/libs/JSystem/src/JFramework/JFWDisplay.cpp +++ b/libs/JSystem/src/JFramework/JFWDisplay.cpp @@ -1,26 +1,30 @@ #include "JSystem/JSystem.h" // IWYU pragma: keep -#include -#include -#include -#include -#include -#include "SDL3/SDL_timer.h" -#include "JSystem/J2DGraph/J2DOrthoGraph.h" #include "JSystem/JFramework/JFWDisplay.h" +#include "JSystem/J2DGraph/J2DOrthoGraph.h" #include "JSystem/JKernel/JKRHeap.h" #include "JSystem/JUtility/JUTAssert.h" #include "JSystem/JUtility/JUTConsole.h" #include "JSystem/JUtility/JUTDbPrint.h" #include "JSystem/JUtility/JUTProcBar.h" -#include "aurora/aurora.h" +#include +#include +#include "global.h" +#include + +#ifdef TARGET_PC #include "dusk/dusk.h" #include "dusk/gx_helper.h" #include "dusk/logging.h" #include "dusk/settings.h" -#include "global.h" +#include "dusk/time.h" + +#include "SDL3/SDL_timer.h" #include "tracy/Tracy.hpp" +#include +#endif + void JFWDisplay::ctor_subroutine(bool enableAlpha) { mEnableAlpha = enableAlpha; mClamp = GX_CLAMP_TOP | GX_CLAMP_BOTTOM; @@ -201,6 +205,14 @@ void JFWDisplay::preGX() { } } +#ifdef TARGET_PC +static s32 s_faderSimSteps = -1; + +void JFWDisplay::setFaderSimSteps(u32 steps) { + s_faderSimSteps = static_cast(steps); +} +#endif + void JFWDisplay::endGX() { s32 bufferNum = JUTXfb::getManager()->getBufferNum(); u16 width = JUTVideo::getManager()->getFbWidth(); @@ -211,7 +223,26 @@ void JFWDisplay::endGX() { if (mFader != NULL) { ortho.setPort(); +#ifdef TARGET_PC + if (dusk::getSettings().game.enableFrameInterpolation) { + u32 advance_count = 1; + if (s_faderSimSteps >= 0) { + advance_count = static_cast(s_faderSimSteps); + s_faderSimSteps = -1; + } + for (u32 i = 0; i < advance_count; i++) { + mFader->control(); + } + if (mFader->getStatus() != 1) { + mFader->draw(); + } + } else { + mFader->control(); + mFader->draw(); + } +#else mFader->control(); +#endif } ortho.setPort(); JUTDbPrint::getManager()->flush(); @@ -350,49 +381,63 @@ void JFWDisplay::waitBlanking(int param_0) { } } +#if TARGET_PC +constexpr auto FRAME_PERIOD = std::chrono::duration_cast( + std::chrono::duration(1001.0 / 30000.0)); +constexpr auto RETRACE_PERIOD = FRAME_PERIOD / 2; + +static void waitPrecise(Limiter& limiter, Uint64 targetNs) { + const auto sleepTime = limiter.SleepTime(std::chrono::nanoseconds(targetNs)); + dusk::frameUsagePct = + 100.0f * (1.0f - static_cast(sleepTime.count()) / static_cast(targetNs)); + limiter.Sleep(std::chrono::nanoseconds(targetNs)); +} +#endif + static void waitForTick(u32 p1, u16 p2) { - ZoneScopedC(tracy::Color::DimGray); - #if TARGET_PC +#if TARGET_PC + if (dusk::getSettings().game.enableFrameInterpolation) { + return; + } if (dusk::getTransientSettings().skipFrameRateLimit) { p1 = OS_TIMER_CLOCK / 120; } - #endif + ZoneScopedC(tracy::Color::DimGray); +#endif - if (p1 != 0) - { + if (p1 != 0) { +#if TARGET_PC + static Limiter limiter; + waitPrecise(limiter, static_cast(OSTicksToMicroseconds(p1)) * 1000ULL); +#else static OSTime nextTick = OSGetTime(); OSTime time = OSGetTime(); - OSTime waitTime = (nextTick > time) ? (nextTick - time) : 0; while (time < nextTick) { JFWDisplay::getManager()->threadSleep((nextTick - time)); time = OSGetTime(); } - dusk::frameUsagePct = 100.0f * (1.0f - (float)waitTime / (float)p1); nextTick = time + p1; +#endif } else { - static u32 nextCount = VIGetRetraceCount(); u32 uVar1 = (p2 == 0) ? 1 : p2; +#if TARGET_PC + static Limiter limiter; + waitPrecise(limiter, static_cast((RETRACE_PERIOD * uVar1).count())); +#else + static u32 nextCount = VIGetRetraceCount(); OSMessage msg; do { if (!OSReceiveMessage(JUTVideo::getManager()->getMessageQueue(), &msg, - OS_MESSAGE_BLOCK)) - { + OS_MESSAGE_BLOCK)) { msg = 0; } } while (((intptr_t)msg - (intptr_t)nextCount) < 0); - dusk::frameUsagePct = 100.0f; nextCount = (intptr_t)msg + uVar1; +#endif } } JSUList JFWAlarm::sList(false); - -#if TARGET_PC -void JFWDisplay::threadSleep(s64 time) { - SDL_DelayNS(OSTicksToMicroseconds(time) * 1'000); -} -#else - static void JFWThreadAlarmHandler(OSAlarm* p_alarm, OSContext* p_ctx) { JFWAlarm* alarm = static_cast(p_alarm); alarm->removeLink(); @@ -410,7 +455,6 @@ void JFWDisplay::threadSleep(s64 time) { OSSuspendThread(alarm.getThread()); OSRestoreInterrupts(status); } -#endif static void dummy() { JUTXfb::getManager()->setDisplayingXfbIndex(0); @@ -450,7 +494,7 @@ void JFWDisplay::clearEfb(GXColor color) { void JFWDisplay::clearEfb(int param_0, int param_1, int param_2, int param_3, GXColor color) { STUB_RET(); - + u16 width; u16 height; Mtx44 mtx; diff --git a/libs/JSystem/src/JUtility/JUTFader.cpp b/libs/JSystem/src/JUtility/JUTFader.cpp index 754979115c..1848e92787 100644 --- a/libs/JSystem/src/JUtility/JUTFader.cpp +++ b/libs/JSystem/src/JUtility/JUTFader.cpp @@ -19,24 +19,24 @@ JUTFader::JUTFader(int x, int y, int width, int height, JUtility::TColor pColor) void JUTFader::control() { if (0 <= mEStatus && mEStatus-- == 0) { - mStatus = field_0x24; - } + mStatus = field_0x24; + } - if (mStatus == 1) { - return; - } + if (mStatus == 1) { + return; + } - switch (mStatus) { + switch (mStatus) { case 0: mColor.a = 0xFF; break; case 2: - #if AVOID_UB +#if AVOID_UB if (field_0x8 == 0) { mStatus = 1; break; } - #endif +#endif mColor.a = 0xFF - ((++field_0xa * 0xFF) / field_0x8); if (field_0xa >= field_0x8) { @@ -45,12 +45,12 @@ void JUTFader::control() { break; case 3: - #if AVOID_UB +#if AVOID_UB if (field_0x8 == 0) { mStatus = 0; break; } - #endif +#endif mColor.a = ((++field_0xa * 0xFF) / field_0x8); if (field_0xa >= field_0x8) { @@ -58,8 +58,12 @@ void JUTFader::control() { } break; - } - draw(); + } + +#ifndef TARGET_PC + // Frame interpolation: draw call moved to JFWDisplay + draw(); +#endif } void JUTFader::draw() { diff --git a/libs/JSystem/src/JUtility/JUTResFont.cpp b/libs/JSystem/src/JUtility/JUTResFont.cpp index 10443d6404..206968f053 100644 --- a/libs/JSystem/src/JUtility/JUTResFont.cpp +++ b/libs/JSystem/src/JUtility/JUTResFont.cpp @@ -435,10 +435,10 @@ void JUTResFont::loadImage(int code, GXTexMapID id){ mHeight = cellRow * cellH; #if TARGET_PC - const auto found = mGlyphTextures->textures.find(code); + const auto found = mGlyphTextures->textures.find(pageIdx); GXTexObj* texObj; if (found == mGlyphTextures->textures.end()) { - texObj = &mGlyphTextures->textures[code]; + texObj = &mGlyphTextures->textures[pageIdx]; void* pImg = &mpGlyphBlocks[i]->data[pageIdx * mpGlyphBlocks[i]->textureSize]; GXInitTexObj(texObj, pImg, mpGlyphBlocks[i]->textureWidth, mpGlyphBlocks[i]->textureHeight, (GXTexFmt)(u16)mpGlyphBlocks[i]->textureFormat, diff --git a/libs/JSystem/src/JUtility/JUTVideo.cpp b/libs/JSystem/src/JUtility/JUTVideo.cpp index bb1f2cee3b..f718fcac23 100644 --- a/libs/JSystem/src/JUtility/JUTVideo.cpp +++ b/libs/JSystem/src/JUtility/JUTVideo.cpp @@ -182,7 +182,9 @@ void JUTVideo::postRetraceProc(u32 retrace_count) { sManager->mPostCallback(retrace_count); } +#ifndef TARGET_PC // Not read by JFWDisplay waitForTick OSSendMessage(&sManager->mMessageQueue, (OSMessage)(uintptr_t)VIGetRetraceCount(), OS_MESSAGE_NOBLOCK); +#endif } void JUTVideo::setRenderMode(GXRenderModeObj const* pObj) { diff --git a/src/DynamicLink.cpp b/src/DynamicLink.cpp index 5c5a2030d4..dae7da52de 100644 --- a/src/DynamicLink.cpp +++ b/src/DynamicLink.cpp @@ -14,6 +14,7 @@ #include "os_report.h" #include "dusk/logging.h" +#if !TARGET_PC DynamicModuleControlBase* DynamicModuleControlBase::mFirst; DynamicModuleControlBase* DynamicModuleControlBase::mLast; @@ -139,6 +140,7 @@ DynamicModuleControl::DynamicModuleControl(char const* name) { mSize = 0; mAsyncLoadCallback = NULL; } +#endif u32 DynamicModuleControl::sAllocBytes; @@ -169,6 +171,7 @@ bool DynamicModuleControl::initialize() { return true; } +#if !TARGET_PC bool DynamicModuleControl::callback(void* moduleControlPtr) { DynamicModuleControl* moduleControl = (DynamicModuleControl*)moduleControlPtr; return moduleControl->do_load(); @@ -484,3 +487,5 @@ extern "C" void ModuleDestructorsX(void (*const *dtors)()) { dtors++; } } + +#endif \ No newline at end of file diff --git a/src/c/c_dylink.cpp b/src/c/c_dylink.cpp index e6417ebbf8..f982c254ee 100644 --- a/src/c/c_dylink.cpp +++ b/src/c/c_dylink.cpp @@ -898,6 +898,9 @@ BOOL cDyl_IsLinked(s16 i_ProfName) { } BOOL cDyl_Unlink(s16 i_ProfName) { +#if TARGET_PC + return FALSE; +#else #if DEBUG cCc_Check(); #endif @@ -911,9 +914,13 @@ BOOL cDyl_Unlink(s16 i_ProfName) { } return FALSE; +#endif } int cDyl_LinkASync(s16 i_ProfName) { +#if TARGET_PC + return cPhs_COMPLEATE_e; +#else #if DEBUG cCc_Check(); #endif @@ -953,6 +960,7 @@ int cDyl_LinkASync(s16 i_ProfName) { } return cPhs_COMPLEATE_e; +#endif } static int cDyl_InitCallback(void* param_0) { diff --git a/src/d/actor/d_a_alink.cpp b/src/d/actor/d_a_alink.cpp index 670dd169c3..e754d7522f 100644 --- a/src/d/actor/d_a_alink.cpp +++ b/src/d/actor/d_a_alink.cpp @@ -850,7 +850,7 @@ daAlink_FaceTexData const daAlink_c::m_faceTexDataTable[] = { {dRes_ID_ALANM_BTP_FDAM01_e, dRes_ID_ALANM_BTK_FFINISHA_e}, {dRes_ID_ALANM_BTP_FFINISHA_e, dRes_ID_ALANM_BTK_FFINISHED_e}, {dRes_ID_ALANM_BTP_FARELORD_e, dRes_ID_ALANM_BTK_FARELORD_e}, - {dRes_ID_ALANM_BTP_FARELORDTAME_e, dRes_ID_ALANM_BTK_FARELORDTAME_e}, + {dRes_ID_ALANM_BTP_FARELORDTAME_e, dRes_ID_ALANM_BTK_FARELORDTAME_e}, {dRes_ID_ALANM_BTP_FPUSHW_e, dRes_ID_ALANM_BTK_FPUSHW_e}, {dRes_ID_ALANM_BTP_FPULLW_e, dRes_ID_ALANM_BTK_FPULLW_e}, {dRes_ID_ALANM_BTP_FWAITST_e, dRes_ID_ALANM_BTK_FWAITST_e}, @@ -1001,7 +1001,7 @@ daAlink_FaceTexData const daAlink_c::m_faceTexDataTable[] = { {dRes_ID_ALANM_BTP_WL_FSWIMDIEA_e, dRes_ID_ALANM_BTK_WL_FA_e}, {dRes_ID_ALANM_BTP_WL_FSWIMDIEP_e, dRes_ID_ALANM_BTK_WL_FA_e}, {dRes_ID_ALANM_BTP_WL_FMDSHOCK_e, dRes_ID_ALANM_BTK_WL_FMDSHOCK_e}, - {dRes_ID_ALANM_BTP_WL_FENTRANCE_e, dRes_ID_ALANM_BTK_WL_FENTRANCE_e}, + {dRes_ID_ALANM_BTP_WL_FENTRANCE_e, dRes_ID_ALANM_BTK_WL_FENTRANCE_e}, {dRes_ID_ALANM_BTP_WL_FHOWLC_e, dRes_ID_ALANM_BTK_WL_FA_e}, {dRes_ID_ALANM_BTP_WL_FC_e, dRes_ID_ALANM_BTK_WL_FA_e}, }; @@ -4943,7 +4943,7 @@ int daAlink_c::create() { if (dComIfG_resLoad(&mShieldPhaseReq, mShieldArcName, mpShieldArcHeap) != cPhs_COMPLEATE_e) { return cPhs_INIT_e; } - + u32 heapSize = 0x3E930; heapSize |= 0x80000000; heapSize |= 0x40000000; @@ -5885,7 +5885,7 @@ void daAlink_c::setItemMatrix(int param_0) { || (mProcID == PROC_CUT_REVERSE && mProcVar2.field_0x300c != 0) || mProcID == PROC_GUARD_BREAK || (mEquipItem == 0x103 && !checkEndResetFlg1(ERFLG1_SHIELD_BACKBONE) && !checkModeFlg(0x400)) - ) + ) { mShieldModel->setBaseTRMtx(mpLinkModel->getAnmMtx(mRightItemJntNo)); @@ -5949,9 +5949,23 @@ void daAlink_c::setItemMatrix(int param_0) { mpLinkBootModels[0]->setAnmMtx(3, mpLinkModel->getAnmMtx(0x15)); mDoMtx_stack_c::XrotS(-0x8000); - mDoMtx_concat(mpLinkModel->getAnmMtx(0x18), mDoMtx_stack_c::get(), mpLinkBootModels[1]->getAnmMtx(1)); - mDoMtx_concat(mpLinkModel->getAnmMtx(0x19), mDoMtx_stack_c::get(), mpLinkBootModels[1]->getAnmMtx(2)); - mDoMtx_concat(mpLinkModel->getAnmMtx(0x1A), mDoMtx_stack_c::get(), mpLinkBootModels[1]->getAnmMtx(3)); +#ifdef TARGET_PC + if (dusk::getSettings().game.enableFrameInterpolation) { + Mtx boot_mtx; + mDoMtx_concat(mpLinkModel->getAnmMtx(0x18), mDoMtx_stack_c::get(), boot_mtx); + mpLinkBootModels[1]->setAnmMtx(1, boot_mtx); + mDoMtx_concat(mpLinkModel->getAnmMtx(0x19), mDoMtx_stack_c::get(), boot_mtx); + mpLinkBootModels[1]->setAnmMtx(2, boot_mtx); + mDoMtx_concat(mpLinkModel->getAnmMtx(0x1A), mDoMtx_stack_c::get(), boot_mtx); + mpLinkBootModels[1]->setAnmMtx(3, boot_mtx); + } else { +#endif + mDoMtx_concat(mpLinkModel->getAnmMtx(0x18), mDoMtx_stack_c::get(), mpLinkBootModels[1]->getAnmMtx(1)); + mDoMtx_concat(mpLinkModel->getAnmMtx(0x19), mDoMtx_stack_c::get(), mpLinkBootModels[1]->getAnmMtx(2)); + mDoMtx_concat(mpLinkModel->getAnmMtx(0x1A), mDoMtx_stack_c::get(), mpLinkBootModels[1]->getAnmMtx(3)); +#ifdef TARGET_PC + } +#endif } if (!checkNoResetFlg2(FLG2_STATUS_WINDOW_DRAW)) { @@ -6279,7 +6293,7 @@ void daAlink_c::setWolfAtCollision() { void daAlink_c::resetAtCollision(BOOL param_0) { if (checkNoResetFlg0(FLG0_CUT_AT_FLG)) { - if (param_0 + if (param_0 && !setSwordHitVibration(&mAtCps[0]) && !setSwordHitVibration(&mAtCps[1]) && !setSwordHitVibration(&mAtCps[2]) @@ -14055,7 +14069,7 @@ void daAlink_c::setMetamorphoseModel(BOOL i_isChangeToWolf) { JKRHeap* heap = setItemHeap(); mHeldItemModel = initModel(loadAramBmd(dRes_ID_ALANM_BMD_AL_WF_e, 0x6000), 0); - + if (!mItemBck.init(bck, FALSE, 2, 1.0f, 0, -1, false)) { JUT_ASSERT(20842, FALSE); } @@ -16727,7 +16741,7 @@ int daAlink_c::procAutoJump() { #if VERSION == VERSION_SHIELD_DEBUG if (!checkStageName("F_SP115") && mGrabItemAcKeep.getActor() != NULL) { if ((fopAcM_GetName(mGrabItemAcKeep.getActor()) == fpcNm_NI_e && ((ni_class*)mGrabItemAcKeep.getActor())->checkGold() != TRUE) || - (fopAcM_GetName(mGrabItemAcKeep.getActor()) == fpcNm_NPC_TKJ2_e)) + (fopAcM_GetName(mGrabItemAcKeep.getActor()) == fpcNm_NPC_TKJ2_e)) { mMaxSpeed = mpHIO->mAutoJump.m.mCuccoJumpMaxSpeed; field_0x3478 = mpHIO->mAutoJump.m.mCuccoFallMaxSpeed; diff --git a/src/d/actor/d_a_alink_dusk.cpp b/src/d/actor/d_a_alink_dusk.cpp index 95218268ac..ae4cb2e96c 100644 --- a/src/d/actor/d_a_alink_dusk.cpp +++ b/src/d/actor/d_a_alink_dusk.cpp @@ -9,11 +9,6 @@ void daAlink_c::handleQuickTransform() { return; } - // Ensure that link is not in a cutscene. - if (checkEventRun()) { - return; - } - // Check to see if Link has the ability to transform. if (!dComIfGs_isEventBit(dSv_event_flag_c::M_077)) { return; @@ -30,8 +25,20 @@ void daAlink_c::handleQuickTransform() { return; } + // Ensure that link is not in a cutscene. + if (checkEventRun()) { + Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); + return; + } + mDoCPd_c::getCpadInfo(PAD_1).mPressedButtonFlags = 0; + // Don't allow quick transform while in the STAR tent. + if (checkStageName("R_SP161")) { + Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); + return; + } + // Ensure that the Z Button is not dimmed if (meterDrawPtr->getButtonZAlpha() != 1.f) { Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); diff --git a/src/d/actor/d_a_e_tk2.cpp b/src/d/actor/d_a_e_tk2.cpp index f0bbc2fe6c..30c0cce9df 100644 --- a/src/d/actor/d_a_e_tk2.cpp +++ b/src/d/actor/d_a_e_tk2.cpp @@ -398,7 +398,7 @@ static int daE_TK2_Execute(e_tk2_class* i_this) { cXyz cStack_94; cXyz cStack_a0; - if (i_this->mExecuteState == 0x00) { + if (i_this->mActionTimer[3] == 0x00) { dBgS_ObjGndChk_Spl ground_check; cStack_94 = actor->current.pos; cStack_94.y += 200.0f; @@ -602,7 +602,7 @@ static int daE_TK2_Create(fopAc_ac_c* actor) { i_this->mSound.init(&actor->current.pos, &actor->eyePos, 0x3, 0x1); i_this->mSound.setEnemyName("E_tk2"); i_this->mAtInfo.mpSound = &i_this->mSound; - i_this->mExecuteState = 0x14; + i_this->mActionTimer[3] = 0x14; daE_TK2_Execute(i_this); } return phase; diff --git a/src/d/actor/d_a_e_vt.cpp b/src/d/actor/d_a_e_vt.cpp index d92992f22b..305e212401 100644 --- a/src/d/actor/d_a_e_vt.cpp +++ b/src/d/actor/d_a_e_vt.cpp @@ -167,6 +167,30 @@ static dCcD_SrcSph cc_vt_magic_src = { } // mSphAttr }; +// !@bug The i<40 loops that index these arrays read one element past the end; on +// GC/Wii the OOB reads land on adjacent rodata instead of a defined value. +#if AVOID_UB +static u8 va_tag_set_size[40] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x00, +}; + +static u8 va_tag_set_num[40] = { + 0x01, 0x0C, 0x16, 0x1F, 0x21, 0x2A, 0x2B, 0x02, 0x04, 0x0D, 0x0F, 0x15, 0x18, + 0x20, 0x2C, 0x03, 0x05, 0x06, 0x0B, 0x10, 0x17, 0x19, 0x1A, 0x22, 0x24, 0x29, + 0x2D, 0x34, 0x49, 0x52, 0x36, 0x3E, 0x54, 0x5C, 0x39, 0x41, 0x4C, 0x56, 0x5E, + 0x00, +}; + +static f32 va_tag_offset[40] = { + 20.0f, 20.0f, 20.0f, 20.0f, 20.0f, 0.0f, 40.0f, 0.0f, 30.0f, 10.0f, 0.0f, 0.0f, 20.0f, + 0.0f, 10.0f, 20.0f, 40.0f, 0.0f, 20.0f, 0.0f, 0.0f, 20.0f, 0.0f, 10.0f, 0.0f, 0.0f, + 20.0f, 10.0f, 20.0f, 30.0f, 0.0f, 10.0f, 20.0f, 0.0f, 0.0f, 10.0f, 20.0f, 30.0f, 0.0f, + 0.0f, +}; +#else static u8 va_tag_set_size[39] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, @@ -184,6 +208,7 @@ static f32 va_tag_offset[39] = { 0.0f, 10.0f, 20.0f, 40.0f, 0.0f, 20.0f, 0.0f, 0.0f, 20.0f, 0.0f, 10.0f, 0.0f, 0.0f, 20.0f, 10.0f, 20.0f, 30.0f, 0.0f, 10.0f, 20.0f, 0.0f, 0.0f, 10.0f, 20.0f, 30.0f, 0.0f, }; +#endif } // namespace diff --git a/src/d/actor/d_a_horse.cpp b/src/d/actor/d_a_horse.cpp index 976e398616..68112eba47 100644 --- a/src/d/actor/d_a_horse.cpp +++ b/src/d/actor/d_a_horse.cpp @@ -20,6 +20,21 @@ #include #include +#if TARGET_PC +#include "dusk/dusk.h" + +namespace { +// FRAME INTERP NOTE: Sim tick control point snapshots for interpolation +constexpr int kHorseReinSimMax = 75; +cXyz s_horseReinSimPrev[kHorseReinSimMax]; +cXyz s_horseReinSimCurr[kHorseReinSimMax]; +int s_horseReinSimNumPrev; +int s_horseReinSimNumCurr; +bool s_horseReinSimPrevValid; +bool s_horseReinSimCurrValid; +} // namespace +#endif + #define ANM_HS_BACK_WALK 6 #define ANM_HS_WALK_START 7 #define ANM_HS_EXCITEMENT 8 @@ -3016,6 +3031,20 @@ void daHorse_c::copyReinPos() { for (i = rein->field_0x8[0] - 1; i >= 0; i--, pos_p++) { *pos_p = rein->field_0x0[0][i]; } +#if TARGET_PC + if (field_0x1204 > 0) { + if (s_horseReinSimCurrValid && s_horseReinSimNumCurr > 0) { + memcpy(s_horseReinSimPrev, s_horseReinSimCurr, s_horseReinSimNumCurr * sizeof(cXyz)); + s_horseReinSimNumPrev = s_horseReinSimNumCurr; + s_horseReinSimPrevValid = true; + } + memcpy(s_horseReinSimCurr, m_reinLine.getPos(0), field_0x1204 * sizeof(cXyz)); + s_horseReinSimNumCurr = field_0x1204; + s_horseReinSimCurrValid = true; + } else { + s_horseReinSimCurrValid = false; + } +#endif } void daHorse_c::setReinPosHandSubstance(int param_0) { @@ -3127,6 +3156,30 @@ void daHorse_c::setReinPosNormalSubstance() { copyReinPos(); } +#if TARGET_PC +void daHorse_c::lerpControlPoints(f32 alpha) { + // FRAME INTERP NOTE: Currently only lerping points for Epona's reins. Need a more global solution. + if (!dusk::getSettings().game.enableFrameInterpolation || !s_horseReinSimPrevValid || !s_horseReinSimCurrValid) { + return; + } + const int nCurr = s_horseReinSimNumCurr; + const int nPrev = s_horseReinSimNumPrev; + if (nCurr <= 0) { + return; + } + int n = nPrev < nCurr ? nPrev : nCurr; + if (n <= 0 || n > kHorseReinSimMax) { + return; + } + cXyz* dst = m_reinLine.getPos(0); + for (int i = 0; i < n; i++) { + const cXyz& p0 = s_horseReinSimPrev[i]; + const cXyz& p1 = s_horseReinSimCurr[i]; + dst[i] = p0 + (p1 - p0) * alpha; + } +} +#endif + void daHorse_c::bgCheck() { if (m_procID != PROC_LARGE_DAMAGE_e) { static cXyz localCenterPos(0.0f, 100.0f, 0.0f); diff --git a/src/d/actor/d_a_midna.cpp b/src/d/actor/d_a_midna.cpp index c63f4e5251..01d472e075 100644 --- a/src/d/actor/d_a_midna.cpp +++ b/src/d/actor/d_a_midna.cpp @@ -14,6 +14,7 @@ #include "d/d_msg_object.h" #include "d/d_s_play.h" #include "d/d_debug_viewer.h" +#include "dusk/frame_interpolation.h" static f32 dummy_lit_3777(int idx, u8 foo) { Vec dummy_vec = {0.0f, 0.0f, 0.0f}; @@ -1052,6 +1053,12 @@ void daMidna_c::setBodyPartMatrix() { mpModel->setAnmMtx(i, mpShadowModel->getAnmMtx(i)); } mpModel->calcWeightEnvelopeMtx(); +#ifdef TARGET_PC + // Frame interpolation: Record weight envelopes for Midna here, as they are otherwise missed causing distortion + for (u16 i = 0; i < mpModel->getModelData()->getWEvlpMtxNum(); i++) { + dusk::frame_interp::record_final_mtx_raw(reinterpret_cast(mpModel->getWeightAnmMtx(i)), mpModel->getWeightAnmMtx(i)); + } +#endif } mDoMtx_stack_c::transS(mpShadowModel->getAnmMtx(JNT_BACKBONE1)[0][3], diff --git a/src/d/actor/d_a_obj_groundwater.cpp b/src/d/actor/d_a_obj_groundwater.cpp index ab6b7af922..2d3d05a36c 100644 --- a/src/d/actor/d_a_obj_groundwater.cpp +++ b/src/d/actor/d_a_obj_groundwater.cpp @@ -300,19 +300,11 @@ int daGrdWater_c::Draw() { J3DTexMtxInfo* mtxInfo = &material->getTexGenBlock()->getTexMtx(0)->getTexMtxInfo(); if (mtxInfo != NULL) { Mtx afStack_50; - - #if TARGET_PC - C_MTXLightPerspective(afStack_50, dComIfGd_getView()->fovy, dComIfGd_getView()->aspect, - 1.0f, 1.0f, dusk::getSettings().game.useWaterProjectionOffset ? -0.01f : 0.0f, 0.0f); - #else C_MTXLightPerspective(afStack_50, dComIfGd_getView()->fovy, dComIfGd_getView()->aspect, 1.0f, 1.0f, -0.01f, 0.0f); - #endif - #if WIDESCREEN_SUPPORT mDoGph_gInf_c::setWideZoomLightProjection(afStack_50); #endif - mtxInfo->setEffectMtx(afStack_50); modelData2->simpleCalcMaterial(0, (MtxP)j3dDefaultMtx); } diff --git a/src/d/actor/d_a_obj_lv3Water.cpp b/src/d/actor/d_a_obj_lv3Water.cpp index 934cb3140d..d0b5490c8d 100644 --- a/src/d/actor/d_a_obj_lv3Water.cpp +++ b/src/d/actor/d_a_obj_lv3Water.cpp @@ -371,15 +371,8 @@ int daLv3Water_c::Draw() { texMtxInfo = &material->getTexGenBlock()->getTexMtx(0)->getTexMtxInfo(); if (texMtxInfo != NULL) { Mtx lightProjMtx; - - #if TARGET_PC - C_MTXLightPerspective(lightProjMtx, dComIfGd_getView()->fovy, - dComIfGd_getView()->aspect, 1.0f, 1.0f, dusk::getSettings().game.useWaterProjectionOffset ? -0.01f : 0.0f, 0.0f); - #else C_MTXLightPerspective(lightProjMtx, dComIfGd_getView()->fovy, dComIfGd_getView()->aspect, 1.0f, 1.0f, -0.01f, 0.0f); - #endif - #if WIDESCREEN_SUPPORT mDoGph_gInf_c::setWideZoomLightProjection(lightProjMtx); #endif diff --git a/src/d/actor/d_a_obj_lv3Water2.cpp b/src/d/actor/d_a_obj_lv3Water2.cpp index 55fc94d605..32995e646b 100644 --- a/src/d/actor/d_a_obj_lv3Water2.cpp +++ b/src/d/actor/d_a_obj_lv3Water2.cpp @@ -197,14 +197,7 @@ int daLv3Water2_c::Draw() { texMtxInfo = &btkMaterial->getTexGenBlock()->getTexMtx(0)->getTexMtxInfo(); if(texMtxInfo) { Mtx lightProjMtx; - - #if TARGET_PC - C_MTXLightPerspective(lightProjMtx, dComIfGd_getView()->fovy, - dComIfGd_getView()->aspect, 1.0f, 1.0f, dusk::getSettings().game.useWaterProjectionOffset ? -0.01f : 0.0f, 0); - #else C_MTXLightPerspective(lightProjMtx, dComIfGd_getView()->fovy, dComIfGd_getView()->aspect, 1.0f, 1.0f, -0.01f, 0); - #endif - #if WIDESCREEN_SUPPORT mDoGph_gInf_c::setWideZoomLightProjection(lightProjMtx); #endif diff --git a/src/d/actor/d_a_obj_lv3WaterB.cpp b/src/d/actor/d_a_obj_lv3WaterB.cpp index 33285d975f..f4ad49685d 100644 --- a/src/d/actor/d_a_obj_lv3WaterB.cpp +++ b/src/d/actor/d_a_obj_lv3WaterB.cpp @@ -27,15 +27,8 @@ static int daObj_Lv3waterB_Draw(obj_lv3WaterB_class* i_this) { J3DTexMtxInfo* tex_mtx_info = &material_p->getTexGenBlock()->getTexMtx(0)->getTexMtxInfo(); if (tex_mtx_info != NULL) { Mtx m; - - #if TARGET_PC - C_MTXLightPerspective(m, dComIfGd_getView()->fovy, dComIfGd_getView()->aspect, 1.0f, 1.0f, - dusk::getSettings().game.useWaterProjectionOffset ? -0.015f : 0.0f, 0.0f); - #else C_MTXLightPerspective(m, dComIfGd_getView()->fovy, dComIfGd_getView()->aspect, 1.0f, 1.0f, -0.015f, 0.0f); - #endif - #if WIDESCREEN_SUPPORT mDoGph_gInf_c::setWideZoomLightProjection(m); #endif diff --git a/src/d/actor/d_a_obj_rstair.cpp b/src/d/actor/d_a_obj_rstair.cpp index 7e44cc3700..609a0cf7b8 100644 --- a/src/d/actor/d_a_obj_rstair.cpp +++ b/src/d/actor/d_a_obj_rstair.cpp @@ -313,15 +313,8 @@ int daObjRotStair_c::Draw() { J3DTexMtxInfo* texMtxInfo = &material->getTexGenBlock()->getTexMtx(0)->getTexMtxInfo(); if (texMtxInfo != NULL) { Mtx lightMtx; - - #if TARGET_PC - C_MTXLightPerspective(lightMtx, dComIfGd_getView()->fovy, - dComIfGd_getView()->aspect, 1.0f, 1.0f, dusk::getSettings().game.useWaterProjectionOffset ? -0.01f : 0.0f, 0); - #else C_MTXLightPerspective(lightMtx, dComIfGd_getView()->fovy, dComIfGd_getView()->aspect, 1.0f, 1.0f, -0.01f, 0); - #endif - #if WIDESCREEN_SUPPORT mDoGph_gInf_c::setWideZoomLightProjection(lightMtx); #endif diff --git a/src/d/actor/d_a_obj_tombo.cpp b/src/d/actor/d_a_obj_tombo.cpp index 48db14ac92..6fcbc938d9 100644 --- a/src/d/actor/d_a_obj_tombo.cpp +++ b/src/d/actor/d_a_obj_tombo.cpp @@ -411,7 +411,7 @@ void daObjTOMBO_c::BoomChk() { speedF = 5.0f; field_0x71c = 5.0f; mIsHitByBoomerang = false; - field_0x718 = 100; + field_0x714[2] = 100; mpMorf->setAnm((J3DAnmTransform*)dComIfG_getObjectRes("Tombo", 6), 2, 5.0f, 1.0f, 0.0f, -1.0f); home.pos = current.pos; @@ -422,7 +422,7 @@ void daObjTOMBO_c::BoomChk() { mIsHitByBoomerang = false; speedF = 5.0f; field_0x71c = 5.0f; - field_0x718 = 100; + field_0x714[2] = 100; mpMorf->setAnm((J3DAnmTransform*)dComIfG_getObjectRes("Tombo", 6), 2, 5.0f, 1.0f, 0.0f, -1.0f); current.pos.y = old.pos.y = playerPos.y + 100.0f; diff --git a/src/d/actor/d_a_obj_tp.cpp b/src/d/actor/d_a_obj_tp.cpp index e2ede47b15..2690de3e04 100644 --- a/src/d/actor/d_a_obj_tp.cpp +++ b/src/d/actor/d_a_obj_tp.cpp @@ -36,15 +36,8 @@ static int daObj_Tp_Draw(obj_tp_class* i_this) { &material->getTexGenBlock()->getTexMtx(0)->getTexMtxInfo(); if (texMtxInfo != NULL) { Mtx lightProjMtx; - - #if TARGET_PC - C_MTXLightPerspective(lightProjMtx, dComIfGd_getView()->fovy, - dComIfGd_getView()->aspect, 1.0f, 1.0f, dusk::getSettings().game.useWaterProjectionOffset ? -0.01f : 0.0f, 0); - #else C_MTXLightPerspective(lightProjMtx, dComIfGd_getView()->fovy, dComIfGd_getView()->aspect, 1.0f, 1.0f, -0.01f, 0); - #endif - #if WIDESCREEN_SUPPORT mDoGph_gInf_c::setWideZoomLightProjection(lightProjMtx); #endif diff --git a/src/d/actor/d_a_title.cpp b/src/d/actor/d_a_title.cpp index 88ce62ec5c..fc727a1248 100644 --- a/src/d/actor/d_a_title.cpp +++ b/src/d/actor/d_a_title.cpp @@ -122,7 +122,7 @@ static procFunc daTitleProc[6] = { int daTitle_c::create() { fopAcM_ct(this, daTitle_c); - + int phase_state = dComIfG_resLoad(&mPhaseReq, l_arcName); if (phase_state != cPhs_COMPLEATE_e) { return phase_state; @@ -152,15 +152,21 @@ int daTitle_c::createHeapCallBack(fopAc_ac_c* actor) { } int daTitle_c::Execute() { - #if PLATFORM_WII || PLATFORM_SHIELD +#if PLATFORM_WII || PLATFORM_SHIELD mDoGph_gInf_c::resetDimming(); - #endif +#endif if (fopOvlpM_IsPeek()) { return 1; } - dMenu_Collect3D_c::setViewPortOffsetY(0.0f); +#ifdef TARGET_PC + if (!dusk::getSettings().game.enableFrameInterpolation) { +#endif + dMenu_Collect3D_c::setViewPortOffsetY(0.0f); +#ifdef TARGET_PC + } +#endif if (mDoRst::isReset()) { return 1; @@ -169,9 +175,9 @@ int daTitle_c::Execute() { (this->*daTitleProc[mProcID])(); KeyWaitAnm(); - #if VERSION == VERSION_SHIELD_DEBUG +#if VERSION == VERSION_SHIELD_DEBUG KeyWaitPosMove(); - #endif +#endif return 1; } @@ -390,7 +396,7 @@ int daTitle_c::Delete() { dComIfG_resDelete(&mPhaseReq, l_arcName); JKR_DELETE(mTitle.Scr); JKR_DELETE(field_0x600); - + mpMount->getArchive()->removeResourceAll(); JKRUnmountArchive(mpMount->getArchive()); mpMount->destroy(); diff --git a/src/d/actor/d_flower.inc b/src/d/actor/d_flower.inc index 4fb53ffaa7..034d35961e 100644 --- a/src/d/actor/d_flower.inc +++ b/src/d/actor/d_flower.inc @@ -5,6 +5,8 @@ #include "JSystem/J3DGraphBase/J3DDrawBuffer.h" #include "SSystem/SComponent/c_counter.h" +#include "dusk/frame_interpolation.h" + #if TARGET_PC const u16 l_J_Ohana00_64TEX__width = 64; const u16 l_J_Ohana00_64TEX__height = 64; @@ -695,7 +697,16 @@ void dFlower_packet_c::draw() { GXSetChanAmbColor(GX_COLOR0A0, sp64); if (!cLib_checkBit(sp44->m_state, 4) && !cLib_checkBit(sp44->m_state, 0x40)) { - GXLoadPosMtxImm(sp44->m_modelMtx, 0); +#ifdef TARGET_PC + Mtx flower_mtx; + if (dusk::frame_interp::lookup_replacement(reinterpret_cast(&sp44->m_modelMtx), flower_mtx)) { + GXLoadPosMtxImm(flower_mtx, 0); + } else { +#endif + GXLoadPosMtxImm(sp44->m_modelMtx, 0); +#ifdef TARGET_PC + } +#endif GXLoadNrmMtxImm(j3dSys.getViewMtx(), 0); #if TARGET_PC @@ -841,7 +852,16 @@ void dFlower_packet_c::draw() { sp30++; if (!cLib_checkBit(sp34->m_state, 4) && cLib_checkBit(sp34->m_state, 0x40)) { - GXLoadPosMtxImm(sp34->m_modelMtx, 0); +#ifdef TARGET_PC + Mtx flower_mtx; + if (dusk::frame_interp::lookup_replacement(reinterpret_cast(&sp34->m_modelMtx), flower_mtx)) { + GXLoadPosMtxImm(flower_mtx, 0); + } else { +#endif + GXLoadPosMtxImm(sp34->m_modelMtx, 0); +#ifdef TARGET_PC + } +#endif GXLoadNrmMtxImm(j3dSys.getViewMtx(), 0); #if TARGET_PC @@ -973,6 +993,9 @@ void dFlower_packet_c::update() { mDoMtx_stack_c::copy(temp_r28); mDoMtx_stack_c::scaleM(temp_f31, temp_f31, temp_f31); cMtx_concat(j3dSys.getViewMtx(), temp_r28, data_p->m_modelMtx); +#ifdef TARGET_PC + dusk::frame_interp::record_final_mtx_raw(reinterpret_cast(&data_p->m_modelMtx), data_p->m_modelMtx); +#endif } } diff --git a/src/d/actor/d_grass.inc b/src/d/actor/d_grass.inc index 3645488b0f..0f651a3203 100644 --- a/src/d/actor/d_grass.inc +++ b/src/d/actor/d_grass.inc @@ -11,6 +11,8 @@ #include "d/d_camera.h" #include "f_op/f_op_camera_mng.h" +#include "dusk/frame_interpolation.h" + const u16 l_M_Hijiki00TEX__width = 31; const u16 l_M_Hijiki00TEX__height = 31; const u16 l_M_kusa05_RGBATEX__width = 31; @@ -751,7 +753,16 @@ void dGrass_packet_c::draw() { GXSetChanAmbColor(GX_COLOR0A0, sp38); if (!cLib_checkBit(var_r29->field_0x01, 2)) { - GXLoadPosMtxImm(var_r29->m_modelMtx, 0); +#ifdef TARGET_PC + Mtx grass_mtx; + if (dusk::frame_interp::lookup_replacement(reinterpret_cast(&var_r29->m_modelMtx), grass_mtx)) { + GXLoadPosMtxImm(grass_mtx, 0); + } else { +#endif + GXLoadPosMtxImm(var_r29->m_modelMtx, 0); +#ifdef TARGET_PC + } +#endif GXLoadNrmMtxImm(j3dSys.getViewMtx(), 0); if (var_r29->field_0x05 <= 3 || var_r29->field_0x05 >= 10) { if (var_r29->field_0x02 < -1) { @@ -1006,6 +1017,9 @@ void dGrass_packet_c::update() { mDoMtx_stack_c::scaleM(scale, scale, scale); cMtx_concat(j3dSys.getViewMtx(), mDoMtx_stack_c::get(), data_p->m_modelMtx); } +#ifdef TARGET_PC + dusk::frame_interp::record_final_mtx_raw(reinterpret_cast(&data_p->m_modelMtx), data_p->m_modelMtx); +#endif } } data_p++; diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index 70e8c105bb..0047978e7e 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -20,6 +20,7 @@ #include "m_Do/m_Do_controller_pad.h" #include "m_Do/m_Do_graphic.h" #include "m_Do/m_Do_lib.h" +#include "dusk/frame_interpolation.h" #include #include @@ -2179,9 +2180,9 @@ fopAc_ac_c* dCamera_c::getParamTargetActor(s32 param_0) { daAlink_c* player = daAlink_getAlinkActorClass(); fopAc_ac_c* result; - u32* name = (u32*)(mCamTypeData[param_0].name + 16); + BE(u32)* name = (BE(u32)*)(mCamTypeData[param_0].name + 16); //name += 16; - switch (*name) { + switch ((u32)*name) { case '@LOC': result = dComIfGp_getAttention()->LockonTarget(0); break; @@ -11030,16 +11031,20 @@ static int camera_draw(camera_process_class* i_this) { int trim_height = body->TrimHeight(); - #if TARGET_PC +#if TARGET_PC trim_height *= viewport->height / FB_HEIGHT; window->setScissor(0.0f, trim_height, viewport->width, viewport->height - trim_height * 2.0f); - #else +#else window->setScissor(0.0f, trim_height, FB_WIDTH, FB_HEIGHT - trim_height * 2.0f); - #endif +#endif C_MTXPerspective(process->view.projMtx, process->view.fovy, process->view.aspect, process->view.near_, process->view.far_); mDoMtx_lookAt(process->view.viewMtx, &process->view.lookat.eye, &process->view.lookat.center, &process->view.lookat.up, process->view.bank); +#ifdef TARGET_PC + dusk::frame_interp::record_final_mtx_raw(reinterpret_cast(process->view.viewMtx), + process->view.viewMtx); +#endif #if WIDESCREEN_SUPPORT mDoGph_gInf_c::setWideZoomProjection(process->view.projMtx); diff --git a/src/d/d_com_inf_game.cpp b/src/d/d_com_inf_game.cpp index 6b7f7d6ae3..aa5b5659e9 100644 --- a/src/d/d_com_inf_game.cpp +++ b/src/d/d_com_inf_game.cpp @@ -1018,7 +1018,7 @@ bool dComIfG_inf_c::baseCsr_c::navi_c::draw(f32 param_1, f32 param_2, u8 param_3 f32 f27 = f31 - field_0x5c; field_0x58 = f29; field_0x5c = f31; - cXyz spdc(param_1 - 304.0f, param_2 - 224.0f, 0.0f); + cXyz spdc(param_1 - FB_WIDTH_BASE / 2, param_2 - FB_HEIGHT_BASE / 2, 0.0f); f32 target = param_3 != 0 ? 1.5f : 0.0f; diff --git a/src/d/d_drawlist.cpp b/src/d/d_drawlist.cpp index 8b7457637d..9f2255b884 100644 --- a/src/d/d_drawlist.cpp +++ b/src/d/d_drawlist.cpp @@ -10,7 +10,13 @@ #include "SSystem/SComponent/c_math.h" #include "d/d_com_inf_game.h" #include "d/d_drawlist.h" + +#include + +#include "absl/container/flat_hash_map.h" +#include "client/TracyScoped.hpp" #include "d/d_s_play.h" +#include "dusk/frame_interpolation.h" #include "dusk/gx_helper.h" #include "dusk/logging.h" #include "m_Do/m_Do_graphic.h" @@ -1089,7 +1095,16 @@ void dDlst_shadowReal_c::draw() { GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); GXSetCurrentMtx(GX_PNMTX0); - GXLoadTexMtxImm(mReceiverProjMtx, GX_TEXMTX0, GX_MTX3x4); +#ifdef TARGET_PC + Mtx receiver_proj_mtx; + if (dusk::frame_interp::lookup_replacement(&mReceiverProjMtx, receiver_proj_mtx)) { + GXLoadTexMtxImm(receiver_proj_mtx, GX_TEXMTX0, GX_MTX3x4); + } else { +#endif + GXLoadTexMtxImm(mReceiverProjMtx, GX_TEXMTX0, GX_MTX3x4); +#ifdef TARGET_PC + } +#endif mShadowRealPoly.draw(); } @@ -1247,6 +1262,10 @@ u8 dDlst_shadowReal_c::setShadowRealMtx(cXyz* param_0, cXyz* param_1, f32 param_ C_MTXOrtho(mRenderProjMtx, param_2, -param_2, -param_2, param_2, 1.0f, 10000.0f); C_MTXLightOrtho(mReceiverProjMtx, param_2, -param_2, -param_2, param_2, 0.5f, -0.5f, 0.5f, 0.5f); cMtx_concat(mReceiverProjMtx, mViewMtx, mReceiverProjMtx); +#ifdef TARGET_PC + dusk::frame_interp::record_final_mtx_raw(&mViewMtx, mViewMtx); + dusk::frame_interp::record_final_mtx_raw(&mReceiverProjMtx, mReceiverProjMtx); +#endif return r29; } @@ -1309,13 +1328,31 @@ void dDlst_shadowSimple_c::draw() { GXSetTevColor(GX_TEVREG0, l_color); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_INDEX8); - GXLoadPosMtxImm(mVolumeMtx, GX_PNMTX0); +#ifdef TARGET_PC + Mtx volume_mtx; + if (dusk::frame_interp::lookup_replacement(&mVolumeMtx, volume_mtx)) { + GXLoadPosMtxImm(volume_mtx, GX_PNMTX0); + } else { +#endif + GXLoadPosMtxImm(mVolumeMtx, GX_PNMTX0); +#ifdef TARGET_PC + } +#endif GXSetCurrentMtx(GX_PNMTX0); GXCallDisplayList(l_frontMat, 0x40); GXCallDisplayList(l_shadowVolumeDL, 0x40); GXCallDisplayList(l_backSubMat, 0x20); GXCallDisplayList(l_shadowVolumeDL, 0x40); - GXLoadPosMtxImm(mMtx, GX_PNMTX1); +#ifdef TARGET_PC + Mtx shadow_mtx; + if (dusk::frame_interp::lookup_replacement(&mMtx, shadow_mtx)) { + GXLoadPosMtxImm(shadow_mtx, GX_PNMTX1); + } else { +#endif + GXLoadPosMtxImm(mMtx, GX_PNMTX1); +#ifdef TARGET_PC + } +#endif GXSetCurrentMtx(GX_PNMTX1); if (mpTexObj != NULL) { @@ -1394,13 +1431,21 @@ void dDlst_shadowSimple_c::set(cXyz* param_0, f32 param_1, f32 param_2, cXyz* pa mDoMtx_stack_c::YrotM(param_4); mDoMtx_stack_c::scaleM(param_2, 1.0f, param_2 * param_5); cMtx_concat(j3dSys.getViewMtx(), mDoMtx_stack_c::get(), mMtx); +#ifdef TARGET_PC + dusk::frame_interp::record_final_mtx_raw(&mVolumeMtx, mVolumeMtx); + dusk::frame_interp::record_final_mtx_raw(&mMtx, mMtx); +#endif mpTexObj = param_6; } void dDlst_shadowControl_c::init() { #if TARGET_PC // Increase shadow map resolution - static u16 l_realImageSize[2] = {1024, 512}; + u16 l_realImageSize[2] = + { + 192 * dusk::getSettings().game.shadowResolutionMultiplier, + 64 * dusk::getSettings().game.shadowResolutionMultiplier + }; #else static u16 l_realImageSize[2] = {192, 64}; #endif @@ -1439,7 +1484,19 @@ void dDlst_shadowControl_c::reset() { #endif } +#if TARGET_PC +int lastShadowValue = 0; +#endif + void dDlst_shadowControl_c::imageDraw(Mtx param_0) { + #if TARGET_PC + if (lastShadowValue != dusk::getSettings().game.shadowResolutionMultiplier) { + reset(); + init(); + lastShadowValue = dusk::getSettings().game.shadowResolutionMultiplier; + } + #endif + static u8 l_matDL[] ATTRIBUTE_ALIGN(32) = { 0x10, 0x00, 0x00, 0x10, 0x0E, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x04, 0x00, 0x61, 0x28, 0x38, 0x00, 0x00, 0x61, 0xC0, 0x08, 0xFF, 0xF2, @@ -1541,7 +1598,16 @@ void dDlst_shadowControl_c::draw(Mtx param_0) { GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX3x4, GX_TG_POS, GX_TEXMTX0); GXSetNumTevStages(1); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); - GXLoadPosMtxImm(param_0, GX_PNMTX0); +#ifdef TARGET_PC + Mtx draw_mtx; + if (dusk::frame_interp::lookup_replacement(param_0, draw_mtx)) { + GXLoadPosMtxImm(draw_mtx, GX_PNMTX0); + } else { +#endif + GXLoadPosMtxImm(param_0, GX_PNMTX0); +#ifdef TARGET_PC + } +#endif GXColor matColor = {0, 0, 0, 0x20}; GXSetChanMatColor(GX_ALPHA0, matColor); @@ -1878,14 +1944,32 @@ int dDlst_list_c::set(dDlst_base_c**& p_start, dDlst_base_c**& p_end, dDlst_base return 1; } +#if TARGET_PC && (TRACY_ENABLE || PARTIAL_DEBUG) +static absl::flat_hash_map typeDrawNames; + +static const char* getTypeDrawName(dDlst_base_c* dlst) { + const auto& info = typeid(*dlst); + auto& elem = typeDrawNames[info]; + if (elem) [[likely]] { + return elem; + } + + const auto size = snprintf(nullptr, 0, "%s::draw()", info.name()); + // Note: pointer is intentionally never freed, Tracy needs it. + const auto buf = static_cast(malloc(size+1)); + snprintf(buf, size+1, "%s::draw()", info.name()); + elem = buf; + return buf; +} +#endif + void dDlst_list_c::draw(dDlst_base_c** p_start, dDlst_base_c** p_end) { for (; p_start < p_end; p_start++) { dDlst_base_c* dlst = *p_start; -#if DEBUG && TARGET_PC - char buf[64]; - snprintf(buf, sizeof(buf), "%s::draw()", typeid(dlst).name()); - GXScopedDebugGroup scope(buf); +#if TARGET_PC && (TRACY_ENABLE || PARTIAL_DEBUG) + const auto name = getTypeDrawName(dlst); + GXScopedDebugGroup scope(name); #endif dlst->draw(); } @@ -1933,3 +2017,13 @@ void dDlst_list_c::calcWipe() { dComIfGd_set2DXlu(&mWipeDlst); } } + +#if TARGET_PC +void dDlst_list_c::refresh3DlineMats(const cXyz& eye) { + for (int i = 0; i < 3; i++) { + for (mDoExt_3DlineMat_c* mat = m3DLineMatSortPacket[i].getFirstMat(); mat != NULL; mat = mat->field_0x4) { + mat->refreshGeometryForPresentationEye(eye); + } + } +} +#endif diff --git a/src/d/d_file_select.cpp b/src/d/d_file_select.cpp index 8d4f98187b..be40b13d1c 100644 --- a/src/d/d_file_select.cpp +++ b/src/d/d_file_select.cpp @@ -5585,7 +5585,7 @@ void dFile_select3D_c::toItem3Dpos(f32 param_0, f32 param_1, f32 param_2, cXyz* Mtx adStack_98; Mtx auStack_c8; param_0 = (2.0f * ((param_0 - mDoGph_gInf_c::getMinXF()) / mDoGph_gInf_c::getWidthF()) - 1.0f); - param_1 = (2.0f * ((param_1 - -100.0f) / 448.0f) - 1.0f); + param_1 = (2.0f * ((param_1 - -100.0f) / FB_HEIGHT_BASE) - 1.0f); calcViewMtx(adStack_98); cMtx_inverse(adStack_98, auStack_c8); f32 tangent = std::tan(M_PI / 8.0f); diff --git a/src/d/d_jcam_editor.cpp b/src/d/d_jcam_editor.cpp index 2d0d315e73..8d9ba72455 100644 --- a/src/d/d_jcam_editor.cpp +++ b/src/d/d_jcam_editor.cpp @@ -1,11 +1,12 @@ #include "d/d_jcam_editor.h" +#include "m_Do/m_Do_graphic.h" #include "m_Do/m_Do_hostIO.h" #include "m_Do/m_Do_ext.h" dJcame_c* dJcame_c::m_myObj; dJcame_c::dJcame_c(const JStage::TSystem* i_system, f32 param_1, JUTGamePad& i_pad) { - mOrthoGraph = JKR_NEW J2DOrthoGraph(0.0f, 0.0f, 608.0f, 448.0f, -1.0f, 1.0f); + mOrthoGraph = JKR_NEW J2DOrthoGraph(0.0f, 0.0f, FB_WIDTH_BASE, FB_HEIGHT_BASE, -1.0f, 1.0f); mFont = JKR_NEW JUTResFont((ResFONT*)JUTResFONT_Ascfont_fix12, NULL); mHeap = JKRExpHeap::create(0x100000, JKRHeap::getRootHeap2(), false); diff --git a/src/d/d_kankyo.cpp b/src/d/d_kankyo.cpp index 8644d433aa..0bd3db8e2a 100644 --- a/src/d/d_kankyo.cpp +++ b/src/d/d_kankyo.cpp @@ -10994,9 +10994,9 @@ void dKy_depth_dist_set(void* process_p) { if ((sp30.x >= 0.0f && sp30.x < FB_WIDTH) && (sp30.y >= 0.0f && #if DEBUG - sp30.y < 608.0f + sp30.y < FB_WIDTH_BASE #else - sp30.y < 600.0f + sp30.y < (FB_WIDTH_BASE - 8) #endif )) { cXyz sp18; @@ -11393,11 +11393,7 @@ void dKy_bg_MAxx_proc(void* bg_model_p) { if (mat_name[6] == '2') { C_MTXLightPerspective(sp1D8, dComIfGd_getView()->fovy, camera_p->view.aspect, 1.0f, 1.0f, -#if TARGET_PC - dusk::getSettings().game.useWaterProjectionOffset ? -0.01f : 0.0f, 0.0f); -#else -0.01f, 0.0f); -#endif } else { C_MTXLightPerspective(sp1D8, dComIfGd_getView()->fovy, camera_p->view.aspect, 0.49f, -0.49f, 0.5f, 0.5f); diff --git a/src/d/d_menu_collect.cpp b/src/d/d_menu_collect.cpp index 72a390a0cc..c7e5bb7414 100644 --- a/src/d/d_menu_collect.cpp +++ b/src/d/d_menu_collect.cpp @@ -2708,7 +2708,7 @@ void dMenu_Collect3D_c::toItem3Dpos(f32 param_0, f32 param_1, f32 param_2, cXyz* Mtx auStack_c8; param_0 = (2.0f * ((param_0 - mDoGph_gInf_c::getMinXF()) / mDoGph_gInf_c::getWidthF()) - 1.0f); - param_1 = (2.0f * ((param_1 - -100.0f) / 448.0f) - 1.0f); + param_1 = (2.0f * ((param_1 - -100.0f) / FB_HEIGHT_BASE) - 1.0f); calcViewMtx(adStack_98); MTXInverse(adStack_98, auStack_c8); f32 tangent = tan(0.39269909262657166); diff --git a/src/d/d_menu_fmap.cpp b/src/d/d_menu_fmap.cpp index 851e22b7c7..149e03349d 100644 --- a/src/d/d_menu_fmap.cpp +++ b/src/d/d_menu_fmap.cpp @@ -229,17 +229,17 @@ dMenu_Fmap_c::dMenu_Fmap_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i } if (i_panDirection == 1) { - mTransX = -608.0f; + mTransX = -FB_WIDTH_BASE; mTransY = 0.0f; } else if (i_panDirection == 3) { - mTransX = 608.0f; + mTransX = FB_WIDTH_BASE; mTransY = 0.0f; } else if (i_panDirection == 2) { mTransX = 0.0f; - mTransY = -448.0f; + mTransY = -FB_HEIGHT_BASE; } else if (i_panDirection == 0) { mTransX = 0.0f; - mTransY = 448.0f; + mTransY = FB_HEIGHT_BASE; } else { mTransX = 0.0f; mTransY = 0.0f; @@ -1133,6 +1133,12 @@ void dMenu_Fmap_c::zoom_region_to_spot_proc() { void dMenu_Fmap_c::zoom_spot_to_region_init() { mZoomLevel = 10; field_0x1ec = 1.0f; +#if TARGET_PC + // Frame interp note: field_0x122d used to be set every draw, causing flickering. Do it here instead. + if (dusk::getSettings().game.enableFrameInterpolation) { + mpDraw2DBack->resetScrollArrowMask(); + } +#endif Z2GetAudioMgr()->seStart(Z2SE_SY_MAP_ZOOMOUT, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); } @@ -1705,17 +1711,17 @@ bool dMenu_Fmap_c::isOpen() { f32 ratio = (f32)mDisplayFrame / (f32)display_frame_num; if (mPanDirection == 1) { - mTransX = (1.0f - ratio) * -608.0f; + mTransX = (1.0f - ratio) * -FB_WIDTH_BASE; mTransY = 0.0f; } else if (mPanDirection == 3) { - mTransX = (1.0f - ratio) * 608.0f; + mTransX = (1.0f - ratio) * FB_WIDTH_BASE; mTransY = 0.0f; } else if (mPanDirection == 2) { mTransX = 0.0f; - mTransY = (1.0f - ratio) * -448.0f; + mTransY = (1.0f - ratio) * -FB_HEIGHT_BASE; } else if (mPanDirection == 0) { mTransX = 0.0f; - mTransY = (1.0f - ratio) * 448.0f; + mTransY = (1.0f - ratio) * FB_HEIGHT_BASE; } mAlphaRatio = ratio; @@ -1755,17 +1761,17 @@ bool dMenu_Fmap_c::isClose() { } if (mPanDirection == 1) { - mTransX = (1.0f - ratio) * 608.0f; + mTransX = (1.0f - ratio) * FB_WIDTH_BASE; mTransY = 0.0f; } else if (mPanDirection == 3) { - mTransX = (1.0f - ratio) * -608.0f; + mTransX = (1.0f - ratio) * -FB_WIDTH_BASE; mTransY = 0.0f; } else if (mPanDirection == 2) { mTransX = 0.0f; - mTransY = (1.0f - ratio) * 448.0f; + mTransY = (1.0f - ratio) * FB_HEIGHT_BASE; } else if (mPanDirection == 0) { mTransX = 0.0f; - mTransY = (1.0f - ratio) * -448.0f; + mTransY = (1.0f - ratio) * -FB_HEIGHT_BASE; } mAlphaRatio = ratio; diff --git a/src/d/d_menu_fmap2D.cpp b/src/d/d_menu_fmap2D.cpp index c9ab812400..558ad05d6d 100644 --- a/src/d/d_menu_fmap2D.cpp +++ b/src/d/d_menu_fmap2D.cpp @@ -17,6 +17,7 @@ #include "d/d_msg_scrn_explain.h" #include "m_Do/m_Do_graphic.h" #include "d/actor/d_a_midna.h" +#include "dusk/frame_interpolation.h" #include dMenu_Fmap2DBack_c::dMenu_Fmap2DBack_c() { @@ -390,11 +391,17 @@ void dMenu_Fmap2DBack_c::draw() { (mArrowPos3DZ + control_ypos + fVar3) - fVar5, &mArrowPos2DX, &mArrowPos2DY); - field_0x11e0 -= g_fmapHIO.mCursorSpeed; +#ifdef TARGET_PC + for (u32 i = 0; i < dusk::frame_interp::get_presentation_ui_advance_ticks(); ++i) { +#endif + field_0x11e0 -= g_fmapHIO.mCursorSpeed; - if (field_0x11e0 < 0.0f) { - field_0x11e0 += 360.0f; + if (field_0x11e0 < 0.0f) { + field_0x11e0 += 360.0f; + } +#ifdef TARGET_PC } +#endif mpPointParent->getPanePtr()->rotate(mpPointParent->getSizeX() / 2.0f, mpPointParent->getSizeY() / 2.0f, ROTATE_Z, @@ -425,7 +432,13 @@ void dMenu_Fmap2DBack_c::draw() { if (field_0x122d) { mpMeterHaihai->drawHaihai(field_0x122d); +#if TARGET_PC + if (!dusk::getSettings().game.enableFrameInterpolation) { + field_0x122d = 0; + } +#else field_0x122d = 0; +#endif } if (g_fmapHIO.mRangeCheck && !g_fmapHIO.mRangeCheckDrawPriority) { diff --git a/src/d/d_menu_insect.cpp b/src/d/d_menu_insect.cpp index 7558fe61b4..88e05226e8 100644 --- a/src/d/d_menu_insect.cpp +++ b/src/d/d_menu_insect.cpp @@ -183,6 +183,9 @@ void dMenu_Insect_c::_draw() { mpExpScreen->draw(0.0f, 0.0f, grafPort); mpSelect_c->setOffsetX(g_drawHIO.mInsectListScreen.mConfirmOptionPosX_4x3); + // the magic numbers here are correlated with the framebuffer size, but + // were likely either chosen by hand or had multiple arithmetic + // operations applied which cannot easily be reverse engineered mpSelect_c->translate(g_drawHIO.mInsectListScreen.mConfirmOptionPosX_4x3 + 486.0f, g_drawHIO.mInsectListScreen.mConfirmOptionPosY_4x3 + 209.0f); mpSelect_c->draw(0.0f, 0.0f); diff --git a/src/d/d_menu_item_explain.cpp b/src/d/d_menu_item_explain.cpp index c106b4bd8c..dd3af54571 100644 --- a/src/d/d_menu_item_explain.cpp +++ b/src/d/d_menu_item_explain.cpp @@ -330,6 +330,9 @@ void dMenu_ItemExplain_c::draw(J2DOrthoGraph* i_graph) { mpInfoString->drawOutFontLocal((J2DTextBox*)mpInfoText->getPanePtr(), -1.0f); drawKantera(); if (mpSelect_c != NULL) { + // the magic numbers here are correlated with the framebuffer size, but + // were likely either chosen by hand or had multiple arithmetic + // operations applied which cannot easily be reverse engineered mpSelect_c->translate(486.0f, 209.0f); mpSelect_c->draw(0.0f, 0.0f); } diff --git a/src/d/d_menu_ring.cpp b/src/d/d_menu_ring.cpp index 3949adffed..8e5b3cacd2 100644 --- a/src/d/d_menu_ring.cpp +++ b/src/d/d_menu_ring.cpp @@ -87,16 +87,16 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i mPikariFlashingSpeed = 0.0f; if (mRingOrigin == 0) { mCenterPosX = 0.0f; - mCenterPosY = 448.0f; + mCenterPosY = FB_HEIGHT_BASE; } else if (mRingOrigin == 2) { mCenterPosX = 0.0f; - mCenterPosY = -448.0f; + mCenterPosY = -FB_HEIGHT_BASE; } if (mRingOrigin == 3) { - mCenterPosX = 608.0f; + mCenterPosX = FB_WIDTH_BASE; mCenterPosY = 0.0f; } else if (mRingOrigin == 1) { - mCenterPosX = -608.0f; + mCenterPosX = -FB_WIDTH_BASE; mCenterPosY = 0.0f; } else { mCenterPosX = 0.0f; @@ -644,15 +644,15 @@ bool dMenu_Ring_c::isOpen() { mAlphaRate = (f32)mOpenCloseFrames / (f32)g_ringHIO.mOpenFrames; if (mRingOrigin == 0) { mCenterPosX = 0.0f; - mCenterPosY = (1.0f - mAlphaRate) * 448.0f; + mCenterPosY = (1.0f - mAlphaRate) * FB_HEIGHT_BASE; } else if (mRingOrigin == 2) { mCenterPosX = 0.0f; - mCenterPosY = (1.0f - mAlphaRate) * -448.0f; + mCenterPosY = (1.0f - mAlphaRate) * -FB_HEIGHT_BASE; } else if (mRingOrigin == 3) { - mCenterPosX = (1.0f - mAlphaRate) * 608.0f; + mCenterPosX = (1.0f - mAlphaRate) * FB_WIDTH_BASE; mCenterPosY = 0.0f; } else if (mRingOrigin == 1) { - mCenterPosX = (1.0f - mAlphaRate) * -608.0f; + mCenterPosX = (1.0f - mAlphaRate) * -FB_WIDTH_BASE; mCenterPosY = 0.0f; } if (mOpenCloseFrames >= g_ringHIO.mOpenFrames) { @@ -715,15 +715,15 @@ bool dMenu_Ring_c::isClose() { } if (mRingOrigin == 0) { mCenterPosX = 0.0f; - mCenterPosY = (1.0f - mAlphaRate) * -448.0f; + mCenterPosY = (1.0f - mAlphaRate) * -FB_HEIGHT_BASE; } else if (mRingOrigin == 2) { mCenterPosX = 0.0f; - mCenterPosY = (1.0f - mAlphaRate) * 448.0f; + mCenterPosY = (1.0f - mAlphaRate) * FB_HEIGHT_BASE; } else if (mRingOrigin == 3) { - mCenterPosX = (1.0f - mAlphaRate) * -608.0f; + mCenterPosX = (1.0f - mAlphaRate) * -FB_WIDTH_BASE; mCenterPosY = 0.0f; } else if (mRingOrigin == 1) { - mCenterPosX = (1.0f - mAlphaRate) * 608.0f; + mCenterPosX = (1.0f - mAlphaRate) * FB_WIDTH_BASE; mCenterPosY = 0.0f; } mpDrawCursor->setPos(mItemSlotPosX[mCurrentSlot] + mCenterPosX, @@ -855,11 +855,11 @@ s16 dMenu_Ring_c::calcStickAngle(STControl* i_stick, u8 param_1) { } void dMenu_Ring_c::setRotate() { - clacEllipsePlotAverage(mItemsTotal, g_ringHIO.mItemRingPosX + 304.0f, - g_ringHIO.mItemRingPosY + 224.0f); + clacEllipsePlotAverage(mItemsTotal, g_ringHIO.mItemRingPosX + FB_WIDTH_BASE / 2, + g_ringHIO.mItemRingPosY + FB_HEIGHT_BASE / 2); for (int i = 0; i < mItemsTotal; i++) { - field_0x63e[i] = cM_atan2s(mItemSlotPosX[i] - (304.0f + g_ringHIO.mItemRingPosX), - mItemSlotPosY[i] - (224.0f + g_ringHIO.mItemRingPosY)); + field_0x63e[i] = cM_atan2s(mItemSlotPosX[i] - (FB_WIDTH_BASE / 2 + g_ringHIO.mItemRingPosX), + mItemSlotPosY[i] - (FB_HEIGHT_BASE / 2 + g_ringHIO.mItemRingPosY)); } } @@ -1446,9 +1446,9 @@ void dMenu_Ring_c::stick_move_proc() { setStatus(field_0x6b2); } else { f32 itemRingPosX = - g_ringHIO.mItemRingPosX + 304.0f + mRingRadiusH * cM_ssin(field_0x66e); + g_ringHIO.mItemRingPosX + FB_WIDTH_BASE / 2 + mRingRadiusH * cM_ssin(field_0x66e); f32 itemRingPosY = - g_ringHIO.mItemRingPosY + 224.0f + mRingRadiusV * cM_scos(field_0x66e); + g_ringHIO.mItemRingPosY + FB_HEIGHT_BASE / 2 + mRingRadiusV * cM_scos(field_0x66e); mpDrawCursor->setPos(itemRingPosX, itemRingPosY); } } diff --git a/src/d/d_meter2_draw.cpp b/src/d/d_meter2_draw.cpp index 58c6e24dc9..417dcad319 100644 --- a/src/d/d_meter2_draw.cpp +++ b/src/d/d_meter2_draw.cpp @@ -20,6 +20,7 @@ #include "d/d_msg_class.h" #include "d/d_msg_object.h" #include "d/d_pane_class.h" +#include "dusk/frame_interpolation.h" #include dMeter2Draw_c::dMeter2Draw_c(JKRExpHeap* mp_heap) { @@ -636,38 +637,49 @@ void dMeter2Draw_c::draw() { if (field_0x756 >= 0) { var_f29 = g_drawHIO.mLightDrop.mDropPikariAnimSpeed_Completed; int temp_r5_2 = g_drawHIO.mLightDrop.mPikariInterval * 15; +#ifdef TARGET_PC + // Set even if not advancing + var_f28 = g_drawHIO.mLightDrop.mPikariScaleComplete; - if (field_0x756 <= temp_r5_2) { - int temp_r4 = (field_0x756 % g_drawHIO.mLightDrop.mPikariInterval); - int temp_r3_5 = field_0x756 / g_drawHIO.mLightDrop.mPikariInterval; + const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); + for (u32 tick = 0; tick < ui_advance_ticks; ++tick) { +#endif + if (field_0x756 <= temp_r5_2) { + int temp_r4 = (field_0x756 % g_drawHIO.mLightDrop.mPikariInterval); + int temp_r3_5 = field_0x756 / g_drawHIO.mLightDrop.mPikariInterval; - if (temp_r4 == 0 && field_0x62c[temp_r3_5] == 0.0f) { - field_0x62c[temp_r3_5] = 18.0f; - } - - var_f28 = g_drawHIO.mLightDrop.mPikariScaleComplete; - field_0x756++; - } else { - int temp_r5_3 = temp_r5_2 + 1; - - if (field_0x756 == temp_r5_3) { - if (field_0x62c[15] == 0.0f) { - field_0x756++; + if (temp_r4 == 0 && field_0x62c[temp_r3_5] == 0.0f) { + field_0x62c[temp_r3_5] = 18.0f; } var_f28 = g_drawHIO.mLightDrop.mPikariScaleComplete; - } else if (field_0x756 >= g_drawHIO.mLightDrop.field_0x54 + temp_r5_3) { - for (int i = 0; i < 16; i++) { - field_0x62c[i] = 18.0f - var_f29; - field_0x66c[i] = 18.0f - g_drawHIO.mLightDrop.mPikariLoopAnimSpeed; - } - - field_0x756 = -1; - } else { field_0x756++; + } else { + int temp_r5_3 = temp_r5_2 + 1; + + if (field_0x756 == temp_r5_3) { + if (field_0x62c[15] == 0.0f) { + field_0x756++; + } + var_f28 = g_drawHIO.mLightDrop.mPikariScaleComplete; + } else if (field_0x756 >= g_drawHIO.mLightDrop.field_0x54 + temp_r5_3) { + for (int i = 0; i < 16; i++) { + field_0x62c[i] = 18.0f - var_f29; + field_0x66c[i] = 18.0f - g_drawHIO.mLightDrop.mPikariLoopAnimSpeed; + } + + field_0x756 = -1; +#ifdef TARGET_PC + break; +#endif + } else { + field_0x756++; + } } } +#ifdef TARGET_PC } +#endif for (int i = 0; i < 16; i++) { if (field_0x66c[i] > 0.0f) { @@ -1336,20 +1348,27 @@ void dMeter2Draw_c::drawPikari(f32 i_posX, f32 i_posY, f32* i_framep, f32 i_scal if (param_9 != 3 && param_9 != 4 && param_9 != 5 && dMsgObject_isTalkNowCheck()) { *i_framep = 0.0f; } else { - *i_framep += param_8; - if (*i_framep > var_f31) { - if (param_9 == 1 || param_9 == 2 || param_9 == 3) { - *i_framep = 18.0f; - } else { - *i_framep = 0.0f; +#ifdef TARGET_PC + const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); + for (u32 i = 0; i < ui_advance_ticks; ++i) { +#endif + *i_framep += param_8; + if (*i_framep > var_f31) { + if (param_9 == 1 || param_9 == 2 || param_9 == 3) { + *i_framep = 18.0f; + } else { + *i_framep = 0.0f; + } } - } - if (*i_framep == 18.0f && param_9 == 1) { - mDoAud_seStart(Z2SE_NAVI_BLINK, NULL, 0, 0); - } else if (*i_framep == 18.0f && param_9 == 2) { - mDoAud_seStart(Z2SE_SY_ITEM_COMBINE_ICON, NULL, 0, 0); + if (*i_framep == 18.0f && param_9 == 1) { + mDoAud_seStart(Z2SE_NAVI_BLINK, NULL, 0, 0); + } else if (*i_framep == 18.0f && param_9 == 2) { + mDoAud_seStart(Z2SE_SY_ITEM_COMBINE_ICON, NULL, 0, 0); + } +#ifdef TARGET_PC } +#endif playPikariBckAnimation(*i_framep); playPikariBpkAnimation(*i_framep); @@ -1543,6 +1562,7 @@ void dMeter2Draw_c::setAlphaLifeAnimeMax() { } void dMeter2Draw_c::drawKanteraScreen(u8 i_meterType) { + GX_AND_TRACY_SCOPED("drawKanteraScreen"); J2DGrafContext* graf_ctx = dComIfGp_getCurrentGrafPort(); mpMagicParent->setAlphaRate(mMeterAlphaRate[i_meterType]); diff --git a/src/d/d_meter2_info.cpp b/src/d/d_meter2_info.cpp index 7307755c04..bb533e6370 100644 --- a/src/d/d_meter2_info.cpp +++ b/src/d/d_meter2_info.cpp @@ -205,11 +205,11 @@ void dMeter2Info_c::init() { unk_0x5c = 0.0f; unk_0x60 = 1.0f; unk_0x64 = 30.0f; - unk_0x68 = 304.0f; - unk_0x6c = 224.0f; + unk_0x68 = FB_WIDTH_BASE / 2; + unk_0x6c = FB_HEIGHT_BASE / 2; - m2DWidth = 608.0f; - m2DHeight = 448.0f; + m2DWidth = FB_WIDTH_BASE; + m2DHeight = FB_HEIGHT_BASE; m2DPosH = 0.0f; m2DPosV = 0.0f; unk_0x80 = 0.0f; diff --git a/src/d/d_meter_HIO.cpp b/src/d/d_meter_HIO.cpp index c23e6c3230..0566494863 100644 --- a/src/d/d_meter_HIO.cpp +++ b/src/d/d_meter_HIO.cpp @@ -197,11 +197,11 @@ void dMeter_drawOptionHIO_c::genMessage(JORMContext* mctx) { mctx->genSlider("位置X", &mOptionTypeBGPosX[3], -300.0f, 300.0f); mctx->genSlider("位置Y", &mOptionTypeBGPosY[3], -300.0f, 300.0f); mctx->genLabel("*****テレビ画面の設定*****", 0); - mctx->genSlider("位置X", &mTVsettingPosX, -300.0f, 300.0f); - mctx->genSlider("位置Y", &mTVsettingPosY, -300.0f, 300.0f); + mctx->genSlider("位置X", &mOptionTypeBGPosX[4], -300.0f, 300.0f); + mctx->genSlider("位置Y", &mOptionTypeBGPosY[4], -300.0f, 300.0f); mctx->genLabel("*****キャリブレーション*****", 0); - mctx->genSlider("位置X", &mPointerCalibrationPosX, -300.0f, 300.0f); - mctx->genSlider("位置Y", &mPointerCalibrationPosY, -300.0f, 300.0f); + mctx->genSlider("位置X", &mOptionTypeBGPosX[5], -300.0f, 300.0f); + mctx->genSlider("位置Y", &mOptionTypeBGPosY[5], -300.0f, 300.0f); mctx->genLabel("*****設定確認*****", 0); mctx->genSlider("背景アルファ", &mBackgroundAlpha, 0, 255); mctx->genSlider("位置Y", &mBackgroundPosY, -300.0f, 300.0f); @@ -1493,7 +1493,7 @@ dMeter_drawLightDropHIO_c::dMeter_drawLightDropHIO_c() { mPikariInterval = 1; field_0x54 = 0xFFF1; mVesselAlpha[2] = 0.5f; - mDropAlpha = 1.0f; + mVesselAlpha[3] = 1.0f; mDropOnColorBlack.set(30, 255, 255, 0); mDropOnColorWhite.set(255, 255, 255, 255); @@ -1554,7 +1554,7 @@ void dMeter_drawLightDropHIO_c::genMessage(JORMContext* mctx) { mctx->genSlider("位置調整Y(会話)", &mVesselTalkPosY, -300.0f, 300.0f); mctx->genSlider("アルファ(会話)", &mVesselAlpha[1], 0.0f, 1.0f); mctx->genSlider("アルファ(器)", &mVesselAlpha[2], 0.0f, 1.0f); - mctx->genSlider("アルファ(雫)", &mDropAlpha, 0.0f, 1.0f); + mctx->genSlider("アルファ(雫)", &mVesselAlpha[3], 0.0f, 1.0f); mctx->genLabel("***光の器ゲット後***", 0); mctx->genSlider("拡大縮小", &mDropGetScale, 0.0f, 3.0f); mctx->genSlider("拡縮アニメフレーム数", &mDropGetScaleAnimFrameNum, 0, 30); @@ -4010,9 +4010,9 @@ void dMeter_fmapHIO_c::genMessage(JORMContext* mctx) { mctx->genSlider("リージョン拡大表示範囲", &mRegionZoomRange, 1000.0, 1000000.0); mctx->genCheckBox("表示基準領域枠表示", (u8*)&mDisplayReferenceArea, 0x1); mctx->genCheckBox("スクロール範囲を表示基準", (u8*)&field_0x308, 0x1); - mctx->genSlider("左上座標X", &mMapTopLeftPosX, 0.0, 608.0); - mctx->genSlider("左上座標Y", &mMapTopLeftPosY, 0.0, 448.0); - mctx->genSlider("領域幅", &mMapScale, 0.0, 608.0); + mctx->genSlider("左上座標X", &mMapTopLeftPosX, 0.0, FB_WIDTH_BASE); + mctx->genSlider("左上座標Y", &mMapTopLeftPosY, 0.0, FB_HEIGHT_BASE); + mctx->genSlider("領域幅", &mMapScale, 0.0, FB_WIDTH_BASE); mctx->genLabel("\n*** スクロール速度境界 ***", 0); mctx->genSlider("0~遅", &mScrollSpeedSlowBound, 0.0, 1.0); mctx->genSlider("遅~速", &mScrollSpeedFastBound, 0.0, 1.0); diff --git a/src/d/d_meter_button.cpp b/src/d/d_meter_button.cpp index 5de89f1da3..88f750d6ca 100644 --- a/src/d/d_meter_button.cpp +++ b/src/d/d_meter_button.cpp @@ -1035,7 +1035,7 @@ void dMeterButton_c::screenInitButton() { } field_0x4b0 = 0; - mParentCenterX = (608.0f / 2) - mpParent->getInitCenterPosX(); + mParentCenterX = (FB_WIDTH_BASE / 2.0f) - mpParent->getInitCenterPosX(); paneTrans(mpParent, mParentCenterX, 0.0f, 0xFF); mpButtonA = JKR_NEW CPaneMgr(mpButtonScreen, MULTI_CHAR('abtn_n'), 2, NULL); diff --git a/src/d/d_meter_haihai.cpp b/src/d/d_meter_haihai.cpp index e51e0b070e..daf30b9aae 100644 --- a/src/d/d_meter_haihai.cpp +++ b/src/d/d_meter_haihai.cpp @@ -99,28 +99,28 @@ void dMeterHaihai_c::draw() { } if (direction & DIR_DOWN_e || i_forceDraw) { - mpParent->getPanePtr()->translate(x_pos[0] + 304.0f, y_pos[0] + 224.0f); + mpParent->getPanePtr()->translate(x_pos[0] + FB_WIDTH_BASE / 2, y_pos[0] + FB_HEIGHT_BASE / 2); mpParent->getPanePtr()->rotate(mpParent->getPanePtr()->getWidth() / 2, mpParent->getPanePtr()->getHeight() / 2, ROTATE_Z, 0.0f); mpHaihaiScreen->draw(0.0f, 0.0f, dComIfGp_getCurrentGrafPort()); } if (direction & DIR_RIGHT_e || i_forceDraw) { - mpParent->getPanePtr()->translate(x_pos[1] + 304.0f, y_pos[1] + 224.0f); + mpParent->getPanePtr()->translate(x_pos[1] + FB_WIDTH_BASE / 2, y_pos[1] + FB_HEIGHT_BASE / 2); mpParent->getPanePtr()->rotate(mpParent->getPanePtr()->getWidth() / 2, mpParent->getPanePtr()->getHeight() / 2, ROTATE_Z, 90.0f); mpHaihaiScreen->draw(0.0f, 0.0f, dComIfGp_getCurrentGrafPort()); } if (direction & DIR_UP_e || i_forceDraw) { - mpParent->getPanePtr()->translate(x_pos[0] + 304.0f, 224.0f - y_pos[0]); + mpParent->getPanePtr()->translate(x_pos[0] + FB_WIDTH_BASE / 2, FB_HEIGHT_BASE / 2 - y_pos[0]); mpParent->getPanePtr()->rotate(mpParent->getPanePtr()->getWidth() / 2, mpParent->getPanePtr()->getHeight() / 2, ROTATE_Z, 180.0f); mpHaihaiScreen->draw(0.0f, 0.0f, dComIfGp_getCurrentGrafPort()); } if (direction & DIR_LEFT_e || i_forceDraw) { - mpParent->getPanePtr()->translate(304.0f - x_pos[1], y_pos[1] + 224.0f); + mpParent->getPanePtr()->translate(FB_WIDTH_BASE / 2 - x_pos[1], y_pos[1] + FB_HEIGHT_BASE / 2); mpParent->getPanePtr()->rotate(mpParent->getPanePtr()->getWidth() / 2, mpParent->getPanePtr()->getHeight() / 2, ROTATE_Z, 270.0f); mpHaihaiScreen->draw(0.0f, 0.0f, dComIfGp_getCurrentGrafPort()); @@ -128,8 +128,8 @@ void dMeterHaihai_c::draw() { } void dMeterHaihai_c::drawHaihai(u8 i_direction) { - f32 center_x = 304.0f; - f32 center_y = 224.0f; + f32 center_x = FB_WIDTH_BASE / 2; + f32 center_y = FB_HEIGHT_BASE / 2; if (mType == 1) { center_x += (3.0f + g_drawHIO.mScrollArrowCenterPosX); diff --git a/src/d/d_msg_flow.cpp b/src/d/d_msg_flow.cpp index d487b8f7bd..c461b179e1 100644 --- a/src/d/d_msg_flow.cpp +++ b/src/d/d_msg_flow.cpp @@ -2234,7 +2234,13 @@ int dMsgFlow_c::event027(mesg_flow_node_event* i_flowNode_p, fopAc_ac_c* i_speak u16 prm0; getParam(&prm0, &prm1, i_flowNode_p->params); + // !@bug aParam8 is undersized; getParam always writes 4 bytes, stomping the + // 2 bytes past the buffer. Harmless on GC/Wii (MWCC stack layout absorbs it). +#if AVOID_UB + u8 aParam8[4]; +#else u8 aParam8[2]; +#endif getParam(aParam8, i_flowNode_p->params); JUT_ASSERT(4509, (aParam8[0] >= 0 && aParam8[0] <= dSv_player_item_c::BOMB_BAG_MAX) || (aParam8[0] == 4)); diff --git a/src/d/d_msg_scrn_explain.cpp b/src/d/d_msg_scrn_explain.cpp index de4ee0caf0..945f75e927 100644 --- a/src/d/d_msg_scrn_explain.cpp +++ b/src/d/d_msg_scrn_explain.cpp @@ -70,7 +70,7 @@ dMsgScrnExplain_c::dMsgScrnExplain_c(STControl* i_stick, u8 param_1, bool i_isUs mpTxScreen = JKR_NEW J2DScreen(); JUT_ASSERT(102, mpTxScreen != NULL); - field_0x48 = 608.0f; + field_0x48 = FB_WIDTH_BASE; if (param_1 == 1 || param_1 == 3) { #if PLATFORM_GCN @@ -309,7 +309,7 @@ void dMsgScrnExplain_c::draw(J2DOrthoGraph* i_graf) { } if (mpBackTex != NULL) { - mpBackTex->draw(0.0f, 0.0f, 608.0f, 448.0f, false, false, false); + mpBackTex->draw(0.0f, 0.0f, FB_WIDTH_BASE, FB_HEIGHT_BASE, false, false, false); } if (field_0x66 != 2 && field_0x66 != 3) { @@ -330,6 +330,9 @@ void dMsgScrnExplain_c::draw(J2DOrthoGraph* i_graf) { strcpy(((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr(), string_buf); if (mpSelect_c != NULL && (field_0x64 == 1 || field_0x64 == 2)) { + // the magic numbers here are relative to the framebuffer size, but were likely + // either chosen by hand or had multiple arithmetic operations applied which + // cannot easily be reverse engineered f32 y_offset = 0.0f; if (field_0x66 == 2) { y_offset = -100.0f; @@ -385,7 +388,7 @@ void dMsgScrnExplain_c::open_request_proc() { void dMsgScrnExplain_c::open_init() { field_0x5a = 0; - field_0x48 = 608.0f; + field_0x48 = FB_WIDTH_BASE; for (int i = 0; i < 2; i++) { mpRoot_c[i]->setAlphaRate(0.0f); } @@ -403,7 +406,7 @@ void dMsgScrnExplain_c::open_proc() { } } - field_0x48 = 608.0f * getAlphaRatio(); + field_0x48 = FB_WIDTH_BASE * getAlphaRatio(); for (int i = 0; i < 2; i++) { mpRoot_c[i]->setAlphaRate(1.0f - getAlphaRatio()); } @@ -583,7 +586,7 @@ void dMsgScrnExplain_c::close_proc() { } } - field_0x48 = 608.0f * getAlphaRatio(); + field_0x48 = FB_WIDTH_BASE * getAlphaRatio(); for (int i = 0; i < 2; i++) { mpRoot_c[i]->setAlphaRate(1.0f - getAlphaRatio()); } diff --git a/src/d/d_msg_scrn_item.cpp b/src/d/d_msg_scrn_item.cpp index 40732eff25..53ef97607c 100644 --- a/src/d/d_msg_scrn_item.cpp +++ b/src/d/d_msg_scrn_item.cpp @@ -609,6 +609,9 @@ void dMsgScrnItem_c::fukiPosCalc(u8 param_1) { field_0x180 = 0.0f; field_0x19c = param_1; f32 yOffset; + // the magic numbers here are correlated with the framebuffer size, but + // were likely either chosen by hand or had multiple arithmetic + // operations applied which cannot easily be reverse engineered switch(field_0x19c) { case 1: yOffset = g_MsgObject_HIO_c.mBoxPos[2][3]; diff --git a/src/d/d_msg_scrn_light.cpp b/src/d/d_msg_scrn_light.cpp index 4635a8e680..c34397aad2 100644 --- a/src/d/d_msg_scrn_light.cpp +++ b/src/d/d_msg_scrn_light.cpp @@ -6,6 +6,7 @@ #include "JSystem/J2DGraph/J2DScreen.h" #include "d/d_com_inf_game.h" #include "d/d_pane_class.h" +#include "dusk/frame_interpolation.h" class dMsgScrnLight_HIO_c { public: @@ -202,10 +203,17 @@ void dMsgScrnLight_c::draw(f32* i_anmFrame, f32 i_posX, f32 i_posY, f32 i_scaleX } if (mPlayAnim) { - *i_anmFrame += 1.0f; - if (*i_anmFrame >= mpBck->getFrameMax()) { - *i_anmFrame = 0.0f; +#ifdef TARGET_PC + const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); + for (u32 i = 0; i < ui_advance_ticks; ++i) { +#endif + *i_anmFrame += 1.0f; + if (*i_anmFrame >= mpBck->getFrameMax()) { + *i_anmFrame = 0.0f; + } +#ifdef TARGET_PC } +#endif mBckFrame = *i_anmFrame; mBpkFrame = *i_anmFrame; @@ -220,11 +228,18 @@ void dMsgScrnLight_c::draw(f32* i_anmFrame, f32 i_posX, f32 i_posY, f32 i_scaleX mpParent_c->setBlackWhite(i_black, i_white); if (mPlayAnim) { - *i_anmFrame += i_anmRate; +#ifdef TARGET_PC + const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); + for (u32 i = 0; i < ui_advance_ticks; ++i) { +#endif + *i_anmFrame += i_anmRate; - if (*i_anmFrame >= mpBck->getFrameMax()) { - *i_anmFrame = 0.0f; + if (*i_anmFrame >= mpBck->getFrameMax()) { + *i_anmFrame = 0.0f; + } +#ifdef TARGET_PC } +#endif mBckFrame = *i_anmFrame; mBpkFrame = *i_anmFrame; diff --git a/src/d/d_msg_scrn_talk.cpp b/src/d/d_msg_scrn_talk.cpp index 546e42112f..68b471a14d 100644 --- a/src/d/d_msg_scrn_talk.cpp +++ b/src/d/d_msg_scrn_talk.cpp @@ -454,11 +454,11 @@ void dMsgScrnTalk_c::fukiPosCalc(u8 param_1) { #if TARGET_PC mDoLib_project(&msgActor->pos, &local_70, {0, 0, FB_WIDTH, FB_HEIGHT}); #else - mDoLib_project(&msgActor->pos, &local_70,); + mDoLib_project(&msgActor->pos, &local_70); #endif - - if (local_70.x >= 0.0f && local_70.x <= 608.0f && local_70.y >= 0.0f && - local_70.y <= 448.0f) + + if (local_70.x >= 0.0f && local_70.x <= FB_WIDTH_BASE && local_70.y >= 0.0f && + local_70.y <= FB_HEIGHT_BASE) { f3y = 0.5f * (cStack_7c.y + local_70.y); } else { @@ -493,6 +493,9 @@ void dMsgScrnTalk_c::fukiPosCalc(u8 param_1) { field_0xf0 = 0.0f; field_0x488 = param_1; f32 dVar15; + // the magic numbers here are correlated with the framebuffer size, but + // were likely either chosen by hand or had multiple arithmetic + // operations applied which cannot easily be reverse engineered switch (field_0x488) { case 1: dVar15 = g_MsgObject_HIO_c.mBoxPos[2][0]; diff --git a/src/d/d_s_logo.cpp b/src/d/d_s_logo.cpp index 6aa3778dbf..71b4d21de4 100644 --- a/src/d/d_s_logo.cpp +++ b/src/d/d_s_logo.cpp @@ -1176,19 +1176,20 @@ void dScnLogo_c::logoInitWii() { break; } - width = 608; - height = 456; + // this uses the standard width but the widescreen height? + width = FB_WIDTH_BASE; + height = FB_HEIGHT; } JUT_ASSERT(2309, timg != NULL); - mStrapImg = JKR_NEW dDlst_2D_c(timg, 304 - (width / 2), 224 - (height / 2), width, height, 255); + mStrapImg = JKR_NEW dDlst_2D_c(timg, (FB_WIDTH_BASE / 2) - (width / 2), (FB_HEIGHT_BASE / 2) - (height / 2), width, height, 255); #if VERSION == VERSION_SHIELD timg = (ResTIMG*)dComIfG_getObjectRes("LogoUsWii", 5); - mNvLogo = JKR_NEW dDlst_2D_c(timg, 304 - (width / 2), 224 - (height / 2), width, height, 255); + mNvLogo = JKR_NEW dDlst_2D_c(timg, (FB_WIDTH_BASE / 2) - (width / 2), (FB_HEIGHT_BASE / 2) - (height / 2), width, height, 255); timg = (ResTIMG*)dComIfG_getObjectRes("LogoUsWii", 4); - mMocImg = JKR_NEW dDlst_2D_c(timg, 304 - (width / 2), 224 - (height / 2), width, height, 255); + mMocImg = JKR_NEW dDlst_2D_c(timg, (FB_WIDTH_BASE / 2) - (width / 2), (FB_HEIGHT_BASE / 2) - (height / 2), width, height, 255); #endif OS_REPORT("\x1b[32m%d archiveHeap->getTotalFreeSize %08x\n\x1b[m", 2316, archiveHeap->getTotalFreeSize()); diff --git a/src/d/d_s_play.cpp b/src/d/d_s_play.cpp index 3ef8ea7eb2..3f3cd073b9 100644 --- a/src/d/d_s_play.cpp +++ b/src/d/d_s_play.cpp @@ -39,7 +39,9 @@ #include "JSystem/JKernel/JKRAram.h" #include "JSystem/JKernel/JKRAramArchive.h" +#if TARGET_PC #include "dusk/memory.h" +#endif #if DEBUG #include "d/d_s_menu.h" diff --git a/src/d/d_scope.cpp b/src/d/d_scope.cpp index 3e2c18131d..2f68f534ed 100644 --- a/src/d/d_scope.cpp +++ b/src/d/d_scope.cpp @@ -136,24 +136,24 @@ void dScope_c::draw() { u8 alpha = mAlpha * 255.0f; if (dComIfGp_checkPlayerStatus0(0, 0x1000)) { - J2DDrawLine(304.0f, mDoGph_gInf_c::getMinYF(), 304.0f, mDoGph_gInf_c::getMaxYF(), - JUtility::TColor(255, 0, 0, alpha), 6); - J2DDrawLine(mDoGph_gInf_c::getMinXF(), 224.0f, mDoGph_gInf_c::getMaxXF(), 224.0f, - JUtility::TColor(255, 0, 0, alpha), 6); + J2DDrawLine(FB_WIDTH_BASE / 2, mDoGph_gInf_c::getMinYF(), FB_WIDTH_BASE / 2, + mDoGph_gInf_c::getMaxYF(), JUtility::TColor(255, 0, 0, alpha), 6); + J2DDrawLine(mDoGph_gInf_c::getMinXF(), FB_HEIGHT_BASE / 2, mDoGph_gInf_c::getMaxXF(), + FB_HEIGHT_BASE / 2, JUtility::TColor(255, 0, 0, alpha), 6); } mpWipeTex->setAlpha(alpha); mpBlackTex->setAlpha(alpha); - f32 temp_f29 = 304.0f - temp_f31; - f32 temp_f28 = 304.0f + temp_f31; - f32 temp_f27 = 224.0f - temp_f30; - f32 temp_f26 = 224.0f + temp_f30; + f32 temp_f29 = FB_WIDTH_BASE / 2 - temp_f31; + f32 temp_f28 = FB_WIDTH_BASE / 2 + temp_f31; + f32 temp_f27 = FB_HEIGHT_BASE / 2 - temp_f30; + f32 temp_f26 = FB_HEIGHT_BASE / 2 + temp_f30; mpWipeTex->draw(temp_f29, temp_f27, temp_f31, temp_f30, false, false, false); - mpWipeTex->draw(304.0f, temp_f27, temp_f31, temp_f30, true, false, false); - mpWipeTex->draw(temp_f29, 224.0f, temp_f31, temp_f30, false, true, false); - mpWipeTex->draw(304.0f, 224.0f, temp_f31, temp_f30, true, true, false); + mpWipeTex->draw(FB_WIDTH_BASE / 2, temp_f27, temp_f31, temp_f30, true, false, false); + mpWipeTex->draw(temp_f29, FB_HEIGHT_BASE / 2, temp_f31, temp_f30, false, true, false); + mpWipeTex->draw(FB_WIDTH_BASE / 2, FB_HEIGHT_BASE / 2, temp_f31, temp_f30, true, true, false); mpBlackTex->draw(mDoGph_gInf_c::getMinXF(), mDoGph_gInf_c::getMinYF(), mDoGph_gInf_c::getWidthF(), temp_f27 - mDoGph_gInf_c::getMinYF(), false, false, diff --git a/src/d/d_select_cursor.cpp b/src/d/d_select_cursor.cpp index 17ee9ca451..2cdd454737 100644 --- a/src/d/d_select_cursor.cpp +++ b/src/d/d_select_cursor.cpp @@ -5,6 +5,7 @@ #include "d/d_com_inf_game.h" #include "JSystem/J2DGraph/J2DAnimation.h" #include "JSystem/J2DGraph/J2DAnmLoader.h" +#include "dusk/frame_interpolation.h" #include dSelect_cursorHIO_c::dSelect_cursorHIO_c() { @@ -270,15 +271,22 @@ void dSelect_cursor_c::update() { if (mUpdateFlag) { if (field_0x30) { if (chkPlayAnime(0)) { - if (mNameIdx == 1) { - field_0x44 += mpCursorHIO->field_0x8 * fVar1; - } else { - field_0x44 += fVar1; - } +#ifdef TARGET_PC + const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); + for (u32 tick = 0; tick < ui_advance_ticks; ++tick) { +#endif + if (mNameIdx == 1) { + field_0x44 += mpCursorHIO->field_0x8 * fVar1; + } else { + field_0x44 += fVar1; + } - if (field_0x44 >= field_0x30->getFrameMax()) { - field_0x44 -= field_0x30->getFrameMax(); + if (field_0x44 >= field_0x30->getFrameMax()) { + field_0x44 -= field_0x30->getFrameMax(); + } +#ifdef TARGET_PC } +#endif field_0x30->setFrame(field_0x44); setBpkAnimation(field_0x30); @@ -294,14 +302,21 @@ void dSelect_cursor_c::update() { for (int i = 0; i < 2; i++) { if (field_0x34[i]) { if ((i == 0 && chkPlayAnime(2)) || (i == 1 && chkPlayAnime(3))) { - if (mNameIdx == 1) { - field_0x48[i] += mpCursorHIO->field_0x8 * fVar1; - } else { - field_0x48[i] += fVar1; - } - if (field_0x48[i] >= field_0x34[i]->getFrameMax()) { - field_0x48[i] -= field_0x34[i]->getFrameMax(); +#ifdef TARGET_PC + const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); + for (u32 tick = 0; tick < ui_advance_ticks; ++tick) { +#endif + if (mNameIdx == 1) { + field_0x48[i] += mpCursorHIO->field_0x8 * fVar1; + } else { + field_0x48[i] += fVar1; + } + if (field_0x48[i] >= field_0x34[i]->getFrameMax()) { + field_0x48[i] -= field_0x34[i]->getFrameMax(); + } +#ifdef TARGET_PC } +#endif field_0x34[i]->setFrame(field_0x48[i]); } @@ -310,7 +325,11 @@ void dSelect_cursor_c::update() { } if (field_0x2C && chkPlayAnime(1)) { - if (mNameIdx == 1) { +#ifdef TARGET_PC + const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); + for (u32 tick = 0; tick < ui_advance_ticks; ++tick) { +#endif + if (mNameIdx == 1) { field_0x40 += mpCursorHIO->field_0x8 * fVar1; } else { field_0x40 += fVar1; @@ -318,14 +337,24 @@ void dSelect_cursor_c::update() { if (field_0x40 >= field_0x2C->getFrameMax()) { field_0x40 -= field_0x2C->getFrameMax(); } - - field_0x2C->setFrame(field_0x40); - setBckAnimation(field_0x2C); +#ifdef TARGET_PC + } +#endif + + field_0x2C->setFrame(field_0x40); + setBckAnimation(field_0x2C); } if (chkPlayAnime(1) && mNameIdx == 0) { - setCursorAnimation(); +#ifdef TARGET_PC + const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); + for (u32 tick = 0; tick < ui_advance_ticks; ++tick) { +#endif + setCursorAnimation(); +#ifdef TARGET_PC + } +#endif } mpScreen->animation(); diff --git a/src/d/d_select_icon.cpp b/src/d/d_select_icon.cpp index cbf5f44381..3a4671fefa 100644 --- a/src/d/d_select_icon.cpp +++ b/src/d/d_select_icon.cpp @@ -2,21 +2,31 @@ #include "d/d_select_icon.h" #include "JSystem/J2DGraph/J2DAnimation.h" +#include "dusk/frame_interpolation.h" dSi_HIO_c::dSi_HIO_c() {} void dSelect_icon_c::animation() { if (field_0x10->getAlpha() != 0) { - field_0x20 += field_0x2c; - if (field_0x20 >= field_0x1c->getFrameMax()) { - field_0x20 = 0.0f; - } - field_0x1c->setFrame(field_0x20); +#ifdef TARGET_PC + const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); + for (u32 i = 0; i < ui_advance_ticks; ++i) { +#endif + field_0x20 += field_0x2c; + if (field_0x20 >= field_0x1c->getFrameMax()) { + field_0x20 = 0.0f; + } + field_0x1c->setFrame(field_0x20); - field_0x28 += field_0x2c; - if (field_0x28 >= field_0x24->getFrameMax()) { - field_0x28 = 0.0f; + field_0x28 += field_0x2c; + if (field_0x28 >= field_0x24->getFrameMax()) { + field_0x28 = 0.0f; + } +#ifdef TARGET_PC } + // Set even if not advancing + field_0x1c->setFrame(field_0x20); +#endif field_0x24->setFrame(field_0x28); field_0x8->animation(); diff --git a/src/dusk/frame_interpolation.cpp b/src/dusk/frame_interpolation.cpp new file mode 100644 index 0000000000..0c21b2b5af --- /dev/null +++ b/src/dusk/frame_interpolation.cpp @@ -0,0 +1,409 @@ +#include "dusk/frame_interpolation.h" + +#include +#include +#include +#include +#include +#include + +namespace { + +enum class Op : uint8_t { + OpenChild, + FinalMtx, +}; + +struct Label { + const void* key = nullptr; + int32_t id = 0; + + bool operator==(const Label& other) const { + return key == other.key && id == other.id; + } +}; + +struct Data { + Label child_label{}; + size_t child_index = 0; + Mtx matrix{}; + const Mtx* dest = nullptr; +}; + +struct Path; + +struct ChildBucket { + Label label{}; + std::vector> nodes; +}; + +struct OpBucket { + Op op = Op::OpenChild; + std::vector values; +}; + +struct Path { + std::vector children; + std::vector ops; + std::vector> items; +}; + +struct Recording { + Path root; +}; + +struct MatrixValue { + Mtx value; +}; + +using FinalMtxLookup = std::unordered_map; + +bool s_initialized = false; + +bool g_enabled = false; +bool g_recording = false; +bool g_interpolating = false; +float g_step = 0.0f; +uint32_t g_pending_presentation_ui_ticks = 0; +uint32_t g_current_presentation_ui_ticks = 0; + +Recording g_current_recording; +Recording g_previous_recording; +std::vector g_current_path; + +std::unordered_map g_replacements; + +inline void copy_matrix(const Mtx src, Mtx dst) { + MTXCopy(src, dst); +} + +inline void concat_matrix(const Mtx lhs, const Mtx rhs, Mtx out) { + MTXConcat(lhs, rhs, out); +} + +inline void lerp_matrix(Mtx out, const Mtx lhs, const Mtx rhs, float step) { + const float old_weight = 1.0f - step; + for (size_t row = 0; row < 3; ++row) { + for (size_t col = 0; col < 4; ++col) { + out[row][col] = lhs[row][col] * old_weight + rhs[row][col] * step; + } + } +} + +inline bool matrix_differs(const Mtx lhs, const Mtx rhs, float epsilon = 0.0001f) { + for (size_t row = 0; row < 3; ++row) { + for (size_t col = 0; col < 4; ++col) { + if (std::abs(lhs[row][col] - rhs[row][col]) > epsilon) { + return true; + } + } + } + return false; +} + +Data& append_op(Op op) { + auto& items = g_current_path.back()->items; + auto& buckets = g_current_path.back()->ops; + auto it = std::find_if(buckets.begin(), buckets.end(), + [op](const OpBucket& bucket) { return bucket.op == op; }); + if (it == buckets.end()) { + buckets.push_back({op, {}}); + it = buckets.end() - 1; + } + items.emplace_back(op, it->values.size()); + return it->values.emplace_back(); +} + +const Data* find_matching_data(const Path& path, Op op, size_t index) { + auto it = std::find_if(path.ops.begin(), path.ops.end(), + [op](const OpBucket& bucket) { return bucket.op == op; }); + if (it == path.ops.end() || index >= it->values.size()) { + return nullptr; + } + return &it->values[index]; +} + +const OpBucket* find_op_bucket(const Path& path, Op op) { + auto it = std::find_if(path.ops.begin(), path.ops.end(), + [op](const OpBucket& bucket) { return bucket.op == op; }); + if (it == path.ops.end()) { + return nullptr; + } + return &*it; +} + +void build_final_mtx_lookup(const Path& path, FinalMtxLookup& lookup) { + lookup.clear(); + + const OpBucket* bucket = find_op_bucket(path, Op::FinalMtx); + if (bucket == nullptr) { + return; + } + + for (const Data& data : bucket->values) { + if (data.dest == nullptr) { + continue; + } + lookup[data.dest] = &data; + } +} + +const Data* find_matching_final_mtx(const FinalMtxLookup& lookup, const Data& new_data) { + if (new_data.dest == nullptr) { + return nullptr; + } + + auto it = lookup.find(new_data.dest); + if (it == lookup.end()) { + return nullptr; + } + return it->second; +} + +ChildBucket& get_child_bucket(Path& path, const Label& label) { + auto it = std::find_if(path.children.begin(), path.children.end(), + [&label](const ChildBucket& bucket) { return bucket.label == label; }); + if (it == path.children.end()) { + path.children.push_back({}); + it = path.children.end() - 1; + it->label = label; + } + return *it; +} + +const ChildBucket* find_child_bucket(const Path& path, const Label& label) { + auto it = std::find_if(path.children.begin(), path.children.end(), + [&label](const ChildBucket& bucket) { return bucket.label == label; }); + if (it == path.children.end()) { + return nullptr; + } + return &*it; +} + +void store_replacement(const Data& old_data, const Data& new_data, float step) { + if (new_data.dest == nullptr) { + return; + } + + auto& replacement = g_replacements[new_data.dest]; + lerp_matrix(replacement.value, old_data.matrix, new_data.matrix, step); +} + +void interpolate_branch(const Path& old_path, const Path& new_path, float step) { + FinalMtxLookup old_final_mtx_lookup; + build_final_mtx_lookup(old_path, old_final_mtx_lookup); + + for (const auto& item : new_path.items) { + const Op op = item.first; + const size_t index = item.second; + const Data* new_data = find_matching_data(new_path, op, index); + if (new_data == nullptr) { + continue; + } + + if (op == Op::OpenChild) { + const ChildBucket* new_children = find_child_bucket(new_path, new_data->child_label); + if (new_children == nullptr || new_data->child_index >= new_children->nodes.size()) + { + continue; + } + + const Path& new_child = *new_children->nodes[new_data->child_index]; + const ChildBucket* old_children = find_child_bucket(old_path, new_data->child_label); + if (old_children != nullptr && new_data->child_index < old_children->nodes.size()) + { + interpolate_branch(*old_children->nodes[new_data->child_index], new_child, step); + } else { + interpolate_branch(new_child, new_child, step); + } + continue; + } + + const Data* indexed_old_data = find_matching_data(old_path, op, index); + const Data* old_data = op == Op::FinalMtx ? find_matching_final_mtx(old_final_mtx_lookup, *new_data) : indexed_old_data; + if (op == Op::FinalMtx) { + store_replacement(old_data != nullptr ? *old_data : *new_data, *new_data, step); + } + } +} + +const Mtx* resolve_replacement(const Mtx* source, Mtx* scratch) { + if (!g_interpolating || source == nullptr) { + return source; + } + + auto it = g_replacements.find(source); + if (it == g_replacements.end()) { + return source; + } + + copy_matrix(it->second.value, *scratch); + return scratch; +} + +bool has_recording_data(const Recording& recording) { + return !recording.root.items.empty() || !recording.root.children.empty(); +} + +void clear_replacements() { + g_replacements.clear(); +} + +} // namespace + +namespace dusk { +namespace frame_interp { + +void ensure_initialized() { + g_enabled = getSettings().game.enableFrameInterpolation; + s_initialized = true; +} + +void begin_record() { + ensure_initialized(); + if (!g_enabled) { + g_interpolating = false; + g_previous_recording = {}; + g_current_recording = {}; + g_current_path.clear(); + clear_replacements(); + return; + } + + g_previous_recording = std::move(g_current_recording); + g_current_recording = {}; + g_current_path.clear(); + g_current_path.push_back(&g_current_recording.root); + g_recording = true; + g_interpolating = false; + clear_replacements(); +} + +void end_record() { + g_recording = false; +} + +void interpolate(float step) { + ensure_initialized(); + clear_replacements(); + g_step = std::clamp(step, 0.0f, 1.0f); + g_interpolating = g_enabled && !g_recording && has_recording_data(g_current_recording); + if (!g_interpolating) { + return; + } + + if (!has_recording_data(g_previous_recording)) { + interpolate_branch(g_current_recording.root, g_current_recording.root, g_step); + return; + } + + interpolate_branch(g_previous_recording.root, g_current_recording.root, g_step); +} + +float get_interpolation_step() { + return g_step; +} + +void notify_sim_tick_complete() { + ensure_initialized(); + g_pending_presentation_ui_ticks++; +} + +uint32_t begin_presentation_ui_pass() { + ensure_initialized(); + g_current_presentation_ui_ticks = g_pending_presentation_ui_ticks; + g_pending_presentation_ui_ticks = 0; + return g_current_presentation_ui_ticks; +} + +uint32_t get_presentation_ui_advance_ticks() { + if (!s_initialized) { + return 0; + } + if (!g_enabled) { + return 1; + } + return g_current_presentation_ui_ticks; +} + +void end_presentation_ui_pass() { + if (!s_initialized) { + return; + } + g_current_presentation_ui_ticks = 0; +} + +void open_child(const void* key, int32_t id) { + if (!s_initialized || !g_recording) { + return; + } + + Label label{key, id}; + auto& siblings = get_child_bucket(*g_current_path.back(), label).nodes; + Data& data = append_op(Op::OpenChild); + data.child_label = label; + data.child_index = siblings.size(); + siblings.emplace_back(std::make_unique()); + g_current_path.push_back(siblings.back().get()); +} + +void close_child() { + if (!s_initialized || !g_recording || g_current_path.size() <= 1) { + return; + } + + g_current_path.pop_back(); +} + +void record_final_mtx_raw(const Mtx* dest, const Mtx src) { + if (!s_initialized || !g_recording || dest == nullptr) { + return; + } + + Data& data = append_op(Op::FinalMtx); + data.dest = dest; + copy_matrix(src, data.matrix); +} + +bool lookup_replacement(const void* source, Mtx out) { + if (!s_initialized || !g_interpolating || source == nullptr) { + return false; + } + + auto it = g_replacements.find(reinterpret_cast(source)); + if (it == g_replacements.end()) { + return false; + } + + copy_matrix(it->second.value, out); + return true; +} + +bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out) { + if (!s_initialized || !g_interpolating || lhs == nullptr || rhs == nullptr) { + return false; + } + + Mtx lhs_scratch; + Mtx rhs_scratch; + const Mtx* resolved_lhs = resolve_replacement(reinterpret_cast(lhs), &lhs_scratch); + const Mtx* resolved_rhs = resolve_replacement(reinterpret_cast(rhs), &rhs_scratch); + if (resolved_lhs == reinterpret_cast(lhs) && + resolved_rhs == reinterpret_cast(rhs)) + { + return false; + } + + concat_matrix(*resolved_lhs, *resolved_rhs, out); + return true; +} + +// TODO: Is there already a built-in function for this? +void camera_eye_from_view_mtx(MtxP view_mtx, cXyz* o_eye) { + o_eye->x = -(view_mtx[0][0] * view_mtx[0][3] + view_mtx[1][0] * view_mtx[1][3] + view_mtx[2][0] * view_mtx[2][3]); + o_eye->y = -(view_mtx[0][1] * view_mtx[0][3] + view_mtx[1][1] * view_mtx[1][3] + view_mtx[2][1] * view_mtx[2][3]); + o_eye->z = -(view_mtx[0][2] * view_mtx[0][3] + view_mtx[1][2] * view_mtx[1][3] + view_mtx[2][2] * view_mtx[2][3]); +} + +} // namespace frame_interp +} // namespace dusk diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 51d5db9c60..6e917761f6 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -206,10 +206,6 @@ namespace dusk { void ImGuiConsole::PreDraw() { ZoneScoped; - if (dusk::IsGameLaunched && !m_isLaunchInitialized) { - m_toasts.emplace_back("Press F1 to toggle menu"s, 5.f); - m_isLaunchInitialized = true; - } UpdateSettings(); @@ -241,6 +237,16 @@ namespace dusk { m_preLaunchWindow.draw(); } + if (!getSettings().backend.wasPresetChosen) { + m_firstRunPreset.draw(); + return; + } + + if (dusk::IsGameLaunched && !m_isLaunchInitialized) { + m_toasts.emplace_back("Press F1 to toggle menu"s, 5.f); + m_isLaunchInitialized = true; + } + m_menuGame.windowControllerConfig(); m_menuGame.windowInputViewer(); if (dusk::IsGameLaunched) { diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index b7a48e8613..0296dc24cc 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -6,6 +6,7 @@ #include #include +#include "ImGuiFirstRunPreset.hpp" #include "ImGuiMenuEnhancements.hpp" #include "ImGuiMenuGame.hpp" #include "ImGuiMenuTools.hpp" @@ -35,6 +36,7 @@ private: bool m_isLaunchInitialized = false; std::deque m_toasts; + ImGuiFirstRunPreset m_firstRunPreset; ImGuiMenuGame m_menuGame; ImGuiMenuEnhancements m_menuEnhancements; ImGuiPreLaunchWindow m_preLaunchWindow; diff --git a/src/dusk/imgui/ImGuiEngine.cpp b/src/dusk/imgui/ImGuiEngine.cpp index 03267df97a..eb7bf4c6bc 100644 --- a/src/dusk/imgui/ImGuiEngine.cpp +++ b/src/dusk/imgui/ImGuiEngine.cpp @@ -144,7 +144,7 @@ void ImGuiEngine_Initialize(float scale) { colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); - colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.60f); } Image GetImage(const std::string& path) { diff --git a/src/dusk/imgui/ImGuiFirstRunPreset.cpp b/src/dusk/imgui/ImGuiFirstRunPreset.cpp new file mode 100644 index 0000000000..be4866fd6a --- /dev/null +++ b/src/dusk/imgui/ImGuiFirstRunPreset.cpp @@ -0,0 +1,132 @@ +#include "ImGuiFirstRunPreset.hpp" + +#include "imgui.h" +#include "ImGuiConsole.hpp" +#include "ImGuiEngine.hpp" +#include "dusk/settings.h" +#include "dusk/config.hpp" +#include + +namespace dusk { + +static void ApplyPresetClassic() { + auto& s = getSettings(); + s.video.lockAspectRatio.setValue(true); + VILockAspectRatio(defaultAspectRatioW, defaultAspectRatioH); +} + +static void ApplyPresetHD() { + auto& s = getSettings(); + s.game.hideTvSettingsScreen.setValue(true); + s.game.noReturnRupees.setValue(true); + s.game.disableRupeeCutscenes.setValue(true); + s.game.noSwordRecoil.setValue(true); + s.game.fastClimbing.setValue(true); + s.game.noMissClimbing.setValue(true); + s.game.fastTears.setValue(true); + s.game.biggerWallets.setValue(true); + s.game.invertCameraXAxis.setValue(true); +} + +static void ApplyPresetDusk() { + ApplyPresetHD(); + + auto& s = getSettings(); + s.game.enableQuickTransform.setValue(true); + s.game.instantSaves.setValue(true); + s.game.midnasLamentNonStop.setValue(true); + s.game.enableFrameInterpolation.setValue(true); +} + +// ========================================================================= + +void ImGuiFirstRunPreset::draw() { + const char* modalTitle = "Welcome to Dusk!"; + + if (m_done) return; + + if (!m_opened) { + ImGui::OpenPopup(modalTitle); + m_opened = true; + } + + const ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); + ImGui::SetNextWindowSize(ImVec2(800.0f * ImGuiScale(), 0.0f), ImGuiCond_Always); + + if (!ImGui::BeginPopupModal(modalTitle, nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) { + // force the user to actually pick one, and not just hit escape to skip the dialog + m_opened = false; + return; + } + + ImGui::TextWrapped("Choose a preset to get started. You can change any setting later from the Enhancements menu."); + ImGui::Spacing(); + ImGui::Separator(); + ImGui::Spacing(); + + int chosen = -1; + + if (ImGui::BeginTable("##presets", 5, ImGuiTableFlags_None)) { + ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed, 16.0f * ImGuiScale()); + ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed, 16.0f * ImGuiScale()); + ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + + ImGui::PushFont(ImGuiEngine::fontLarge); + + ImGui::TableSetColumnIndex(0); + if (ImGui::Button("Classic##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) { + chosen = 0; + } + + ImGui::TableSetColumnIndex(2); + if (ImGui::Button("HD##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) { + chosen = 1; + } + + ImGui::TableSetColumnIndex(4); + if (ImGui::Button("Dusk##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) + { + chosen = 2; + } + + ImGui::PopFont(); + + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + ImGui::Spacing(); + ImGui::TextWrapped("All enhancements disabled to match the GameCube version. Good for speedrunning or simple nostalgia!"); + + ImGui::TableSetColumnIndex(2); + ImGui::Spacing(); + ImGui::TextWrapped("Some enhancements enabled to match the HD version. A good starting point for most players!"); + + ImGui::TableSetColumnIndex(4); + ImGui::Spacing(); + ImGui::TextWrapped("More enhancements enabled than the HD preset. Veteran players will appreciate the additional tweaks!"); + + ImGui::EndTable(); + } + + if (chosen >= 0) { + if (chosen == 0) ApplyPresetClassic(); + if (chosen == 1) ApplyPresetHD(); + if (chosen == 2) ApplyPresetDusk(); + + getSettings().backend.wasPresetChosen.setValue(true); + config::Save(); + + m_done = true; + + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); +} + +} // namespace dusk diff --git a/src/dusk/imgui/ImGuiFirstRunPreset.hpp b/src/dusk/imgui/ImGuiFirstRunPreset.hpp new file mode 100644 index 0000000000..33ba6b3d0f --- /dev/null +++ b/src/dusk/imgui/ImGuiFirstRunPreset.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace dusk { + +class ImGuiFirstRunPreset { +public: + void draw(); + +private: + bool m_opened = false; + bool m_done = false; +}; + +} // namespace dusk diff --git a/src/dusk/imgui/ImGuiMenuEnhancements.cpp b/src/dusk/imgui/ImGuiMenuEnhancements.cpp index c623a910e0..b505d84345 100644 --- a/src/dusk/imgui/ImGuiMenuEnhancements.cpp +++ b/src/dusk/imgui/ImGuiMenuEnhancements.cpp @@ -14,48 +14,48 @@ namespace dusk { config::ImGuiCheckbox("Bigger Wallets", getSettings().game.biggerWallets); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Wallet sizes are like in the HD version (500, 1000, 2000)"); + ImGui::SetTooltip("Wallet sizes are like in the HD version. (500, 1000, 2000)"); } config::ImGuiCheckbox("No Rupee Returns", getSettings().game.noReturnRupees); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full"); + ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full."); } config::ImGuiCheckbox("Disable Rupee Cutscenes", getSettings().game.disableRupeeCutscenes); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Rupees won't play cutscenes after you've collected them the first time"); + ImGui::SetTooltip("Rupees won't play cutscenes after you've collected them the first time."); } config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Link won't recoil when his sword hits walls"); + ImGui::SetTooltip("Link won't recoil when his sword hits walls."); } config::ImGuiCheckbox("Faster Climbing", getSettings().game.fastClimbing); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version"); + ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version."); } config::ImGuiCheckbox("No Climbing Miss Animation", getSettings().game.noMissClimbing); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Prevents Link from playing a struggle animation\n" - "when using the Clawshot on vines at a weird angle"); + "when grabbing ledges or climbing on vines."); } config::ImGuiCheckbox("Faster Tears of Light", getSettings().game.fastTears); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version"); + ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version."); } config::ImGuiCheckbox("Hide TV Settings Screen", getSettings().game.hideTvSettingsScreen); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Hides the TV calibration screen shown when loading a save"); + ImGui::SetTooltip("Hides the TV calibration screen shown when loading a save."); } - + config::ImGuiCheckbox("Instant Saves", getSettings().game.instantSaves); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Skip the delay when writing to the Memory Card"); + ImGui::SetTooltip("Skip the delay when writing to the Memory Card."); } ImGui::EndMenu(); @@ -64,7 +64,7 @@ namespace dusk { if (ImGui::BeginMenu("Preferences")) { config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Mirrors the world, matching the Wii version of the game"); + ImGui::SetTooltip("Mirrors the world, matching the Wii version of the game."); } config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis); @@ -73,12 +73,21 @@ namespace dusk { } if (ImGui::BeginMenu("Graphics")) { - config::ImGuiCheckbox("Native Bloom", getSettings().game.enableBloom); + config::ImGuiCheckbox("Unlock Framerate", getSettings().game.enableFrameInterpolation); + const bool frameInterpolationHovered = ImGui::IsItemHovered(); - config::ImGuiCheckbox("Water Projection Offset", getSettings().game.useWaterProjectionOffset); + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.72f, 0.2f, 1.0f)); + ImGui::TextUnformatted("[EXPERIMENTAL]"); + ImGui::PopStyleColor(); + + if (frameInterpolationHovered || ImGui::IsItemHovered()) { + ImGui::SetTooltip("Uses inter-frame interpolation to enable higher frame rates.\nVisual artifacts, animation glitches, or instability may occur."); + } + + config::ImGuiSliderInt("Shadow Resolution", getSettings().game.shadowResolutionMultiplier, 1, 8, "x%d"); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Adds GC-specific -0.01 transS offset\n" - "that causes ~6px ghost artifacts in water reflections"); + ImGui::SetTooltip("Improves the shadow resolution, making them higher quality."); } ImGui::EndMenu(); @@ -87,12 +96,12 @@ namespace dusk { if (ImGui::BeginMenu("Audio")) { config::ImGuiCheckbox("No Low HP Sound", getSettings().game.noLowHpSound); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Disable the beeping sound when having low health"); + ImGui::SetTooltip("Disable the beeping sound when having low health."); } config::ImGuiCheckbox("Non-Stop Midna's Lament", getSettings().game.midnasLamentNonStop); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Prevents enemy music while Midna's Lament is playing"); + ImGui::SetTooltip("Prevents enemy music while Midna's Lament is playing."); } ImGui::EndMenu(); @@ -103,7 +112,7 @@ namespace dusk { config::ImGuiCheckbox("Can Transform Anywhere", getSettings().game.canTransformAnywhere); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Allows you to transform even if NPCs are looking"); + ImGui::SetTooltip("Allows you to transform even if NPCs are looking."); } config::ImGuiCheckbox("Fast Spinner", getSettings().game.fastSpinner); @@ -124,7 +133,7 @@ namespace dusk { config::ImGuiCheckbox("Instant Death", getSettings().game.instantDeath); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Any hit will instantly kill you"); + ImGui::SetTooltip("Any hit will instantly kill you."); } ImGui::EndMenu(); @@ -134,7 +143,7 @@ namespace dusk { config::ImGuiCheckbox("Restore Wii 1.0 Glitches", getSettings().game.restoreWiiGlitches); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n" - "the first released version"); + "the first released version."); } ImGui::EndMenu(); @@ -142,6 +151,10 @@ namespace dusk { if (ImGui::BeginMenu("Tools")) { config::ImGuiCheckbox("Enable Turbo Key", getSettings().game.enableTurboKeybind); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Holding TAB will speed up the game.\n" + "This will not work with the \"Unlock Framerate\" enhancement."); + } ImGui::EndMenu(); } diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 494dbe1c22..d2b893c884 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -62,6 +62,10 @@ namespace dusk { config::Save(); } + config::ImGuiCheckbox("Native Bloom", getSettings().game.enableBloom); + + config::ImGuiCheckbox("Enable Water Refraction", getSettings().game.enableWaterRefraction); + ImGui::EndMenu(); } diff --git a/src/dusk/imgui/ImGuiSaveEditor.cpp b/src/dusk/imgui/ImGuiSaveEditor.cpp index e461545e33..fd45789ccf 100644 --- a/src/dusk/imgui/ImGuiSaveEditor.cpp +++ b/src/dusk/imgui/ImGuiSaveEditor.cpp @@ -7,6 +7,8 @@ #include "d/d_com_inf_game.h" #include "d/d_item_data.h" +#include "d/d_meter2_info.h" +#include "d/d_save.h" #include @@ -280,6 +282,132 @@ namespace dusk { { dItemNo_NONE_e, {"None"} }, }; + static constexpr int BUG_SPECIES_COUNT = 12; + + static const u8 sBugItemIds[BUG_SPECIES_COUNT * 2] = { + dItemNo_M_ANT_e, dItemNo_F_ANT_e, + dItemNo_M_MAYFLY_e, dItemNo_F_MAYFLY_e, + dItemNo_M_BEETLE_e, dItemNo_F_BEETLE_e, + dItemNo_M_MANTIS_e, dItemNo_F_MANTIS_e, + dItemNo_M_STAG_BEETLE_e, dItemNo_F_STAG_BEETLE_e, + dItemNo_M_DANGOMUSHI_e, dItemNo_F_DANGOMUSHI_e, + dItemNo_M_BUTTERFLY_e, dItemNo_F_BUTTERFLY_e, + dItemNo_M_LADYBUG_e, dItemNo_F_LADYBUG_e, + dItemNo_M_SNAIL_e, dItemNo_F_SNAIL_e, + dItemNo_M_NANAFUSHI_e, dItemNo_F_NANAFUSHI_e, + dItemNo_M_GRASSHOPPER_e, dItemNo_F_GRASSHOPPER_e, + dItemNo_M_DRAGONFLY_e, dItemNo_F_DRAGONFLY_e, + }; + + static const u16 sBugTurnInFlags[BUG_SPECIES_COUNT * 2] = { + dSv_event_flag_c::F_0421, dSv_event_flag_c::F_0422, + dSv_event_flag_c::F_0423, dSv_event_flag_c::F_0424, + dSv_event_flag_c::F_0401, dSv_event_flag_c::F_0402, + dSv_event_flag_c::F_0413, dSv_event_flag_c::F_0414, + dSv_event_flag_c::F_0405, dSv_event_flag_c::F_0406, + dSv_event_flag_c::F_0411, dSv_event_flag_c::F_0412, + dSv_event_flag_c::F_0403, dSv_event_flag_c::F_0404, + dSv_event_flag_c::F_0415, dSv_event_flag_c::F_0416, + dSv_event_flag_c::F_0417, dSv_event_flag_c::F_0418, + dSv_event_flag_c::F_0409, dSv_event_flag_c::F_0410, + dSv_event_flag_c::F_0407, dSv_event_flag_c::F_0408, + dSv_event_flag_c::F_0419, dSv_event_flag_c::F_0420, + }; + + static const char* sBugSpeciesNames[BUG_SPECIES_COUNT] = { + "Ant", "Dayfly", "Beetle", "Mantis", + "Stag Beetle", "Pill Bug", "Butterfly", "Ladybug", + "Snail", "Phasmid", "Grasshopper", "Dragonfly", + }; + + static constexpr int HIDDEN_SKILL_COUNT = 7; + + static const u16 sHiddenSkillFlags[HIDDEN_SKILL_COUNT] = { + dSv_event_flag_c::F_0339, dSv_event_flag_c::F_0338, + dSv_event_flag_c::F_0340, dSv_event_flag_c::F_0341, + dSv_event_flag_c::F_0342, dSv_event_flag_c::F_0343, + dSv_event_flag_c::F_0344, + }; + + static const char* sHiddenSkillNames[HIDDEN_SKILL_COUNT] = { + "Ending Blow", "Shield Attack", "Back Slice", "Helm Splitter", + "Mortal Draw", "Jump Strike", "Great Spin", + }; + + static constexpr int LETTER_COUNT = 16; + + static const char* sLetterSenders[LETTER_COUNT] = { + "Renado", "Ooccoo 1", "Ooccoo 2", "The Postman", + "Kakariko Goods", "Barnes 1", "Barnes 2", "Barnes Bombs", + "Malo Mart", "Telma", "Purlo", "From Jr.", + "Princess Agitha", "Lanayru Tourism", "Shad", "Yeta", + }; + + static constexpr int FISH_COUNT = 6; + + static const struct { + u8 index; + const char* name; + } sFishSpecies[FISH_COUNT] = { + { 3, "Ordon Catfish" }, + { 5, "Greengill" }, + { 4, "Reekfish" }, + { 0, "Hyrule Bass" }, + { 2, "Hylian Pike" }, + { 1, "Hylian Loach" }, + }; + + static const char* sSwordNames[4] = { + "Ordon Sword", "Master Sword", "Wooden Sword", "Light Sword", + }; + + static const char* sShieldNames[3] = { + "Wooden Shield", "Ordon Shield", "Hylian Shield", + }; + + static const char* sFusedShadowNames[3] = { + "Forest Temple", + "Goron Mines", + "Lakebed Temple", + }; + + static const struct { + u8 index; + const char* name; + } sMirrorShards[3] = { + { 1, "Snowpeak Ruins" }, + { 2, "Temple of Time" }, + { 3, "City in the Sky" }, + }; + + static const struct { + u8 slot; + u8 item; + } sDefaultInventory[] = { + { SLOT_0, dItemNo_BOOMERANG_e }, + { SLOT_1, dItemNo_KANTERA_e }, + { SLOT_2, dItemNo_SPINNER_e }, + { SLOT_3, dItemNo_HVY_BOOTS_e }, + { SLOT_4, dItemNo_BOW_e }, + { SLOT_5, dItemNo_HAWK_EYE_e }, + { SLOT_6, dItemNo_IRONBALL_e }, + { SLOT_8, dItemNo_COPY_ROD_e }, + { SLOT_9, dItemNo_HOOKSHOT_e }, + { SLOT_10, dItemNo_W_HOOKSHOT_e }, + { SLOT_11, dItemNo_EMPTY_BOTTLE_e }, + { SLOT_12, dItemNo_EMPTY_BOTTLE_e }, + { SLOT_13, dItemNo_EMPTY_BOTTLE_e }, + { SLOT_14, dItemNo_EMPTY_BOTTLE_e }, + { SLOT_15, dItemNo_NORMAL_BOMB_e }, + { SLOT_16, dItemNo_WATER_BOMB_e }, + { SLOT_17, dItemNo_POKE_BOMB_e }, + { SLOT_18, dItemNo_DUNGEON_EXIT_e }, + { SLOT_20, dItemNo_FISHING_ROD_1_e}, + { SLOT_21, dItemNo_HORSE_FLUTE_e }, + { SLOT_22, dItemNo_ANCIENT_DOCUMENT_e }, + { SLOT_23, dItemNo_PACHINKO_e }, + }; + ImGuiSaveEditor::ImGuiSaveEditor() {} void ImGuiSaveEditor::draw(bool& open) { @@ -307,7 +435,7 @@ namespace dusk { } if (ImGui::BeginTabItem("Collection")) { - //DrawFlagsTab(); + drawCollectionTab(); ImGui::EndTabItem(); } @@ -675,9 +803,30 @@ namespace dusk { } } + static u8 getSlotDefault(int slot) { + for (size_t i = 0; i < sizeof(sDefaultInventory) / sizeof(sDefaultInventory[0]); i++) { + if (sDefaultInventory[i].slot == slot) { + return sDefaultInventory[i].item; + } + } + return dItemNo_NONE_e; + } + void ImGuiSaveEditor::drawInventoryTab() { dSv_player_item_c& item = dComIfGs_getSaveData()->getPlayer().getItem(); + if (ImGui::Button("Default All##inv_default_all")) { + for (int slot = 0; slot < 24; slot++) { + dComIfGs_setItem(slot, getSlotDefault(slot)); + } + } + ImGui::SameLine(); + if (ImGui::Button("Clear All##inv_clear_all")) { + for (int slot = 0; slot < 24; slot++) { + dComIfGs_setItem(slot, dItemNo_NONE_e); + } + } + ImGuiBeginGroupPanel("Items", { 200, 100 }); for (int slot = 0; slot < 24; slot++) { ImGui::Text("Slot %02d: ", slot); @@ -696,12 +845,369 @@ namespace dusk { } ImGui::EndCombo(); } + ImGui::SameLine(); + if (ImGui::SmallButton(fmt::format("Default##slot_d_{}", slot).c_str())) { + dComIfGs_setItem(slot, getSlotDefault(slot)); + } + ImGui::SameLine(); + if (ImGui::SmallButton(fmt::format("Clear##slot_c_{}", slot).c_str())) { + dComIfGs_setItem(slot, dItemNo_NONE_e); + } } ImGuiEndGroupPanel(); } + static inline void setItemFirstBit(u8 itemNo, bool owned) { + if (owned) { + dComIfGs_onItemFirstBit(itemNo); + } else { + dComIfGs_offItemFirstBit(itemNo); + } + } + + static inline void setEventBit(u16 flag, bool on) { + if (on) { + dComIfGs_onEventBit(flag); + } else { + dComIfGs_offEventBit(flag); + } + } + + static void setLetterGetFlag(int idx, bool received) { + dSv_letter_info_c& info = dComIfGs_getSaveData()->getPlayer().getLetterInfo(); + if (received) { + if (dComIfGs_isLetterGetFlag(idx)) return; + dComIfGs_onLetterGetFlag(idx); + u8 slot = dMeter2Info_getRecieveLetterNum() - 1; + if (slot < 64) { + dComIfGs_setGetNumber(slot, (u8)(idx + 1)); + } + } else { + if (!dComIfGs_isLetterGetFlag(idx)) return; + info.mLetterGetFlags[idx >> 5] &= ~(1u << (idx & 0x1F)); + for (int j = 0; j < 64; j++) { + if (dComIfGs_getGetNumber(j) == idx + 1) { + for (int k = j; k < 63; k++) { + dComIfGs_setGetNumber(k, dComIfGs_getGetNumber(k + 1)); + } + dComIfGs_setGetNumber(63, 0); + break; + } + } + } + } + + void ImGuiSaveEditor::drawCollectionTab() { + if (ImGui::TreeNode("Equipment")) { + if (ImGui::TreeNode("Swords")) { + for (int i = 0; i < 4; i++) { + bool got = dComIfGs_isCollectSword((u8)i) != 0; + if (ImGui::Checkbox(fmt::format("{0}##sword_{1}", sSwordNames[i], i).c_str(), &got)) { + if (got) dComIfGs_setCollectSword((u8)i); + else dComIfGs_offCollectSword((u8)i); + } + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Shields")) { + for (int i = 0; i < 3; i++) { + bool got = dComIfGs_isCollectShield((u8)i) != 0; + if (ImGui::Checkbox(fmt::format("{0}##shield_{1}", sShieldNames[i], i).c_str(), &got)) { + if (got) dComIfGs_setCollectShield((u8)i); + else dComIfGs_offCollectShield((u8)i); + } + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Tunics")) { + bool ordonClothes = dComIfGs_isItemFirstBit(dItemNo_WEAR_CASUAL_e) != 0; + if (ImGui::Checkbox("Ordon Clothes##tunic_ordon", &ordonClothes)) { + setItemFirstBit(dItemNo_WEAR_CASUAL_e, ordonClothes); + } + + bool greenTunic = dComIfGs_isCollectClothes(KOKIRI_CLOTHES_FLAG) != 0; + if (ImGui::Checkbox("Hero's Clothes##tunic_green", &greenTunic)) { + if (greenTunic) dComIfGs_setCollectClothes(KOKIRI_CLOTHES_FLAG); + else dComIfGs_offCollectClothes(KOKIRI_CLOTHES_FLAG); + } + + bool zoraArmor = dComIfGs_isItemFirstBit(dItemNo_WEAR_ZORA_e) != 0; + if (ImGui::Checkbox("Zora Armor##tunic_zora", &zoraArmor)) { + setItemFirstBit(dItemNo_WEAR_ZORA_e, zoraArmor); + } + + bool magicArmor = dComIfGs_isItemFirstBit(dItemNo_ARMOR_e) != 0; + if (ImGui::Checkbox("Magic Armor##tunic_magic", &magicArmor)) { + setItemFirstBit(dItemNo_ARMOR_e, magicArmor); + } + ImGui::TreePop(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Key Items")) { + if (ImGui::TreeNode("Fused Shadows")) { + for (int i = 0; i < 3; i++) { + bool got = dComIfGs_isCollectCrystal((u8)i) != 0; + if (ImGui::Checkbox( + fmt::format("{0}##fs_{1}", sFusedShadowNames[i], i).c_str(), &got)) { + if (got) dComIfGs_onCollectCrystal((u8)i); + else dComIfGs_offCollectCrystal((u8)i); + } + } + ImGui::Spacing(); + if (ImGui::Button("All##fs_all")) { + for (int i = 0; i < 3; i++) dComIfGs_onCollectCrystal((u8)i); + } + ImGui::SameLine(); + if (ImGui::Button("None##fs_clear")) { + for (int i = 0; i < 3; i++) dComIfGs_offCollectCrystal((u8)i); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Mirror Shards")) { + for (int i = 0; i < 3; i++) { + u8 idx = sMirrorShards[i].index; + bool got = dComIfGs_isCollectMirror(idx) != 0; + if (ImGui::Checkbox( + fmt::format("{0}##ms_{1}", sMirrorShards[i].name, i).c_str(), &got)) { + if (got) dComIfGs_onCollectMirror(idx); + else dComIfGs_offCollectMirror(idx); + } + } + ImGui::Spacing(); + if (ImGui::Button("All##ms_all")) { + for (int i = 0; i < 3; i++) dComIfGs_onCollectMirror(sMirrorShards[i].index); + } + ImGui::SameLine(); + if (ImGui::Button("None##ms_clear")) { + for (int i = 0; i < 3; i++) dComIfGs_offCollectMirror(sMirrorShards[i].index); + } + ImGui::TreePop(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Heart Pieces & Poe Souls")) { + if (ImGui::TreeNode("Poe Souls")) { + int poeCount = dComIfGs_getPohSpiritNum(); + ImGui::Text("Collected:"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(100.0f); + if (ImGui::InputInt("##poe_count", &poeCount)) { + if (poeCount < 0) poeCount = 0; + if (poeCount > 60) poeCount = 60; + dComIfGs_setPohSpiritNum((u8)poeCount); + } + ImGui::SameLine(); + ImGui::TextDisabled("/ 60"); + ImGui::Spacing(); + if (ImGui::Button("All 60##poe_all")) { + dComIfGs_setPohSpiritNum(60); + } + ImGui::SameLine(); + if (ImGui::Button("Clear##poe_clear")) { + dComIfGs_setPohSpiritNum(0); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Heart Pieces")) { + int maxLife = dComIfGs_getMaxLife(); + int hearts = maxLife / 5; + int pieces = maxLife % 5; + ImGui::Text("Max Life:"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(160.0f); + if (ImGui::InputInt("##max_life", &maxLife, 1, 5)) { + if (maxLife < 15) maxLife = 15; + if (maxLife > 100) maxLife = 100; + dComIfGs_setMaxLife((u8)maxLife); + u16 maxHealth = (dComIfGs_getMaxLife() / 5) * 4; + if (dComIfGs_getLife() > maxHealth) { + dComIfGs_setLife(maxHealth); + } + } + ImGui::SameLine(); + ImGui::TextDisabled("(%d hearts + %d pieces)", hearts, pieces); + ImGui::Spacing(); + if (ImGui::Button("3 Hearts##hp_min")) { + dComIfGs_setMaxLife(15); + dComIfGs_setLife(12); + } + ImGui::SameLine(); + if (ImGui::Button("20 Hearts##hp_max")) { + dComIfGs_setMaxLife(100); + dComIfGs_setLife(80); + } + ImGui::TreePop(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Golden Bugs")) { + if (ImGui::BeginTable("GoldenBugTable", 5, + ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) { + ImGui::TableSetupColumn("Species"); + ImGui::TableSetupColumn("Male"); + ImGui::TableSetupColumn("Female"); + ImGui::TableSetupColumn("M \xe2\x86\x92 Agitha"); // M → Agitha + ImGui::TableSetupColumn("F \xe2\x86\x92 Agitha"); // F → Agitha + ImGui::TableHeadersRow(); + + for (int species = 0; species < BUG_SPECIES_COUNT; species++) { + int maleIdx = species * 2; + int femaleIdx = species * 2 + 1; + u8 maleItem = sBugItemIds[maleIdx]; + u8 femaleItem = sBugItemIds[femaleIdx]; + u16 maleFlag = sBugTurnInFlags[maleIdx]; + u16 femaleFlag = sBugTurnInFlags[femaleIdx]; + + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted(sBugSpeciesNames[species]); + + ImGui::TableSetColumnIndex(1); + bool maleOwned = dComIfGs_isItemFirstBit(maleItem) != 0; + if (ImGui::Checkbox(fmt::format("##bugM_own_{}", species).c_str(), &maleOwned)) { + setItemFirstBit(maleItem, maleOwned); + } + + ImGui::TableSetColumnIndex(2); + bool femaleOwned = dComIfGs_isItemFirstBit(femaleItem) != 0; + if (ImGui::Checkbox(fmt::format("##bugF_own_{}", species).c_str(), &femaleOwned)) { + setItemFirstBit(femaleItem, femaleOwned); + } + + ImGui::TableSetColumnIndex(3); + bool maleGiven = dComIfGs_isEventBit(maleFlag) != 0; + if (ImGui::Checkbox(fmt::format("##bugM_giv_{}", species).c_str(), &maleGiven)) { + setEventBit(maleFlag, maleGiven); + } + + ImGui::TableSetColumnIndex(4); + bool femaleGiven = dComIfGs_isEventBit(femaleFlag) != 0; + if (ImGui::Checkbox(fmt::format("##bugF_giv_{}", species).c_str(), &femaleGiven)) { + setEventBit(femaleFlag, femaleGiven); + } + } + + ImGui::EndTable(); + } + + ImGui::Spacing(); + if (ImGui::Button("Collect All##bugs_all")) { + for (int i = 0; i < BUG_SPECIES_COUNT * 2; i++) { + dComIfGs_onItemFirstBit(sBugItemIds[i]); + } + } + ImGui::SameLine(); + if (ImGui::Button("Clear All##bugs_clear")) { + for (int i = 0; i < BUG_SPECIES_COUNT * 2; i++) { + dComIfGs_offItemFirstBit(sBugItemIds[i]); + dComIfGs_offEventBit(sBugTurnInFlags[i]); + } + } + ImGui::SameLine(); + if (ImGui::Button("Give All to Agitha##bugs_giveall")) { + for (int i = 0; i < BUG_SPECIES_COUNT * 2; i++) { + dComIfGs_onEventBit(sBugTurnInFlags[i]); + } + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Hidden Skills")) { + for (int i = 0; i < HIDDEN_SKILL_COUNT; i++) { + bool learned = dComIfGs_isEventBit(sHiddenSkillFlags[i]) != 0; + if (ImGui::Checkbox( + fmt::format("{0}##skill_{1}", sHiddenSkillNames[i], i).c_str(), &learned)) { + setEventBit(sHiddenSkillFlags[i], learned); + } + } + ImGui::Spacing(); + if (ImGui::Button("Learn All##skills_all")) { + for (int i = 0; i < HIDDEN_SKILL_COUNT; i++) { + dComIfGs_onEventBit(sHiddenSkillFlags[i]); + } + } + ImGui::SameLine(); + if (ImGui::Button("Forget All##skills_clear")) { + for (int i = 0; i < HIDDEN_SKILL_COUNT; i++) { + dComIfGs_offEventBit(sHiddenSkillFlags[i]); + } + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Collection Logs")) { + if (ImGui::TreeNode("Postman Letters")) { + for (int i = 0; i < LETTER_COUNT; i++) { + bool had = dComIfGs_isLetterGetFlag(i) != 0; + if (ImGui::Checkbox( + fmt::format("{0}##letter_{1}", sLetterSenders[i], i).c_str(), &had)) { + setLetterGetFlag(i, had); + } + } + ImGui::Spacing(); + if (ImGui::Button("Receive All##letters_all")) { + for (int i = 0; i < LETTER_COUNT; i++) setLetterGetFlag(i, true); + } + ImGui::SameLine(); + if (ImGui::Button("Clear All##letters_clear")) { + for (int i = 0; i < LETTER_COUNT; i++) setLetterGetFlag(i, false); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Fishing Log")) { + dSv_fishing_info_c& fish = dComIfGs_getSaveData()->getPlayer().getFishingInfo(); + if (ImGui::BeginTable("FishTable", 3, + ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) { + ImGui::TableSetupColumn("Species"); + ImGui::TableSetupColumn("Caught"); + ImGui::TableSetupColumn("Biggest (cm)"); + ImGui::TableHeadersRow(); + + for (int i = 0; i < FISH_COUNT; i++) { + u8 idx = sFishSpecies[i].index; + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted(sFishSpecies[i].name); + + ImGui::TableSetColumnIndex(1); + int count = dComIfGs_getFishNum(idx); + ImGui::SetNextItemWidth(100.0f); + if (ImGui::InputInt(fmt::format("##fish_c_{}", i).c_str(), &count, 1, 10)) { + if (count < 0) count = 0; + if (count > 999) count = 999; + fish.mFishCount[idx] = (u16)count; + } + + ImGui::TableSetColumnIndex(2); + int size = dComIfGs_getFishSize(idx); + ImGui::SetNextItemWidth(100.0f); + if (ImGui::InputInt(fmt::format("##fish_s_{}", i).c_str(), &size, 1, 10)) { + if (size < 0) size = 0; + if (size > 255) size = 255; + dComIfGs_setFishSize(idx, (u8)size); + } + } + ImGui::EndTable(); + } + ImGui::TreePop(); + } + ImGui::TreePop(); + } + } + void drawFlagList(const char* id, BE(u32)& flags) { u32 tempFlagField = flags; @@ -845,5 +1351,19 @@ namespace dusk { if (ImGui::BeginCombo("Target Type", "Hold")) { ImGui::EndCombo(); } + + static const char* kSoundModeNames[] = { "Mono", "Stereo", "Surround" }; + const char* current = (config.mSoundMode < 3) ? kSoundModeNames[config.mSoundMode] + : "Unknown"; + if (ImGui::BeginCombo("Sound", current)) { + for (u8 i = 0; i < 3; i++) { + bool selected = (config.mSoundMode == i); + if (ImGui::Selectable(kSoundModeNames[i], selected)) { + config.mSoundMode = i; + } + if (selected) ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } } } \ No newline at end of file diff --git a/src/dusk/imgui/ImGuiSaveEditor.hpp b/src/dusk/imgui/ImGuiSaveEditor.hpp index 3afc1b8145..4b211311b0 100644 --- a/src/dusk/imgui/ImGuiSaveEditor.hpp +++ b/src/dusk/imgui/ImGuiSaveEditor.hpp @@ -15,6 +15,7 @@ namespace dusk { void drawPlayerStatusTab(); void drawLocationTab(); void drawInventoryTab(); + void drawCollectionTab(); void drawFlagsTab(); void drawConfigTab(); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index a8a6c26522..dabed72f19 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -40,7 +40,9 @@ UserSettings g_userSettings = { // Graphics .enableBloom {"game.enableBloom", true}, - .useWaterProjectionOffset {"game.useWaterProjectionOffset", false}, + .enableWaterRefraction {"game.enableWaterRefraction", true}, + .enableFrameInterpolation = {"game.enableFrameInterpolation", false}, + .shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1}, // Audio .noLowHpSound {"game.noLowHpSound", false}, @@ -56,14 +58,15 @@ UserSettings g_userSettings = { .restoreWiiGlitches {"game.restoreWiiGlitches", false}, // Controls - .enableTurboKeybind {"game.enableTurboKeybind", true}, + .enableTurboKeybind {"game.enableTurboKeybind", false}, }, .backend = { .isoPath {"backend.isoPath", ""}, .graphicsBackend {"backend.graphicsBackend", "auto"}, .skipPreLaunchUI {"backend.skipPreLaunchUI", false}, - .showPipelineCompilation{"backend.showPipelineCompilation", false} + .showPipelineCompilation {"backend.showPipelineCompilation", false}, + .wasPresetChosen {"backend.wasPresetChosen", false} } }; @@ -100,7 +103,8 @@ void registerSettings() { Register(g_userSettings.game.enableMirrorMode); Register(g_userSettings.game.invertCameraXAxis); Register(g_userSettings.game.enableBloom); - Register(g_userSettings.game.useWaterProjectionOffset); + Register(g_userSettings.game.enableWaterRefraction); + Register(g_userSettings.game.shadowResolutionMultiplier); Register(g_userSettings.game.enableFastIronBoots); Register(g_userSettings.game.canTransformAnywhere); Register(g_userSettings.game.freeMagicArmor); @@ -110,11 +114,13 @@ void registerSettings() { Register(g_userSettings.game.midnasLamentNonStop); Register(g_userSettings.game.enableTurboKeybind); Register(g_userSettings.game.fastSpinner); + Register(g_userSettings.game.enableFrameInterpolation); Register(g_userSettings.backend.isoPath); Register(g_userSettings.backend.graphicsBackend); Register(g_userSettings.backend.skipPreLaunchUI); Register(g_userSettings.backend.showPipelineCompilation); + Register(g_userSettings.backend.wasPresetChosen); } // Transient settings diff --git a/src/f_ap/f_ap_game.cpp b/src/f_ap/f_ap_game.cpp index 180e6bc361..8e2b0a367c 100644 --- a/src/f_ap/f_ap_game.cpp +++ b/src/f_ap/f_ap_game.cpp @@ -14,6 +14,7 @@ #include "d/actor/d_a_midna.h" #include "d/d_model.h" #include "d/d_tresure.h" +#include "dusk/frame_interpolation.h" #include "dusk/logging.h" #include "f_op/f_op_camera_mng.h" #include "f_op/f_op_draw_tag.h" @@ -722,6 +723,17 @@ void fapGm_After() { fopCamM_Management(); } +#ifdef TARGET_PC +static void fapGm_Before() { + dusk::frame_interp::begin_record(); +} + +static void fapGm_AfterRecord() { + dusk::frame_interp::end_record(); + fapGm_After(); +} +#endif + void fapGm_Execute() { ZoneScoped; static u32 sExecCount = 0; @@ -752,7 +764,11 @@ void fapGm_Execute() { } #endif - fpcM_Management(NULL, fapGm_After); +#ifdef TARGET_PC + fpcM_Management(fapGm_Before, fapGm_AfterRecord); +#else + fpcM_ManagementFunc(NULL, fapGm_After); +#endif cCt_Counter(0); } diff --git a/src/f_pc/f_pc_draw.cpp b/src/f_pc/f_pc_draw.cpp index 0677a47827..697d19cf29 100644 --- a/src/f_pc/f_pc_draw.cpp +++ b/src/f_pc/f_pc_draw.cpp @@ -8,6 +8,7 @@ #include "f_pc/f_pc_leaf.h" #include "f_pc/f_pc_node.h" #include "f_pc/f_pc_pause.h" +#include "dusk/frame_interpolation.h" #include #include "dusk/logging.h" @@ -25,7 +26,13 @@ int fpcDw_Execute(base_process_class* i_proc) { } fpcLy_SetCurrentLayer(i_proc->layer_tag.layer); +#ifdef TARGET_PC + dusk::frame_interp::open_child(i_proc, 0); +#endif ret = draw_func(i_proc); +#ifdef TARGET_PC + dusk::frame_interp::close_child(); +#endif fpcLy_SetCurrentLayer(save_layer); return ret; } diff --git a/src/f_pc/f_pc_manager.cpp b/src/f_pc/f_pc_manager.cpp index a3d85d4df6..03319db3d8 100644 --- a/src/f_pc/f_pc_manager.cpp +++ b/src/f_pc/f_pc_manager.cpp @@ -20,7 +20,6 @@ #include "f_pc/f_pc_pause.h" #include "f_pc/f_pc_priority.h" #include "m_Do/m_Do_controller_pad.h" -#include #include "tracy/Tracy.hpp" @@ -64,7 +63,14 @@ void fpcM_Management(fpcM_ManagementFunc i_preExecuteFn, fpcM_ManagementFunc i_p l_dvdError = false; } - cAPIGph_Painter(); +#ifdef TARGET_PC + // Frame interpolation: call moved to m_Do_main + if (!dusk::getSettings().game.enableFrameInterpolation) { +#endif + cAPIGph_Painter(); +#ifdef TARGET_PC + } +#endif if (!dPa_control_c::isStatus(1)) { fpcDt_Handler(); @@ -153,4 +159,3 @@ void* fpcM_JudgeInLayer(fpc_ProcID i_layerID, fpcCtIt_JudgeFunc i_judgeFunc, voi return NULL; } - diff --git a/src/m_Do/m_Do_ext.cpp b/src/m_Do/m_Do_ext.cpp index 0593815b1d..577ada0837 100644 --- a/src/m_Do/m_Do_ext.cpp +++ b/src/m_Do/m_Do_ext.cpp @@ -2398,7 +2398,12 @@ void mDoExt_3DlineMat0_c::draw() { var_r28++; } - field_0x16 ^= (u8)1; +#if TARGET_PC + if (!dusk::getSettings().game.enableFrameInterpolation) +#endif + { + field_0x16 ^= (u8)1; + } } void mDoExt_3DlineMat0_c::update(int param_0, f32 param_1, GXColor& param_2, u16 param_3, @@ -2648,6 +2653,9 @@ int mDoExt_3DlineMat1_c::init(u16 param_0, u16 param_1, ResTIMG* param_2, int pa field_0x4 = 0; mIsDrawn = 0; +#if TARGET_PC + mInterpLineKind = 0; +#endif GXInitTexObj(&mTextureObject, (void*)((intptr_t)param_2 + param_2->imageOffset), param_2->width, param_2->height, (GXTexFmt)param_2->format, (GXTexWrapMode)param_2->wrapS, @@ -2720,11 +2728,19 @@ void mDoExt_3DlineMat1_c::draw() { lines++; } GXSetTexCoordScaleManually(GX_TEXCOORD0, 0, 0, 0); - mIsDrawn ^= (u8)1; +#if TARGET_PC + if (!dusk::getSettings().game.enableFrameInterpolation) +#endif + { + mIsDrawn ^= (u8)1; + } } -void mDoExt_3DlineMat1_c::update(int param_0, f32 param_1, GXColor& param_2, u16 param_3, - dKy_tevstr_c* param_4) { +#if TARGET_PC +void mDoExt_3DlineMat1_c::update(int param_0, f32 param_1, GXColor& param_2, u16 param_3, dKy_tevstr_c* param_4, const cXyz* presentationEye) { +#else +void mDoExt_3DlineMat1_c::update(int param_0, f32 param_1, GXColor& param_2, u16 param_3, dKy_tevstr_c* param_4) { +#endif mColor = param_2; this->mpTevStr = param_4; if (param_0 < 0) { @@ -2734,6 +2750,13 @@ void mDoExt_3DlineMat1_c::update(int param_0, f32 param_1, GXColor& param_2, u16 } else { field_0x34 = param_0; } + +#if TARGET_PC + mInterpLineKind = 1; + mInterpLineF = param_1; + mInterpLineU16 = param_3; +#endif + view_class* sp_3c = dComIfGd_getView(); mDoExt_3Dline_c* sp_38 = mpLines; f32 local_f27 = param_3 != 0 ? param_1 / param_3 : 0.0f; @@ -2787,7 +2810,12 @@ void mDoExt_3DlineMat1_c::update(int param_0, f32 param_1, GXColor& param_2, u16 local_f31 += local_f30 * 0.02f * (8.0f / param_1); } +#if TARGET_PC + const cXyz& lineEye = (presentationEye != nullptr && dusk::getSettings().game.enableFrameInterpolation) ? *presentationEye : sp_3c->lookat.eye; + sp_13c = *local_r27 - lineEye; +#else sp_13c = *local_r27 - sp_3c->lookat.eye; +#endif sp_130 = sp_130.outprod(sp_13c); sp_130.normalizeZP(); @@ -2822,7 +2850,11 @@ void mDoExt_3DlineMat1_c::update(int param_0, f32 param_1, GXColor& param_2, u16 local_f31 += local_f30 * 0.02f * (8.0f / param_1); } +#if TARGET_PC + sp_13c = local_r27[0] - lineEye; +#else sp_13c = local_r27[0] - sp_3c->lookat.eye; +#endif sp_130 = sp_130.outprod(sp_13c); sp_130.normalizeZP(); @@ -2894,7 +2926,11 @@ void mDoExt_3DlineMat2_c::setMaterial() { GXLoadNrmMtxImm(cMtx_getIdentity(), 0); } -void mDoExt_3DlineMat1_c::update(int param_0, GXColor& param_2, dKy_tevstr_c* param_4) { +#if TARGET_PC +void mDoExt_3DlineMat1_c::update(int param_0, GXColor& param_2, dKy_tevstr_c* param_4, const cXyz* presentationEye) { +#else +void mDoExt_3DlineMat1_c::update(int param_0, GXColor& param_2, dKy_tevstr_c* param_4 +#endif mColor = param_2; this->mpTevStr = param_4; if (param_0 < 0) { @@ -2904,6 +2940,11 @@ void mDoExt_3DlineMat1_c::update(int param_0, GXColor& param_2, dKy_tevstr_c* pa } else { field_0x34 = param_0; } + +#if TARGET_PC + mInterpLineKind = 2; +#endif + view_class* stack_3c = dComIfGd_getView(); mDoExt_3Dline_c* sp_38 = mpLines; f32 local_f30; @@ -2929,6 +2970,12 @@ void mDoExt_3DlineMat1_c::update(int param_0, GXColor& param_2, dKy_tevstr_c* pa for (s32 sp_14 = 0; sp_14 < mNumLines; sp_14++) { local_r27 = sp_38[0].field_0x0; size_p = sp_38->field_0x4; +#if TARGET_PC + if (presentationEye != nullptr && dusk::getSettings().game.enableFrameInterpolation && size_p == NULL) { + sp_38 += 1; + continue; + } +#endif JUT_ASSERT(5875, size_p != NULL); sp_24 = sp_38->field_0x8[mIsDrawn]; sp_28 = sp_24; @@ -2942,7 +2989,12 @@ void mDoExt_3DlineMat1_c::update(int param_0, GXColor& param_2, dKy_tevstr_c* pa sp_130 = local_r27[1] - local_r27[0]; local_f30 = sp_130.abs(); local_f31 += local_f30 * 0.1f; +#if TARGET_PC + const cXyz& lineEye = (presentationEye != nullptr && dusk::getSettings().game.enableFrameInterpolation) ? *presentationEye : stack_3c->lookat.eye; + sp_13c = local_r27[0] - lineEye; +#else sp_13c = local_r27[0] - stack_3c->lookat.eye; +#endif sp_130 = sp_130.outprod(sp_13c); sp_130.normalizeZP(); local_r30->x = sp_130.x * 64.0f; @@ -2964,7 +3016,11 @@ void mDoExt_3DlineMat1_c::update(int param_0, GXColor& param_2, dKy_tevstr_c* pa sp_130 = local_r27[1] - local_r27[0]; local_f30 = sp_130.abs(); local_f31 += local_f30 * 0.1f; +#if TARGET_PC + sp_13c = local_r27[0] - lineEye; +#else sp_13c = local_r27[0] - stack_3c->lookat.eye; +#endif sp_130 = sp_130.outprod(sp_13c); sp_130.normalizeZP(); local_r30 += 2; @@ -3008,6 +3064,19 @@ void mDoExt_3DlineMat1_c::update(int param_0, GXColor& param_2, dKy_tevstr_c* pa } } +#if TARGET_PC +void mDoExt_3DlineMat1_c::refreshGeometryForPresentationEye(const cXyz& eye) { + if (!dusk::getSettings().game.enableFrameInterpolation) { + return; + } + if (mInterpLineKind == 1) { + update(field_0x34, mInterpLineF, mColor, mInterpLineU16, mpTevStr, &eye); + } else if (mInterpLineKind == 2) { + update(field_0x34, mColor, mpTevStr, &eye); + } +} +#endif + void mDoExt_3DlineMatSortPacket::setMat(mDoExt_3DlineMat_c* i_3DlineMat) { if (mp3DlineMat == NULL) { dComIfGd_getListPacket()->entryImm(this, 0); diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index a5bd4640c8..ff9e2042a0 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -29,10 +29,13 @@ #include "d/d_meter2_info.h" #include "d/d_s_play.h" #include "dusk/endian.h" +#include "dusk/frame_interpolation.h" #include "dusk/gx_helper.h" #include "dusk/logging.h" #include "f_ap/f_ap_game.h" +#include "f_op/f_op_actor_mng.h" #include "f_op/f_op_camera_mng.h" +#include "f_pc/f_pc_name.h" #include "m_Do/m_Do_controller_pad.h" #include "m_Do/m_Do_graphic.h" #include "m_Do/m_Do_machine.h" @@ -48,6 +51,7 @@ #endif #if TARGET_PC +#include "d/actor/d_a_horse.h" #include "dusk/imgui/ImGuiConsole.hpp" #include "dusk/dusk.h" #endif @@ -465,6 +469,51 @@ void darwFilter(GXColor matColor) { GXEnd(); } +#ifdef TARGET_PC +static void mDoGph_AdvanceFadeState() { + if (mDoGph_gInf_c::isFade() != 0) { + f32 fade_rate = mDoGph_gInf_c::getFadeRate() + mDoGph_gInf_c::getFadeSpeed(); + + if (fade_rate < 0.0f) { + fade_rate = 0.0f; + mDoGph_gInf_c::offFade(); + } else if (fade_rate > 1.0f) { + fade_rate = 1.0f; + } + + mDoGph_gInf_c::setFadeRate(fade_rate); + mDoGph_gInf_c::getFadeColor().a = 255.0f * fade_rate; + } else { + GXColor& fade_color = mDoGph_gInf_c::getFadeColor(); + if (dComIfG_getBrightness() != 255) { + fade_color.r = 0; + fade_color.g = 0; + fade_color.b = 0; + fade_color.a = 255 - dComIfG_getBrightness(); + } else { + fade_color.a = 0; + } + } +} + +static void mDoGph_AdvanceFadeState(u32 tick_count) { + for (u32 i = 0; i < tick_count; ++i) { + mDoGph_AdvanceFadeState(); + } +} + +static void mDoGph_DrawStoredFade() { + GXColor& fade_color = mDoGph_gInf_c::getFadeColor(); + if (fade_color.a != 0) { + darwFilter(fade_color); + } +} + +void mDoGph_gInf_c::calcFade() { + mDoGph_AdvanceFadeState(); + mDoGph_DrawStoredFade(); +} +#else void mDoGph_gInf_c::calcFade() { if (mDoGph_gInf_c::mFade != 0) { mFadeRate += mFadeSpeed; @@ -493,6 +542,7 @@ void mDoGph_gInf_c::calcFade() { darwFilter(mFadeColor); } } +#endif #if PLATFORM_WII || PLATFORM_SHIELD u32 mDoGph_gInf_c::csr_c::m_blurID; @@ -531,29 +581,29 @@ f32 mDoGph_gInf_c::m_scale = 1.0f; f32 mDoGph_gInf_c::m_invScale = 1.0f; -int mDoGph_gInf_c::m_maxX = 608 - 1; +int mDoGph_gInf_c::m_maxX = FB_WIDTH_BASE - 1; -int mDoGph_gInf_c::m_maxY = 448 - 1; +int mDoGph_gInf_c::m_maxY = FB_HEIGHT_BASE - 1; -int mDoGph_gInf_c::m_width = 608; +int mDoGph_gInf_c::m_width = FB_WIDTH_BASE; -int mDoGph_gInf_c::m_height = 448; +int mDoGph_gInf_c::m_height = FB_HEIGHT_BASE; -f32 mDoGph_gInf_c::m_maxXF = 608.0f - 1; +f32 mDoGph_gInf_c::m_maxXF = FB_WIDTH_BASE - 1; -f32 mDoGph_gInf_c::m_maxYF = 448.0f - 1; +f32 mDoGph_gInf_c::m_maxYF = FB_HEIGHT_BASE - 1; -f32 mDoGph_gInf_c::m_widthF = 608.0f; +f32 mDoGph_gInf_c::m_widthF = FB_WIDTH_BASE; -f32 mDoGph_gInf_c::m_heightF = 448.0f; +f32 mDoGph_gInf_c::m_heightF = FB_HEIGHT_BASE; struct tvSize { u16 width; u16 height; }; const tvSize l_tvSize[2] = { - {608, 448}, - {808, 448}, + {FB_WIDTH_BASE, FB_HEIGHT_BASE}, + {808, FB_HEIGHT_BASE}, }; #if TARGET_PC @@ -569,8 +619,8 @@ void mDoGph_gInf_c::setTvSize() { m_width = tvsize->width; m_height = tvsize->height; - m_minX = -((m_width - 608) / 2); - m_minY = -((m_height - 448) / 2); + m_minX = -((m_width - FB_WIDTH_BASE) / 2); + m_minY = -((m_height - FB_HEIGHT_BASE) / 2); m_maxX = m_minX + m_width; m_maxY = m_minY + m_height; @@ -819,6 +869,9 @@ int mDoGph_AfterOfDraw() { JUTVideo::getManager()->setRenderMode(mDoMch_render_c::getRenderModeObj()); mDoGph_gInf_c::endFrame(); +#ifdef TARGET_PC + dusk::frame_interp::notify_sim_tick_complete(); +#endif return 1; } @@ -1661,6 +1714,15 @@ static void captureScreenPerspDrawInfo(JPADrawInfo& info) { static void drawItem3D() { ZoneScoped; +#ifdef TARGET_PC + // Frame interpolation: Title screen needs 0.0f while everything else that runs through this is -100.0f. + // Running presentation faster than logic revealed the problem. Thanks, Nintendo. + if (fopAcM_SearchByName(fpcNm_TITLE_e) != nullptr) { + dMenu_Collect3D_c::setViewPortOffsetY(0.0f); + } else { + dMenu_Collect3D_c::setViewPortOffsetY(-100.0f); + } +#endif Mtx item_mtx; dMenu_Collect3D_c::setupItem3D(item_mtx); @@ -1688,13 +1750,21 @@ int mDoGph_Painter() { #if TARGET_PC dusk::g_imguiConsole.PreDraw(); + + const u32 pending_ui_ticks = dusk::frame_interp::begin_presentation_ui_pass(); #endif #if DEBUG drawHeapMap(); #endif - dComIfGp_particle_calcMenu(); +#ifdef TARGET_PC + for (u32 i = 0; i < pending_ui_ticks; ++i) { +#endif + dComIfGp_particle_calcMenu(); +#ifdef TARGET_PC + } +#endif JFWDisplay::getManager()->setFader(mDoGph_gInf_c::getFader()); mDoGph_gInf_c::setClearColor(mDoGph_gInf_c::getBackColor()); @@ -1780,7 +1850,13 @@ int mDoGph_Painter() { GXSetScissor(view_port->x_orig, view_port->y_orig, view_port->width, view_port->height); +#ifdef TARGET_PC + // Frame interpolation: Call setViewMtx earlier so that it's interpolated in time for draw_info to use it + j3dSys.setViewMtx(camera_p->view.viewMtx); + JPADrawInfo draw_info(j3dSys.getViewMtx(), camera_p->view.fovy, camera_p->view.aspect); +#else JPADrawInfo draw_info(camera_p->view.viewMtx, camera_p->view.fovy, camera_p->view.aspect); +#endif #if WIDESCREEN_SUPPORT if (mDoGph_gInf_c::isWideZoom()) { @@ -1818,8 +1894,15 @@ int mDoGph_Painter() { PPCSync(); +#ifndef TARGET_PC j3dSys.setViewMtx(camera_p->view.viewMtx); +#endif dKy_setLight(); +#if TARGET_PC + if (dusk::getSettings().game.enableFrameInterpolation) { + dKy_setLight_again(); + } +#endif GX_DEBUG_GROUP(dComIfGd_drawOpaListSky); GX_DEBUG_GROUP(dComIfGd_drawXluListSky); @@ -1870,6 +1953,18 @@ int mDoGph_Painter() { GX_DEBUG_GROUP(dComIfGd_drawOpaListDark); } +#if TARGET_PC + if (dusk::getSettings().game.enableFrameInterpolation) { + cXyz pres_eye; + dusk::frame_interp::camera_eye_from_view_mtx(j3dSys.getViewMtx(), &pres_eye); + // FRAME INTERP NOTE: Currently only recalculating points for Epona's reins. Need a more global solution. + if (daHorse_c* horse = dComIfGp_getHorseActor()) { + horse->lerpControlPoints(dusk::frame_interp::get_interpolation_step()); + } + g_dComIfG_gameInfo.drawlist.refresh3DlineMats(pres_eye); + } +#endif + GX_DEBUG_GROUP(dComIfGd_drawOpaListPacket); #if DEBUG @@ -2138,7 +2233,7 @@ int mDoGph_Painter() { if (fapGmHIO_getParticle()) { #if WIDESCREEN_SUPPORT if (mDoGph_gInf_c::isWideZoom()) { - ortho.setOrtho(0.0f, 0.0f, 608.0f, 448.0f, 100000.0f, -100000.0f); + ortho.setOrtho(0.0f, 0.0f, FB_WIDTH_BASE, FB_HEIGHT_BASE, 100000.0f, -100000.0f); } else #endif { @@ -2149,8 +2244,8 @@ int mDoGph_Painter() { ortho.setPort(); Mtx m3; - MTXTrans(m3, FB_WIDTH / 2, FB_HEIGHT / 2, 0.0f); - JPADrawInfo draw_info2(m3, 0.0f, FB_HEIGHT, 0.0f, FB_WIDTH); + MTXTrans(m3, FB_WIDTH_BASE / 2, FB_HEIGHT_BASE / 2, 0.0f); + JPADrawInfo draw_info2(m3, 0.0f, FB_HEIGHT_BASE, 0.0f, FB_WIDTH_BASE); dComIfGp_particle_draw2Dgame(&draw_info2); } @@ -2159,7 +2254,12 @@ int mDoGph_Painter() { if (strcmp(dComIfGp_getStartStageName(), "F_SP127") != 0 && (mDoGph_gInf_c::isFade() & 0x80) == 0) { +#ifdef TARGET_PC + mDoGph_AdvanceFadeState(pending_ui_ticks); + mDoGph_DrawStoredFade(); +#else mDoGph_gInf_c::calcFade(); +#endif } #if DEBUG @@ -2224,7 +2324,13 @@ int mDoGph_Painter() { #endif GXSetClipMode(GX_CLIP_ENABLE); - dDlst_list_c::calcWipe(); +#ifdef TARGET_PC + for (u32 i = 0; i < pending_ui_ticks; ++i) { +#endif + dDlst_list_c::calcWipe(); +#ifdef TARGET_PC + } +#endif j3dSys.reinitGX(); ortho.setOrtho(mDoGph_gInf_c::getMinXF(), mDoGph_gInf_c::getMinYF(), @@ -2241,9 +2347,9 @@ int mDoGph_Painter() { cMtx_copy(j3dSys.getViewMtx(), m4); Mtx m5; - MTXTrans(m5, FB_WIDTH / 2, FB_HEIGHT / 2, 0.0f); + MTXTrans(m5, FB_WIDTH_BASE / 2, FB_HEIGHT_BASE / 2, 0.0f); - JPADrawInfo draw_info3(m5, 0.0f, FB_HEIGHT, 0.0f, FB_WIDTH); + JPADrawInfo draw_info3(m5, 0.0f, FB_HEIGHT_BASE, 0.0f, FB_WIDTH_BASE); if (!dComIfGp_isPauseFlag()) { GX_DEBUG_GROUP(dComIfGp_particle_draw2Dback, &draw_info3); @@ -2274,7 +2380,12 @@ int mDoGph_Painter() { if (strcmp(dComIfGp_getStartStageName(), "F_SP127") == 0 || (mDoGph_gInf_c::isFade() & 0x80) != 0) { +#ifdef TARGET_PC + mDoGph_AdvanceFadeState(pending_ui_ticks); + mDoGph_DrawStoredFade(); +#else mDoGph_gInf_c::calcFade(); +#endif } GX_DEBUG_GROUP(dComIfGp_particle_draw2DmenuFore, &draw_info3); @@ -2307,10 +2418,16 @@ int mDoGph_Painter() { #if TARGET_PC dusk::g_imguiConsole.PostDraw(); + + JFWDisplay::getManager()->setFaderSimSteps(pending_ui_ticks); #endif mDoGph_gInf_c::endRender(); +#ifdef TARGET_PC + dusk::frame_interp::end_presentation_ui_pass(); +#endif + #if WIDESCREEN_SUPPORT mDoGph_gInf_c::offWideZoom(); #endif diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 1fc8cc2128..4dd1a658e5 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -20,6 +20,7 @@ #include "JSystem/JUtility/JUTProcBar.h" #include "JSystem/JUtility/JUTReport.h" #include "SSystem/SComponent/c_counter.h" +#include "SSystem/SComponent/c_API_graphic.h" #include "Z2AudioLib/Z2WolfHowlMgr.h" #include "c/c_dylink.h" #include "d/d_com_inf_game.h" @@ -46,12 +47,12 @@ #include "SSystem/SComponent/c_API.h" #include "dusk/app_info.hpp" #include "dusk/dusk.h" +#include "dusk/frame_interpolation.h" #include "dusk/imgui/ImGuiEngine.hpp" #include "dusk/logging.h" #include "dusk/main.h" #include "dusk/imgui/ImGuiConsole.hpp" #include "version.h" -#include "dusk/time.h" #include #include @@ -197,6 +198,11 @@ void main01(void) { if (preLaunchUIWindowSize.width != 0) mDoGph_gInf_c::setWindowSize(preLaunchUIWindowSize); + using clock = std::chrono::steady_clock; + constexpr double kSimStepSeconds = 1.0 / 30.0; + auto previous_time = clock::now(); + double accumulator = kSimStepSeconds; + do { // 1. Update Window Events const AuroraEvent* event = aurora_update(); @@ -219,27 +225,42 @@ void main01(void) { eventsDone:; - static u32 frame = 0; - frame++; - - // Game Inputs - mDoCPd_c::read(); + auto current_time = clock::now(); + double frame_seconds = std::chrono::duration(current_time - previous_time).count(); + previous_time = current_time; + accumulator += frame_seconds; VIWaitForRetrace(); -#if TARGET_PC dusk::lastFrameAuroraStats = *aurora_get_stats(); if (!aurora_begin_frame()) { DuskLog.debug("aurora_begin_frame returned false, skipping draw this frame"); continue; } -#endif - // EXECUTE GAME LOGIC & RENDER - // This calls mDoGph_Painter -> JFWDisplay -> GX Functions - fapGm_Execute(); + if (dusk::getSettings().game.enableFrameInterpolation) { + while (accumulator >= kSimStepSeconds) { + mDoCPd_c::read(); + fapGm_Execute(); + mDoAud_Execute(); + accumulator -= kSimStepSeconds; + } - mDoAud_Execute(); + float interp_alpha = static_cast(accumulator / kSimStepSeconds); + dusk::frame_interp::interpolate(interp_alpha); + cAPIGph_Painter(); + } else { + accumulator = 0.0; + + // Game Inputs + mDoCPd_c::read(); + + // EXECUTE GAME LOGIC & RENDER + // This calls mDoGph_Painter -> JFWDisplay -> GX Functions + fapGm_Execute(); + + mDoAud_Execute(); + } aurora_end_frame();