diff --git a/files.cmake b/files.cmake index 5f11f13fad..dbcc9b2e5f 100644 --- a/files.cmake +++ b/files.cmake @@ -1363,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_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/d_com_inf_game.h b/include/d/d_com_inf_game.h index d1fd930678..0ca4da42ca 100644 --- a/include/d/d_com_inf_game.h +++ b/include/d/d_com_inf_game.h @@ -4848,8 +4848,8 @@ inline void dComIfGd_drawXluListDark() { inline void dComIfGd_drawXluListInvisible() { ZoneScoped; #ifdef TARGET_PC - // FIXME: Water rendering hack for frame interpolation - if (!dusk::getSettings().game.enableFrameInterpolation) { + if (dusk::getSettings().game.enableWaterRefraction && + !dusk::getSettings().game.enableFrameInterpolation) { #endif g_dComIfG_gameInfo.drawlist.drawXluListInvisible(); #ifdef TARGET_PC @@ -4859,7 +4859,14 @@ inline void dComIfGd_drawXluListInvisible() { 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/dusk/frame_interpolation.h b/include/dusk/frame_interpolation.h index 3dfa00d53b..98495d18db 100644 --- a/include/dusk/frame_interpolation.h +++ b/include/dusk/frame_interpolation.h @@ -6,6 +6,8 @@ #include #include +struct cXyz; + #ifdef __cplusplus namespace dusk { namespace frame_interp { @@ -15,6 +17,7 @@ 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(); @@ -27,6 +30,8 @@ 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 diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 4a1250e312..8418602251 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -52,8 +52,9 @@ struct UserSettings { // Graphics ConfigVar enableBloom; - ConfigVar useWaterProjectionOffset; + ConfigVar enableWaterRefraction; ConfigVar enableFrameInterpolation; + ConfigVar shadowResolutionMultiplier; // Audio ConfigVar noLowHpSound; @@ -77,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/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/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 0ae8e95fa7..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; @@ -377,6 +381,19 @@ 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) { #if TARGET_PC if (dusk::getSettings().game.enableFrameInterpolation) { @@ -385,44 +402,42 @@ static void waitForTick(u32 p1, u16 p2) { if (dusk::getTransientSettings().skipFrameRateLimit) { p1 = OS_TIMER_CLOCK / 120; } + ZoneScopedC(tracy::Color::DimGray); #endif - ZoneScopedC(tracy::Color::DimGray); - 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(); @@ -440,7 +455,6 @@ void JFWDisplay::threadSleep(s64 time) { OSSuspendThread(alarm.getThread()); OSRestoreInterrupts(status); } -#endif static void dummy() { JUTXfb::getManager()->setDisplayingXfbIndex(0); @@ -480,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/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/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_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_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/d_drawlist.cpp b/src/d/d_drawlist.cpp index 1474b6f24d..9f2255b884 100644 --- a/src/d/d_drawlist.cpp +++ b/src/d/d_drawlist.cpp @@ -1441,7 +1441,11 @@ void dDlst_shadowSimple_c::set(cXyz* param_0, f32 param_1, f32 param_2, cXyz* pa 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 @@ -1480,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, @@ -2001,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_kankyo.cpp b/src/d/d_kankyo.cpp index 753be77a40..0bd3db8e2a 100644 --- a/src/d/d_kankyo.cpp +++ b/src/d/d_kankyo.cpp @@ -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_fmap.cpp b/src/d/d_menu_fmap.cpp index 652f3a06c5..149e03349d 100644 --- a/src/d/d_menu_fmap.cpp +++ b/src/d/d_menu_fmap.cpp @@ -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); } diff --git a/src/d/d_menu_fmap2D.cpp b/src/d/d_menu_fmap2D.cpp index e016ed14dd..558ad05d6d 100644 --- a/src/d/d_menu_fmap2D.cpp +++ b/src/d/d_menu_fmap2D.cpp @@ -432,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_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/dusk/frame_interpolation.cpp b/src/dusk/frame_interpolation.cpp index a3777dea7e..0c21b2b5af 100644 --- a/src/dusk/frame_interpolation.cpp +++ b/src/dusk/frame_interpolation.cpp @@ -300,6 +300,10 @@ void interpolate(float step) { 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++; @@ -394,5 +398,12 @@ bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx 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 39cf09a9cd..b505d84345 100644 --- a/src/dusk/imgui/ImGuiMenuEnhancements.cpp +++ b/src/dusk/imgui/ImGuiMenuEnhancements.cpp @@ -85,6 +85,11 @@ namespace dusk { 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("Improves the shadow resolution, making them higher quality."); + } + ImGui::EndMenu(); } diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index ac738c6e24..d2b893c884 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -64,11 +64,7 @@ namespace dusk { config::ImGuiCheckbox("Native Bloom", getSettings().game.enableBloom); - config::ImGuiCheckbox("Water Projection Offset", getSettings().game.useWaterProjectionOffset); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Adds GC-specific -0.01 transS offset\n" - "that causes ~6px ghost artifacts in water reflections."); - } + 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 f67eee6fbb..dabed72f19 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -40,8 +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}, @@ -57,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} } }; @@ -101,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); @@ -117,6 +120,7 @@ void registerSettings() { 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/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 22dffad197..ff9e2042a0 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -29,6 +29,7 @@ #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" @@ -39,7 +40,6 @@ #include "m_Do/m_Do_graphic.h" #include "m_Do/m_Do_machine.h" #include "m_Do/m_Do_main.h" -#include "dusk/frame_interpolation.h" #include "tracy/Tracy.hpp" #if PLATFORM_WII || PLATFORM_SHIELD @@ -51,6 +51,7 @@ #endif #if TARGET_PC +#include "d/actor/d_a_horse.h" #include "dusk/imgui/ImGuiConsole.hpp" #include "dusk/dusk.h" #endif @@ -1897,6 +1898,11 @@ int mDoGph_Painter() { 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); @@ -1947,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 diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index e0b4ca32da..1cfa8edd3b 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -53,7 +53,6 @@ #include "dusk/main.h" #include "dusk/imgui/ImGuiConsole.hpp" #include "version.h" -#include "dusk/time.h" #include #include