mirror of
https://github.com/HarbourMasters/SpaghettiKart
synced 2026-08-23 07:29:37 -04:00
Fix garbage frame flash on cinematic camera cuts (#720)
* Fix garbage frame flash on cinematic camera cuts Frame interpolation blended matrices across instant camera teleports in attract/demo and post-race cameras, flashing 1-2 frames of sheared geometry. Complete the existing camera-epoch mechanism: flag a cut in func_80019890 when the camera moves >100 units, and have the interpolator snap that frame to the new view instead of blending. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Retrigger CI (transient 502 downloading libogg from gitlab.xiph.org) * Name the cinematic shot dispatcher Review feedback on #720: func_80019890 -> camera_start_cinematic_shot. It starts whichever shot D_80164680 has selected for a camera, dispatching to the per-shot setup that teleports the camera to the shot's opening position, which is why the cut detection lives there. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Apply fix to freecam Added FrameInterpolation_DontInterpolateCamera calls to improve camera behavior when changing target players. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: MegaMech <MegaMech@users.noreply.github.com>
This commit is contained in:
+24
-7
@@ -41,6 +41,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "port/Game.h"
|
||||
#include "port/interpolation/FrameInterpolation.h"
|
||||
#include "engine/tracks/Track.h"
|
||||
#include "engine/RaceManager.h"
|
||||
|
||||
@@ -6550,12 +6551,19 @@ void func_80019760(Camera* camera, UNUSED Player* player, UNUSED s32 arg2, s32 c
|
||||
camera->rot[2] = 0;
|
||||
}
|
||||
|
||||
void func_80019890(s32 playerId, s32 cameraId) {
|
||||
// Starts the cinematic shot selected in D_80164680[cameraId]: dispatches to the
|
||||
// per-shot setup, which teleports the camera to the shot's opening position.
|
||||
void camera_start_cinematic_shot(s32 playerId, s32 cameraId) {
|
||||
s32 pathIndex;
|
||||
f32 prevX, prevY, prevZ, dx, dy, dz;
|
||||
Camera* camera = camera1;
|
||||
camera += cameraId;
|
||||
camera->playerId = playerId;
|
||||
|
||||
prevX = camera->pos[0];
|
||||
prevY = camera->pos[1];
|
||||
prevZ = camera->pos[2];
|
||||
|
||||
D_801646C0[cameraId] = 0;
|
||||
pathIndex = gPathIndexByPlayerId[playerId];
|
||||
|
||||
@@ -6614,6 +6622,15 @@ void func_80019890(s32 playerId, s32 cameraId) {
|
||||
if ((s16) D_80164680[cameraId] == 9) {
|
||||
D_80163DD8[cameraId] = (s32) pathIndex;
|
||||
}
|
||||
|
||||
// Flag a camera cut only when the camera teleported to a new shot;
|
||||
// small moves are continuous tracking updates that should stay smooth.
|
||||
dx = camera->pos[0] - prevX;
|
||||
dy = camera->pos[1] - prevY;
|
||||
dz = camera->pos[2] - prevZ;
|
||||
if ((dx * dx + dy * dy + dz * dz) > 100.0f * 100.0f) {
|
||||
FrameInterpolation_DontInterpolateCamera();
|
||||
}
|
||||
}
|
||||
|
||||
void func_80019B50(s32 cameraIndex, u16 arg1) {
|
||||
@@ -6737,11 +6754,11 @@ void func_80019DF4(void) {
|
||||
|
||||
void func_80019E58(void) {
|
||||
D_80164680[0] = 1;
|
||||
func_80019890(0, 0);
|
||||
camera_start_cinematic_shot(0, 0);
|
||||
D_80164670[0] = D_80164678[0];
|
||||
D_80164678[0] = 1;
|
||||
D_80164680[1] = 9;
|
||||
func_80019890(0, 1);
|
||||
camera_start_cinematic_shot(0, 1);
|
||||
D_80164670[1] = D_80164678[1];
|
||||
D_80164678[1] = 0;
|
||||
}
|
||||
@@ -6866,7 +6883,7 @@ void func_8001A348(s32 cameraId, f32 arg1, s32 arg2) {
|
||||
playerId = cameras[cameraId].playerId;
|
||||
D_80164688[cameraId] = arg1;
|
||||
D_80164680[cameraId] = func_8001A310((s32) gNearestPathPointByCameraId[cameraId], arg2);
|
||||
func_80019890(playerId, cameraId);
|
||||
camera_start_cinematic_shot(playerId, cameraId);
|
||||
}
|
||||
|
||||
void func_8001A3D8(s32 arg0, f32 arg1, s32 arg2) {
|
||||
@@ -6876,7 +6893,7 @@ void func_8001A3D8(s32 arg0, f32 arg1, s32 arg2) {
|
||||
D_80164688[arg0] = arg1;
|
||||
if (arg2 != D_80164680[arg0]) {
|
||||
D_80164680[arg0] = arg2;
|
||||
func_80019890(playerId, arg0);
|
||||
camera_start_cinematic_shot(playerId, arg0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6891,7 +6908,7 @@ void func_8001A450(s32 playerId, s32 arg1, s32 arg2) {
|
||||
temp_v0 = func_8001A310(waypoint, (temp_v1 + 1) % 10);
|
||||
if ((temp_v0 != temp_v1) || (arg2 != playerId)) {
|
||||
D_80164680[arg1] = temp_v0;
|
||||
func_80019890(arg2, arg1);
|
||||
camera_start_cinematic_shot(arg2, arg1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6938,7 +6955,7 @@ void func_8001A588(UNUSED u16* localD_80152300, Camera* camera, Player* player,
|
||||
} else {
|
||||
func_8001A124((s32) playerId, cameraIndex);
|
||||
}
|
||||
func_80019890((s32) playerId, cameraIndex);
|
||||
camera_start_cinematic_shot((s32) playerId, cameraIndex);
|
||||
}
|
||||
|
||||
if ((D_80164680[cameraIndex] == 14) || (D_80164680[cameraIndex] == 0)) {
|
||||
|
||||
+1
-1
@@ -261,7 +261,7 @@ void func_8001933C(Camera*, UNUSED Player*, s32, s32);
|
||||
void func_8001968C(void);
|
||||
void func_8001969C(s32, f32, s32, s16);
|
||||
void func_80019760(Camera*, UNUSED Player*, s32, s32);
|
||||
void func_80019890(s32, s32);
|
||||
void camera_start_cinematic_shot(s32, s32);
|
||||
void func_80019B50(s32, u16);
|
||||
void func_80019C50(s32);
|
||||
void func_80019D2C(Camera*, Player*, s32);
|
||||
|
||||
@@ -236,9 +236,11 @@ void freecam_keyboard_manager(Camera* camera, Vec3f forwardVector) {
|
||||
if (bFreecamUseController) {
|
||||
if (fController.buttonDepressed & R_TRIG) {
|
||||
fTargetPlayer = true;
|
||||
FrameInterpolation_DontInterpolateCamera();
|
||||
}
|
||||
if (fController.buttonDepressed & L_TRIG) {
|
||||
fTargetPlayer = false;
|
||||
FrameInterpolation_DontInterpolateCamera();
|
||||
}
|
||||
if (fController.buttonPressed & L_JPAD) {
|
||||
TargetPreviousPlayer = true;
|
||||
@@ -273,6 +275,7 @@ void freecam_keyboard_manager(Camera* camera, Vec3f forwardVector) {
|
||||
else if (wnd->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) {
|
||||
if (FreecamKeyDown('F')) {
|
||||
fTargetPlayer = !fTargetPlayer;
|
||||
FrameInterpolation_DontInterpolateCamera();
|
||||
}
|
||||
if (FreecamKeyDown('N')) {
|
||||
TargetPreviousPlayer = true;
|
||||
@@ -309,6 +312,7 @@ void freecam_keyboard_manager(Camera* camera, Vec3f forwardVector) {
|
||||
const uint8_t* keystate = SDL_GetKeyboardState(NULL);
|
||||
if (FreecamKeyDown(SDL_SCANCODE_F)) {
|
||||
fTargetPlayer = !fTargetPlayer;
|
||||
FrameInterpolation_DontInterpolateCamera();
|
||||
}
|
||||
if (FreecamKeyDown(SDL_SCANCODE_N)) {
|
||||
TargetPreviousPlayer = true;
|
||||
@@ -345,6 +349,7 @@ void freecam_keyboard_manager(Camera* camera, Vec3f forwardVector) {
|
||||
fRankIndex--;
|
||||
camera->playerId = fRankIndex;
|
||||
gScreenOneCtx->player = &gPlayers[fRankIndex];
|
||||
FrameInterpolation_DontInterpolateCamera();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +359,7 @@ void freecam_keyboard_manager(Camera* camera, Vec3f forwardVector) {
|
||||
fRankIndex++;
|
||||
camera->playerId = fRankIndex;
|
||||
gScreenOneCtx->player = &gPlayers[fRankIndex];
|
||||
FrameInterpolation_DontInterpolateCamera();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -253,6 +253,7 @@ bool is_recording;
|
||||
vector<Path*> current_path;
|
||||
uint32_t camera_epoch;
|
||||
uint32_t previous_camera_epoch;
|
||||
bool frame_had_camera_cut;
|
||||
Recording current_recording;
|
||||
Recording previous_recording;
|
||||
|
||||
@@ -599,8 +600,10 @@ struct InterpolateCtx {
|
||||
|
||||
unordered_map<Mtx*, MtxF> FrameInterpolation_Interpolate(float step) {
|
||||
InterpolateCtx ctx;
|
||||
ctx.step = step;
|
||||
ctx.w = 1.0f - step;
|
||||
// On a camera cut, blending the two frames would mix unrelated views;
|
||||
// snap every sub-frame to the new frame instead (hard cut, like hardware).
|
||||
ctx.step = frame_had_camera_cut ? 1.0f : step;
|
||||
ctx.w = 1.0f - ctx.step;
|
||||
ctx.interpolate_branch(&previous_recording.root_path, ¤t_recording.root_path);
|
||||
return ctx.mtx_replacements;
|
||||
}
|
||||
@@ -633,6 +636,7 @@ void FrameInterpolation_StartRecord(void) {
|
||||
}
|
||||
|
||||
void FrameInterpolation_StopRecord(void) {
|
||||
frame_had_camera_cut = camera_epoch != previous_camera_epoch;
|
||||
previous_camera_epoch = camera_epoch;
|
||||
is_recording = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user