From 834eaa9a65e2c3a8b7e3855d36367cdb94e250cc Mon Sep 17 00:00:00 2001 From: Sonic Dreamcaster Date: Mon, 23 Feb 2026 03:37:34 -0300 Subject: [PATCH] Interpolation: Camera interpolation skip for cutscenes --- src/engine/camera_interp_skip.c | 108 ++++++++++++++++++++++++++++++++ src/engine/fox_display.c | 34 ++++++++++ 2 files changed, 142 insertions(+) create mode 100644 src/engine/camera_interp_skip.c diff --git a/src/engine/camera_interp_skip.c b/src/engine/camera_interp_skip.c new file mode 100644 index 00000000..2a458a5a --- /dev/null +++ b/src/engine/camera_interp_skip.c @@ -0,0 +1,108 @@ +#include "global.h" + +#define SQXYZ(vec) ((vec.x) * (vec.x) + (vec.y) * (vec.y) + (vec.z) * (vec.z)) + +void Math_Vec3f_Diff(Vec3f* l, Vec3f* r, Vec3f* dest) { + dest->x = l->x - r->x; + dest->y = l->y - r->y; + dest->z = l->z - r->z; +} + +void Math_Vec3f_Sum(Vec3f* l, Vec3f* r, Vec3f* dest) { + dest->x = l->x + r->x; + dest->y = l->y + r->y; + dest->z = l->z + r->z; +} + +f32 Math_Vec3f_DistXYZ(Vec3f* a, Vec3f* b) { + Vec3f diff; + Math_Vec3f_Diff(b, a, &diff); + return sqrtf(SQXYZ(diff)); +} + +bool should_interpolate_perspective(Vec3f* eye, Vec3f* at) { + static Vec3f prev_eye = { 0, 0, 0 }; + static Vec3f prev_at = { 0, 0, 0 }; + static Vec3f eye_velocity = { 0, 0, 0 }; + static Vec3f at_velocity = { 0, 0, 0 }; + + Vec3f predicted_eye; + Vec3f predicted_at; + // Predict the new eye and at positions based on the previous velocity and positions. + Math_Vec3f_Sum(&prev_eye, &eye_velocity, &predicted_eye); + Math_Vec3f_Sum(&prev_at, &at_velocity, &predicted_at); + + // Calculate the current velocities from the previous and current positions. + Math_Vec3f_Diff(eye, &prev_eye, &eye_velocity); + Math_Vec3f_Diff(at, &prev_at, &at_velocity); + + // Compare the predicted positions to the real positions. + float eye_dist = Math_Vec3f_DistXYZ(&predicted_eye, eye); + float at_dist = Math_Vec3f_DistXYZ(&predicted_at, at); + + // Compare the velocities of the eye and at positions. + float velocity_diff = Math_Vec3f_DistXYZ(&eye_velocity, &at_velocity); + + // Update the tracking for the previous positions with the new ones. + prev_eye = *eye; + prev_at = *at; + + // These numbers are all picked via testing. + + // If the velocity of both positions was the same, then they're moving together and should interpolate. + if (velocity_diff <= 3.0f && eye_dist <= 100.0f && at_dist <= 100.0f) { + return true; + } + + // If the focus or position are basically the same across frames and the eye didn't move too far then it should + // probably be interpolated. + if (at_dist <= 20.0f && eye_dist <= 300.0f) { + return true; + } + if (eye_dist <= 20.0f && at_dist <= 300.0f) { + return true; + } + + // Force camera interpolation during the all-range transition + if (gPlayer[0].state == PLAYERSTATE_START_360) { + return true; + } + + // Force camera interpolation during camera shake + if (gCameraShake > 0 || gCameraShake > 0) { + return true; + } + + switch (gCurrentLevel) { + // For Robot destroy cutscenes + case LEVEL_SECTOR_Y: + if (velocity_diff > 100.0f || at_dist > 500.0f || eye_dist > 500.0f) { + eye_velocity.x = 0.0f; + eye_velocity.y = 0.0f; + eye_velocity.z = 0.0f; + at_velocity.x = 0.0f; + at_velocity.y = 0.0f; + at_velocity.z = 0.0f; + + printf("velocity_diff: %f\n at_dist: %f\n eye_dist: %f\n", velocity_diff, at_dist, eye_dist); + return false; + } + break; + + default: + if (velocity_diff > 200.0f || at_dist > 150.0f || eye_dist > 300.0f) { + eye_velocity.x = 0.0f; + eye_velocity.y = 0.0f; + eye_velocity.z = 0.0f; + at_velocity.x = 0.0f; + at_velocity.y = 0.0f; + at_velocity.z = 0.0f; + + printf("velocity_diff: %f\n at_dist: %f\n eye_dist: %f\n", velocity_diff, at_dist, eye_dist); + return false; + } + break; + } + + return true; +} \ No newline at end of file diff --git a/src/engine/fox_display.c b/src/engine/fox_display.c index bcf769a9..fae15d6f 100644 --- a/src/engine/fox_display.c +++ b/src/engine/fox_display.c @@ -1,5 +1,6 @@ #include "global.h" #include "mods/hit64.c" +#include "camera_interp_skip.c" #include "assets/ast_arwing.h" #include "assets/ast_allies.h" #include "assets/ast_landmaster.h" @@ -1877,6 +1878,31 @@ void Display_Update(void) { gPlayCamAt.y = camPlayer->cam.at.y; gPlayCamAt.z = camPlayer->cam.at.z; } + + static PlayState prevPlayState = 0; + static int camSkipTimes = 0; + + bool bigJump = !should_interpolate_perspective(&gPlayCamEye, &gPlayCamAt); + + // @port: Force interpolation camera skip if we're transitioning to or from a pause state. + if (((prevPlayState == PLAY_PAUSE) && (gPlayState == PLAY_UPDATE)) || + ((prevPlayState == PLAY_UPDATE) && (gPlayState == PLAY_PAUSE))) { + bigJump = true; + } + + if (bigJump) { + // @port Skip interpolation + FrameInterpolation_ShouldInterpolateFrame(false); + printf("CAMERA 1 SKIPED: %d\n", camSkipTimes++); + gCamera1Skipped = true; + } else { + FrameInterpolation_RecordOpenChild("GamePlayCam", 0); + FrameInterpolation_RecordMarker(__FILE__, __LINE__); + gCamera1Skipped = false; + } + + prevPlayState = gPlayState; + camPlayer->camYaw = -Math_Atan2F(gPlayCamEye.x - gPlayCamAt.x, gPlayCamEye.z - gPlayCamAt.z); camPlayer->camPitch = -Math_Atan2F(gPlayCamEye.y - gPlayCamAt.y, sqrtf(SQ(gPlayCamEye.z - gPlayCamAt.z) + SQ(gPlayCamEye.x - gPlayCamAt.x))); @@ -2051,6 +2077,14 @@ void Display_Update(void) { HUD_Draw(); HUD_EdgeArrows_Update(); } + + if (bigJump) { + // @port Re-enable Interpolation if it was skipped + FrameInterpolation_ShouldInterpolateFrame(true); + } else { + FrameInterpolation_RecordCloseChild(); + } + Matrix_Pop(&gGfxMatrix); Display_DrawHelpAlert(); sPlayersVisible[gPlayerNum] = false;