Fix Camera Interpolation For Sudden Camera Changes (#479)

* Fix Camera Interpolation On Sudden Movements

* huge formatting discovery

* increase angle tolerance
This commit is contained in:
inspectredc
2024-05-26 19:13:25 -05:00
committed by GitHub
parent 80559a0c20
commit 324cba6644
9 changed files with 154 additions and 0 deletions
@@ -0,0 +1,109 @@
#include "CameraInterpolationFixes.h"
#include <libultraship/bridge.h>
#include "Enhancements/GameInteractor/GameInteractor.h"
#include "CameraUtils.h"
#include "Enhancements/FrameInterpolation/FrameInterpolation.h"
extern "C" {
#include <z64.h>
#include "macros.h"
#include "functions.h"
extern PlayState* gPlayState;
extern SaveContext gSaveContext;
extern CameraSetting sCameraSettings[];
}
// This function should check for outstanding conditions which the distance check would falsly flag
// as something we do not want to interpolate. This mostly applies to general play for now
bool Camera_ShouldOverrideInterpolationCheck(Camera* camera) {
switch (sCameraSettings[camera->setting].cameraModes[camera->mode].funcId) {
case CAM_FUNC_BATTLE0:
case CAM_FUNC_BATTLE1:
case CAM_FUNC_BATTLE2:
case CAM_FUNC_BATTLE3:
case CAM_FUNC_BATTLE4:
case CAM_FUNC_PARALLEL0:
case CAM_FUNC_PARALLEL1:
case CAM_FUNC_PARALLEL2:
case CAM_FUNC_PARALLEL3:
case CAM_FUNC_PARALLEL4:
return true;
}
return false;
}
// This function checks whether there is too large a distance or change in angle between the expected
// position camera and the actual position of the camera. If there is, then we should not interpolate
bool Camera_ShouldInterpolateDist(Camera* camera) {
// Account for changes in position, pitch and yaw. Roll is rarely used so not currently handled
// `Velocity` is measured as the change across the previous frame
static f32 lastYaw = 0.0f;
static f32 lastYawVelocity = 0.0f;
static f32 lastPitch = 0.0f;
static f32 lastPitchVelocity = 0.0f;
static Vec3f lastEye = { 0.0f, 0.0f, 0.0f };
static Vec3f lastEyeVelocity = { 0.0f, 0.0f, 0.0f };
Vec3f* eye = &camera->eye;
Vec3f* at = &camera->at;
Vec3f eyeVelo;
bool shouldInterpolate = true;
VecGeo eyeGeo = OLib_Vec3fDiffToVecGeo(at, eye);
Vec3f expectedEye;
// Calculate Current
f32 yaw = BINANG_TO_DEG(eyeGeo.yaw);
if (yaw > 360.0f) {
yaw -= 360.0f;
} else if (yaw < 0.0f) {
yaw += 360.0f;
}
f32 yawVelocity = yaw - lastYaw;
f32 pitch = BINANG_TO_DEG(eyeGeo.pitch);
f32 pitchVelocity = pitch - lastPitch;
Math_Vec3f_Diff(eye, &lastEye, &eyeVelo);
// Update static variables.
lastYaw = yaw;
lastYawVelocity = yawVelocity;
lastPitch = pitch;
lastPitchVelocity = pitchVelocity;
lastEye = *eye;
lastEyeVelocity = eyeVelo;
if (Camera_ShouldOverrideInterpolationCheck(camera)) {
return true;
}
// Calculate Expected
f32 expectedYaw = lastYaw + lastYawVelocity;
if (expectedYaw > 360.0f) {
expectedYaw -= 360.0f;
} else if (expectedYaw < 0.0f) {
expectedYaw += 360.0f;
}
f32 expectedPitch = CLAMP(lastPitch + lastPitchVelocity, -180.0f, 180.0f);
Math_Vec3f_Sum(&lastEye, &lastEyeVelocity, &expectedEye);
// Check if changes are too great
f32 diffYaw = fabsf(yaw - expectedYaw);
f32 diffPitch = fabsf(pitch - expectedPitch);
f32 diffDistEye = Math_Vec3f_DistXYZ(eye, &expectedEye);
if ((diffYaw > 90.0f && diffYaw < 270.0f) || diffPitch > 60.0f || diffDistEye > 200.0f) {
shouldInterpolate = false;
}
// If we aren't interpolating, then reset velocities as they are inaccurate to the camera's current movement
if (!shouldInterpolate) {
lastYawVelocity = 0.0f;
lastPitchVelocity = 0.0f;
lastEyeVelocity = { 0.0f, 0.0f, 0.0f };
}
return shouldInterpolate;
}
void RegisterCameraInterpolationFixes() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::AfterCameraUpdate>(
[](Camera* camera) { FrameInterpolation_ShouldInterpolateFrame(Camera_ShouldInterpolateDist(camera)); });
}
@@ -0,0 +1,6 @@
#ifndef CAMERA_INTERPOLATE_FIXES_H
#define CAMERA_INTERPOLATE_FIXES_H
void RegisterCameraInterpolationFixes();
#endif // CAMERA_INTERPOLATE_FIXES_H
+1
View File
@@ -2,6 +2,7 @@
void InitEnhancements() {
// Camera
RegisterCameraInterpolationFixes();
RegisterCameraFreeLook();
RegisterDebugCam();
+1
View File
@@ -1,6 +1,7 @@
#ifndef ENHANCEMENTS_H
#define ENHANCEMENTS_H
#include "Camera/CameraInterpolationFixes.h"
#include "Camera/DebugCam.h"
#include "Camera/FreeLook.h"
#include "Cheats/MoonJump.h"
@@ -451,11 +451,23 @@ unordered_map<Mtx*, MtxF> FrameInterpolation_Interpolate(float step) {
return ctx.mtx_replacements;
}
bool camera_interpolation = false;
void FrameInterpolation_ShouldInterpolateFrame(bool shouldInterpolate) {
camera_interpolation = shouldInterpolate;
}
void FrameInterpolation_StartRecord(void) {
previous_recording = move(current_recording);
current_recording = {};
current_path.clear();
current_path.push_back(&current_recording.root_path);
if (!camera_interpolation) {
// default to interpolating
camera_interpolation = true;
is_recording = false;
return;
}
if (OTRGlobals::Instance->GetInterpolationFPS() != 20) {
is_recording = true;
}
@@ -12,6 +12,8 @@ extern "C" {
#endif
void FrameInterpolation_ShouldInterpolateFrame(bool shouldInterpolate);
void FrameInterpolation_StartRecord(void);
void FrameInterpolation_StopRecord(void);
@@ -141,6 +141,21 @@ void GameInteractor_ExecuteOnCameraChangeModeFlags(Camera* camera) {
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnCameraChangeModeFlags>(camera);
}
void GameInteractor_ExecuteAfterCameraUpdate(Camera* camera) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::AfterCameraUpdate>(camera);
GameInteractor::Instance->ExecuteHooksForID<GameInteractor::AfterCameraUpdate>(camera->uid, camera);
GameInteractor::Instance->ExecuteHooksForPtr<GameInteractor::AfterCameraUpdate>((uintptr_t)camera, camera);
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::AfterCameraUpdate>(camera);
}
void GameInteractor_ExecuteOnCameraChangeSettingsFlags(Camera* camera) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnCameraChangeSettingsFlags>(camera);
GameInteractor::Instance->ExecuteHooksForID<GameInteractor::OnCameraChangeSettingsFlags>(camera->uid, camera);
GameInteractor::Instance->ExecuteHooksForPtr<GameInteractor::OnCameraChangeSettingsFlags>((uintptr_t)camera,
camera);
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnCameraChangeSettingsFlags>(camera);
}
void GameInteractor_ExecuteOnPassPlayerInputs(Input* input) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPassPlayerInputs>(input);
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnPassPlayerInputs>(input);
@@ -269,7 +269,9 @@ class GameInteractor {
DEFINE_HOOK(OnFlagSet, (FlagType flagType, u32 flag));
DEFINE_HOOK(OnFlagUnset, (FlagType flagType, u32 flag));
DEFINE_HOOK(AfterCameraUpdate, (Camera * camera));
DEFINE_HOOK(OnCameraChangeModeFlags, (Camera * camera));
DEFINE_HOOK(OnCameraChangeSettingsFlags, (Camera * camera));
DEFINE_HOOK(OnPassPlayerInputs, (Input * input));
@@ -308,7 +310,9 @@ void GameInteractor_ExecuteOnSceneFlagUnset(s16 sceneId, FlagType flagType, u32
void GameInteractor_ExecuteOnFlagSet(FlagType flagType, u32 flag);
void GameInteractor_ExecuteOnFlagUnset(FlagType flagType, u32 flag);
void GameInteractor_ExecuteAfterCameraUpdate(Camera* camera);
void GameInteractor_ExecuteOnCameraChangeModeFlags(Camera* camera);
void GameInteractor_ExecuteOnCameraChangeSettingsFlags(Camera* camera);
void GameInteractor_ExecuteOnPassPlayerInputs(Input* input);
+4
View File
@@ -7635,6 +7635,8 @@ Vec3s Camera_Update(Camera* camera) {
camera->inputDir.z = 0;
}
GameInteractor_ExecuteAfterCameraUpdate(camera);
return camera->inputDir;
}
@@ -7885,6 +7887,8 @@ s16 Camera_ChangeSettingFlags(Camera* camera, s16 setting, s16 flags) {
camera->setting = setting;
GameInteractor_ExecuteOnCameraChangeSettingsFlags(camera);
if (Camera_ChangeModeFlags(camera, camera->mode, true) >= 0) {
Camera_ResetActionFuncState(camera, camera->mode);
}