From 6c56c224c9ad883e951e573cd4e9b973219581a3 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Sun, 19 Apr 2026 02:25:00 -0700 Subject: [PATCH] frame interp camera cleanups --- include/dusk/frame_interpolation.h | 13 ++- .../JSystem/J3DGraphAnimator/J3DModel.h | 2 +- .../JSystem/src/J3DGraphAnimator/J3DModel.cpp | 8 +- src/d/d_camera.cpp | 12 ++- src/d/d_kankyo.cpp | 13 +++ src/dusk/frame_interpolation.cpp | 89 ++++++++++--------- src/m_Do/m_Do_main.cpp | 4 +- 7 files changed, 84 insertions(+), 57 deletions(-) diff --git a/include/dusk/frame_interpolation.h b/include/dusk/frame_interpolation.h index b05bbe786c..3ba1af4799 100644 --- a/include/dusk/frame_interpolation.h +++ b/include/dusk/frame_interpolation.h @@ -1,5 +1,4 @@ -#ifndef DUSK_FRAME_INTERP_H -#define DUSK_FRAME_INTERP_H +#pragma once #include #include @@ -7,6 +6,7 @@ #include class camera_process_class; +class view_class; #ifdef __cplusplus namespace dusk { @@ -16,7 +16,8 @@ void ensure_initialized(); void begin_record(); void end_record(); -void interpolate(float step); +void begin_frame(bool is_sim_frame, float step); +void interpolate(); float get_interpolation_step(); void request_presentation_sync(); @@ -29,14 +30,14 @@ void set_ui_tick_pending(bool value); bool get_ui_tick_pending(); void record_camera(::camera_process_class* cam, int camera_id); +void interp_view(::view_class* view); void record_final_mtx(Mtx m, const void *key); void record_final_mtx(Mtx m); bool lookup_replacement(const void* key, Mtx out); bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out); -typedef void (*InterpolationCallBack)(void* pUserWork); -void reset_interpolation_callbacks(); +typedef void (*InterpolationCallBack)(bool isSimFrame, void* pUserWork); // call on a sim tick, will get called during presentation void add_interpolation_callback(InterpolationCallBack pCallBack, void* pUserWork); @@ -55,5 +56,3 @@ struct PresentationCameraScope { } // namespace frame_interp } // namespace dusk #endif - -#endif diff --git a/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DModel.h b/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DModel.h index edf8c543b2..2e5b3bd38d 100644 --- a/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DModel.h +++ b/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DModel.h @@ -80,7 +80,7 @@ public: virtual ~J3DModel() {} #if TARGET_PC - static void interp_callback(void* pUserWork); + static void interp_callback(bool isSimFrame, void* pUserWork); #endif J3DModelData* getModelData() { return mModelData; } diff --git a/libs/JSystem/src/J3DGraphAnimator/J3DModel.cpp b/libs/JSystem/src/J3DGraphAnimator/J3DModel.cpp index b666caa457..139bba5dd6 100644 --- a/libs/JSystem/src/J3DGraphAnimator/J3DModel.cpp +++ b/libs/JSystem/src/J3DGraphAnimator/J3DModel.cpp @@ -98,10 +98,12 @@ s32 J3DModel::entryModelData(J3DModelData* pModelData, u32 mdlFlags, u32 mtxNum) } #if TARGET_PC -void J3DModel::interp_callback(void* pUserWork) { +void J3DModel::interp_callback(bool isSimFrame, void* pUserWork) { J3DModel* i_this = static_cast(pUserWork); - i_this->calcMaterial(); - i_this->diff(); + if (!isSimFrame) { + i_this->calcMaterial(); + i_this->diff(); + } } #endif diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index b93d569003..3e82c339ed 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -11009,6 +11009,15 @@ static int camera_execute(camera_process_class* i_this) { i_this->mCamera.CalcTrimSize(); store(i_this); + +#ifdef TARGET_PC + // record new camera for our sim frame + dusk::frame_interp::record_camera(i_this, get_camera_id(i_this)); + // interpolate the view now so that this sim frame's view matrix matches what + // we'll be rendering with later + dusk::frame_interp::interp_view(&i_this->view); +#endif + view_setup(i_this); return 1; } @@ -11077,9 +11086,6 @@ static int camera_draw(camera_process_class* i_this) { 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_camera(process, camera_id); -#endif #if WIDESCREEN_SUPPORT mDoGph_gInf_c::setWideZoomProjection(process->view.projMtx); diff --git a/src/d/d_kankyo.cpp b/src/d/d_kankyo.cpp index c6739ee925..05a0f70f5a 100644 --- a/src/d/d_kankyo.cpp +++ b/src/d/d_kankyo.cpp @@ -35,6 +35,7 @@ #if TARGET_PC #include "dusk/imgui/ImGuiBloomWindow.hpp" #include "dusk/settings.h" +#include "dusk/frame_interpolation.h" #endif static void GxXFog_set(); @@ -8105,11 +8106,23 @@ void dKankyo_HIO_c::genMessage(JORMContext* mctx) { #endif +#if TARGET_PC +static void interp_callback(bool isSimFrame, void* pUserWork) { + if (!isSimFrame) { + g_env_light.drawKankyo(); + } +} +#endif + void dScnKy_env_light_c::drawKankyo() { setSunpos(); SetBaseLight(); setLight(); dKy_setLight_nowroom(g_env_light.PrevCol); + +#if TARGET_PC + dusk::frame_interp::add_interpolation_callback(interp_callback, nullptr); +#endif } void dKy_undwater_filter_draw() { diff --git a/src/dusk/frame_interpolation.cpp b/src/dusk/frame_interpolation.cpp index 1826bbf171..01604ede4f 100644 --- a/src/dusk/frame_interpolation.cpp +++ b/src/dusk/frame_interpolation.cpp @@ -19,6 +19,7 @@ bool g_interpolating = false; bool g_sync_presentation = false; float g_step = 0.0f; +bool g_is_sim_frame = false; bool g_ui_tick_pending = false; Recording g_current_recording; @@ -141,6 +142,14 @@ bool is_enabled() { return g_enabled; } +void begin_frame(bool is_sim_frame, float step) { + g_is_sim_frame = is_sim_frame; + g_step = std::clamp(step, 0.0f, 1.0f); + if (is_sim_frame) { + s_interpolationCallBackWork.clear(); + } +} + void begin_record() { ensure_initialized(); @@ -167,11 +176,6 @@ void begin_record() { s_cam_prev.valid = false; s_cam_curr.valid = false; return; - } else { - copy_view_to_snap(&s_cam_prev, cam->view); -#if WIDESCREEN_SUPPORT - s_cam_prev.wideZoom = s_cam_curr.valid ? s_cam_curr.wideZoom : false; -#endif } } @@ -179,10 +183,9 @@ void end_record() { g_recording = false; } -void interpolate(float step) { +void interpolate() { ensure_initialized(); clear_replacements(); - g_step = std::clamp(step, 0.0f, 1.0f); g_interpolating = g_enabled && !g_recording && !g_sync_presentation && has_recording_data(g_current_recording); if (!g_interpolating) { return; @@ -274,25 +277,56 @@ void record_camera(::camera_process_class* cam, int camera_id) { if (!g_enabled || camera_id != 0 || cam == nullptr) { return; } + s_cam_prev = std::move(s_cam_curr); copy_view_to_snap(&s_cam_curr, cam->view); #if WIDESCREEN_SUPPORT s_cam_curr.wideZoom = mDoGph_gInf_c::isWideZoom(); #endif } +void interp_view(::view_class* view) { + const f32 step = get_interpolation_step(); + cXyz eye; + cXyz center; + cXyz up; + lerp_xyz(&eye, s_cam_prev.eye, s_cam_curr.eye, step); + lerp_xyz(¢er, s_cam_prev.center, s_cam_curr.center, step); + lerp_xyz(&up, s_cam_prev.up, s_cam_curr.up, step); + if (!up.normalizeRS()) { + up = s_cam_curr.up; + up.normalizeRS(); + } + + view->lookat.eye = eye; + view->lookat.center = center; + view->lookat.up = up; + view->bank = lerp_bank(s_cam_prev.bank, s_cam_curr.bank, step); + view->fovy = s_cam_prev.fovy + (s_cam_curr.fovy - s_cam_prev.fovy) * step; + view->aspect = s_cam_prev.aspect + (s_cam_curr.aspect - s_cam_prev.aspect) * step; + view->near_ = s_cam_prev.near_ + (s_cam_curr.near_ - s_cam_prev.near_) * step; + view->far_ = s_cam_prev.far_ + (s_cam_curr.far_ - s_cam_prev.far_) * step; + + // FRAME INTERP TODO: It might be better if I rewired the game to not clear this flag until the + // next sim frame, but I don't care enough to right now +#if WIDESCREEN_SUPPORT + if (mDoGph_gInf_c::isWide() && !mDoGph_gInf_c::isWideZoom() && step >= 0.5f ? + s_cam_curr.wideZoom : + s_cam_prev.wideZoom) + { + mDoGph_gInf_c::onWideZoom(); + } +#endif +} + static void run_interpolation_callbacks() { for (size_t i = 0; i < s_interpolationCallBackWork.size(); i++) { auto const& work = s_interpolationCallBackWork[i]; - work.pCallBack(work.pUserWork); + work.pCallBack(g_is_sim_frame, work.pUserWork); } } -void reset_interpolation_callbacks() { - s_interpolationCallBackWork.clear(); -} - void add_interpolation_callback(InterpolationCallBack pCallBack, void* pUserWork) { - if (!is_enabled() || s_presentation_depth > 0) + if (!is_enabled() || s_presentation_depth > 0 || !g_is_sim_frame) return; s_interpolationCallBackWork.emplace_back(pCallBack, pUserWork); @@ -317,34 +351,7 @@ void begin_presentation_camera() { } std::memcpy(&s_presentation_view_backup, view, sizeof(view_class)); - - const f32 step = get_interpolation_step(); - cXyz eye; - cXyz center; - cXyz up; - lerp_xyz(&eye, s_cam_prev.eye, s_cam_curr.eye, step); - lerp_xyz(¢er, s_cam_prev.center, s_cam_curr.center, step); - lerp_xyz(&up, s_cam_prev.up, s_cam_curr.up, step); - if (!up.normalizeRS()) { - up = s_cam_curr.up; - up.normalizeRS(); - } - - view->lookat.eye = eye; - view->lookat.center = center; - view->lookat.up = up; - view->bank = lerp_bank(s_cam_prev.bank, s_cam_curr.bank, step); - view->fovy = s_cam_prev.fovy + (s_cam_curr.fovy - s_cam_prev.fovy) * step; - view->aspect = s_cam_prev.aspect + (s_cam_curr.aspect - s_cam_prev.aspect) * step; - view->near_ = s_cam_prev.near_ + (s_cam_curr.near_ - s_cam_prev.near_) * step; - view->far_ = s_cam_prev.far_ + (s_cam_curr.far_ - s_cam_prev.far_) * step; - - // FRAME INTERP TODO: It might be better if I rewired the game to not clear this flag until the next sim frame, but I don't care enough to right now -#if WIDESCREEN_SUPPORT - if (mDoGph_gInf_c::isWide() && !mDoGph_gInf_c::isWideZoom() && step >= 0.5f ? s_cam_curr.wideZoom : s_cam_prev.wideZoom) { - mDoGph_gInf_c::onWideZoom(); - } -#endif + interp_view(view); // FRAME INTERP TODO: Largely copied from d_camera's camera_draw function from this point, got any better ideas? C_MTXPerspective(view->projMtx, view->fovy, view->aspect, view->near_, view->far_); diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 0249269f32..7ef445fbe6 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -234,8 +234,8 @@ void main01(void) { mDoGph_gInf_c::updateRenderSize(); if (pacing.is_interpolating) { + dusk::frame_interp::begin_frame(pacing.do_sim_tick, pacing.interpolation_step); if (pacing.do_sim_tick) { - dusk::frame_interp::reset_interpolation_callbacks(); dusk::frame_interp::set_ui_tick_pending(true); mDoCPd_c::read(); dusk::gyro::read(pacing.sim_pace); @@ -243,7 +243,7 @@ void main01(void) { mDoAud_Execute(); dusk::game_clock::reset_accumulator(); } - dusk::frame_interp::interpolate(pacing.interpolation_step); + dusk::frame_interp::interpolate(); { dusk::frame_interp::PresentationCameraScope presentation_camera; cAPIGph_Painter();