Files
inspectredcandGitHub 324cba6644 Fix Camera Interpolation For Sudden Camera Changes (#479)
* Fix Camera Interpolation On Sudden Movements

* huge formatting discovery

* increase angle tolerance
2024-05-26 19:13:25 -05:00

110 lines
3.7 KiB
C++

#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)); });
}