From e70aaf2fe8d5bb530469ae8924689bc1dee0ea0e Mon Sep 17 00:00:00 2001 From: KebsCS Date: Thu, 26 Sep 2024 17:45:46 +0200 Subject: [PATCH] Add smooth spectating --- src/engine/shared/config_variables.h | 1 + src/game/client/components/camera.cpp | 85 +++++++++++++++++++++++++++ src/game/client/components/camera.h | 14 +++++ 3 files changed, 100 insertions(+) diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 92704b218..45859dec3 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -99,6 +99,7 @@ MACRO_CONFIG_INT(ClMultiViewSensitivity, cl_multiview_sensitivity, 100, 0, 200, MACRO_CONFIG_INT(ClMultiViewZoomSmoothness, cl_multiview_zoom_smoothness, 1300, 50, 5000, CFGFLAG_CLIENT | CFGFLAG_INSENSITIVE, "Set the smoothness of the multi-view zoom (in ms, higher = slower)") MACRO_CONFIG_INT(ClSpectatorMouseclicks, cl_spectator_mouseclicks, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Enables left-click to toggle between spectating the closest player and free-view") +MACRO_CONFIG_INT(ClSmoothSpectatingTime, cl_smooth_spectating_time, 300, 0, 5000, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Time of smooth camera switch animation when spectating in ms (0 for off)") MACRO_CONFIG_INT(EdAutosaveInterval, ed_autosave_interval, 10, 0, 240, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Interval in minutes at which a copy of the current editor map is automatically saved to the 'auto' folder (0 for off)") MACRO_CONFIG_INT(EdAutosaveMax, ed_autosave_max, 10, 0, 1000, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Maximum number of autosaves that are kept per map name (0 = no limit)") diff --git a/src/game/client/components/camera.cpp b/src/game/client/components/camera.cpp index 7a809bef1..307f3c326 100644 --- a/src/game/client/components/camera.cpp +++ b/src/game/client/components/camera.cpp @@ -30,6 +30,16 @@ CCamera::CCamera() mem_zero(m_aLastPos, sizeof(m_aLastPos)); m_PrevCenter = vec2(0, 0); m_Center = vec2(0, 0); + + m_PrevSpecId = -1; + m_WasSpectating = false; + + m_CameraSmoothing = false; +} + +float CCamera::CameraSmoothingProgress(float CurrentTime) const +{ + return (CurrentTime - m_CameraSmoothingStart) / (m_CameraSmoothingEnd - m_CameraSmoothingStart); } float CCamera::ZoomProgress(float CurrentTime) const @@ -101,6 +111,33 @@ void CCamera::OnRender() m_Zoom = clamp(m_Zoom, MinZoomLevel(), MaxZoomLevel()); } + if(m_CameraSmoothing) + { + if(!m_pClient->m_Snap.m_SpecInfo.m_Active) + { + m_Center = m_CameraSmoothingTarget; + m_CameraSmoothing = false; + } + else + { + float Time = Client()->LocalTime(); + if(Time >= m_CameraSmoothingEnd) + { + m_Center = m_CameraSmoothingTarget; + m_CameraSmoothing = false; + } + else + { + m_CameraSmoothingCenter = vec2(m_CameraSmoothingBezierX.Evaluate(CameraSmoothingProgress(Time)), m_CameraSmoothingBezierY.Evaluate(CameraSmoothingProgress(Time))); + if(distance(m_CameraSmoothingCenter, m_CameraSmoothingTarget) <= 0.1f) + { + m_Center = m_CameraSmoothingTarget; + m_CameraSmoothing = false; + } + } + } + } + if(!ZoomAllowed()) { m_ZoomSet = false; @@ -187,7 +224,53 @@ void CCamera::OnRender() else m_ForceFreeviewPos = m_Center; + const int SpecId = m_pClient->m_Snap.m_SpecInfo.m_SpectatorId; + + // start smoothing from the current position when the target changes + if(m_CameraSmoothing && SpecId != m_PrevSpecId) + m_CameraSmoothing = false; + + if(m_pClient->m_Snap.m_SpecInfo.m_Active && + (SpecId != m_PrevSpecId || + (m_CameraSmoothing && m_CameraSmoothingTarget != m_Center)) && // the target is moving during camera smoothing + !(!m_WasSpectating && m_Center != m_PrevCenter) && // dont smooth when starting to spectate + m_CamType != CAMTYPE_SPEC && + !GameClient()->m_MultiViewActivated) + { + float Now = Client()->LocalTime(); + if(!m_CameraSmoothing) + m_CenterBeforeSmoothing = m_PrevCenter; + + vec2 Derivative = {0.f, 0.f}; + if(m_CameraSmoothing) + { + float Progress = CameraSmoothingProgress(Now); + Derivative.x = m_CameraSmoothingBezierX.Derivative(Progress); + Derivative.y = m_CameraSmoothingBezierY.Derivative(Progress); + } + + m_CameraSmoothingTarget = m_Center; + m_CameraSmoothingBezierX = CCubicBezier::With(m_CenterBeforeSmoothing.x, Derivative.x, 0, m_CameraSmoothingTarget.x); + m_CameraSmoothingBezierY = CCubicBezier::With(m_CenterBeforeSmoothing.y, Derivative.y, 0, m_CameraSmoothingTarget.y); + + if(!m_CameraSmoothing) + { + m_CameraSmoothingStart = Now; + m_CameraSmoothingEnd = Now + (float)g_Config.m_ClSmoothSpectatingTime / 1000.0f; + } + + if(!m_CameraSmoothing) + m_CameraSmoothingCenter = m_PrevCenter; + + m_CameraSmoothing = true; + } + + if(m_CameraSmoothing) + m_Center = m_CameraSmoothingCenter; + m_PrevCenter = m_Center; + m_PrevSpecId = SpecId; + m_WasSpectating = m_pClient->m_Snap.m_SpecInfo.m_Active; } void CCamera::OnConsoleInit() @@ -203,6 +286,8 @@ void CCamera::OnConsoleInit() void CCamera::OnReset() { + m_CameraSmoothing = false; + m_Zoom = std::pow(CCamera::ZOOM_STEP, g_Config.m_ClDefaultZoom - 10); m_Zooming = false; } diff --git a/src/game/client/components/camera.h b/src/game/client/components/camera.h index 18d8ef84e..72b42230f 100644 --- a/src/game/client/components/camera.h +++ b/src/game/client/components/camera.h @@ -25,6 +25,20 @@ class CCamera : public CComponent vec2 m_aLastPos[NUM_DUMMIES]; vec2 m_PrevCenter; + int m_PrevSpecId; + bool m_WasSpectating; + + bool m_CameraSmoothing; + vec2 m_CameraSmoothingCenter; + vec2 m_CameraSmoothingTarget; + CCubicBezier m_CameraSmoothingBezierX; + CCubicBezier m_CameraSmoothingBezierY; + float m_CameraSmoothingStart; + float m_CameraSmoothingEnd; + vec2 m_CenterBeforeSmoothing; + + float CameraSmoothingProgress(float CurrentTime) const; + CCubicBezier m_ZoomSmoothing; float m_ZoomSmoothingStart; float m_ZoomSmoothingEnd;