Add smoothing of camera zoom using a cubic polynomial

This commit is contained in:
heinrich5991 2020-07-01 00:19:17 +02:00
parent 3578c74667
commit cdd715fd53
6 changed files with 123 additions and 86 deletions

View file

@ -1527,6 +1527,8 @@ set_src(ENGINE_SHARED GLOB src/engine/shared
websockets.h
)
set_src(GAME_SHARED GLOB src/game
bezier.cpp
bezier.h
collision.cpp
collision.h
ddracecommands.h

23
src/game/bezier.cpp Normal file
View file

@ -0,0 +1,23 @@
#include "bezier.h"
CCubicBezier CCubicBezier::With(float Start, float StartDerivative, float EndDerivative, float End)
{
return CCubicBezier(Start, Start + StartDerivative / 3, End - EndDerivative / 3, End);
}
// f(t) = (1-t)³ a + 3(1-t)²t b + 3(1-t)t² c + t³ d
float CCubicBezier::Evaluate(float t) const
{
return (1 - t) * (1 - t) * (1 - t) * a
+ 3 * (1 - t) * (1 - t) * t * b
+ 3 * (1 - t) * t * t * c
+ t * t * t * d;
}
// f(t) = 3(1-t)²(b-a) + 6(1-t)t(c-b) + 3t²(d-c)
float CCubicBezier::Derivative(float t) const
{
return 3 * (1 - t) * (1 - t) * (b - a)
+ 6 * (1 - t) * t * (c - b)
+ 3 * t * t * (d - c);
}

29
src/game/bezier.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef GAME_BEZIER_H
#define GAME_BEZIER_H
// Evaluates the Bernstein polynomial of degree 3/a one-dimensional Bezier curve
//
// https://en.wikipedia.org/w/index.php?title=Bernstein_polynomial&oldid=965314973
//
// f(t) = (1-t)³ a + 3(1-t)²t b + 3(1-t)t² c + t³ d
class CCubicBezier
{
float a;
float b;
float c;
float d;
CCubicBezier(float a, float b, float c, float d)
{
this->a = a;
this->b = b;
this->c = c;
this->d = d;
}
public:
CCubicBezier() {}
float Evaluate(float t) const;
float Derivative(float t) const;
static CCubicBezier With(float Start, float StartDerivative, float EndDerivative, float End);
};
#endif // GAME_BEZIER_H

View file

@ -6,49 +6,83 @@
#include <engine/shared/config.h>
#include <base/math.h>
#include <game/collision.h>
#include <game/client/gameclient.h>
#include <game/client/component.h>
#include <game/client/gameclient.h>
#include <game/collision.h>
#include "camera.h"
#include "controls.h"
#include <engine/serverbrowser.h>
const float ZoomStep = 0.866025f;
const float ZoomTime = 0.25f;
CCamera::CCamera()
{
m_CamType = CAMTYPE_UNDEFINED;
m_ZoomSet = false;
m_Zoom = 1.0f;
m_StartZoom = m_Zoom;
m_TargetZoom = m_Zoom;
m_ZoomAnimStartTick = 0;
m_ZoomAnimEndTick = 0;
m_Zooming = false;
}
float CCamera::ZoomProgress(float CurrentTime) const
{
return (CurrentTime - m_ZoomSmoothingStart) / (m_ZoomSmoothingEnd - m_ZoomSmoothingStart);
}
void CCamera::ScaleZoom(float Factor)
{
float CurrentTarget = m_Zooming ? m_ZoomSmoothingTarget : m_Zoom;
ChangeZoom(CurrentTarget * Factor);
}
void CCamera::ChangeZoom(float Target)
{
if(Target >= 500.0f/ZoomStep)
{
return;
}
float Now = Client()->LocalTime();
float Current = m_Zoom;
float Derivative = 0.0f;
if(m_Zooming)
{
float Progress = ZoomProgress(Now);
Current = m_ZoomSmoothing.Evaluate(Progress);
Derivative = m_ZoomSmoothing.Derivative(Progress);
}
m_ZoomSmoothingTarget = Target;
m_ZoomSmoothing = CCubicBezier::With(Current, Derivative, 0, m_ZoomSmoothingTarget);
m_ZoomSmoothingStart = Now;
m_ZoomSmoothingEnd = Now + ZoomTime;
m_Zooming = true;
}
void CCamera::OnRender()
{
if(IsZooming())
if(m_Zooming)
{
// The logistic function with default values give values near maximums and minimums on [-6, 6].
float ScaledProgress = ZoomProgress() * 12 - 6;
float Amount = 1.f / (1.f + exp(-ScaledProgress));
m_Zoom = mix(m_StartZoom, m_TargetZoom, Amount);
if(m_TargetZoom < m_StartZoom)
m_Zoom = clamp(m_Zoom, m_TargetZoom, m_StartZoom);
float Time = Client()->LocalTime();
if(Time >= m_ZoomSmoothingEnd)
{
m_Zoom = m_ZoomSmoothingTarget;
m_Zooming = false;
}
else
m_Zoom = clamp(m_Zoom, m_StartZoom, m_TargetZoom);
{
m_Zoom = m_ZoomSmoothing.Evaluate(ZoomProgress(Time));
}
}
if(!(m_pClient->m_Snap.m_SpecInfo.m_Active || GameClient()->m_GameInfo.m_AllowZoom || Client()->State() == IClient::STATE_DEMOPLAYBACK))
{
m_ZoomSet = false;
m_Zoom = 1.0f;
m_StartZoom = m_Zoom;
m_TargetZoom = m_Zoom;
m_ZoomAnimEndTick = 0;
m_Zooming = false;
}
else if(!m_ZoomSet && g_Config.m_ClDefaultZoom != 10)
{
@ -108,24 +142,10 @@ void CCamera::OnConsoleInit()
Console()->Register("zoom", "", CFGFLAG_CLIENT, ConZoomReset, this, "Zoom reset");
}
const float ZoomStep = 0.866025f;
void CCamera::OnReset()
{
m_Zoom = 1.0f;
if(g_Config.m_ClDefaultZoom < 10)
{
m_Zoom = pow(1/ZoomStep, 10 - g_Config.m_ClDefaultZoom);
}
else if(g_Config.m_ClDefaultZoom > 10)
{
m_Zoom = pow(ZoomStep, g_Config.m_ClDefaultZoom - 10);
}
m_StartZoom = m_Zoom;
m_TargetZoom = m_Zoom;
m_ZoomAnimEndTick = 0;
m_Zoom = pow(ZoomStep, g_Config.m_ClDefaultZoom - 10);
m_Zooming = false;
}
void CCamera::ConZoomPlus(IConsole::IResult *pResult, void *pUserData)
@ -133,10 +153,7 @@ void CCamera::ConZoomPlus(IConsole::IResult *pResult, void *pUserData)
CCamera *pSelf = (CCamera *)pUserData;
if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || pSelf->GameClient()->m_GameInfo.m_AllowZoom || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK)
{
if(g_Config.m_ClSmoothZoom)
pSelf->StartSmoothZoom(ZoomStep);
else
pSelf->m_Zoom *= ZoomStep;
pSelf->ScaleZoom(ZoomStep);
}
}
void CCamera::ConZoomMinus(IConsole::IResult *pResult, void *pUserData)
@ -144,44 +161,10 @@ void CCamera::ConZoomMinus(IConsole::IResult *pResult, void *pUserData)
CCamera *pSelf = (CCamera *)pUserData;
if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || pSelf->GameClient()->m_GameInfo.m_AllowZoom || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK)
{
if(pSelf->m_Zoom < 500.0f/ZoomStep)
{
if(g_Config.m_ClSmoothZoom)
pSelf->StartSmoothZoom(1/ZoomStep);
else
pSelf->m_Zoom *= 1/ZoomStep;
}
pSelf->ScaleZoom(1 / ZoomStep);
}
}
void CCamera::ConZoomReset(IConsole::IResult *pResult, void *pUserData)
{
((CCamera *)pUserData)->OnReset();
}
float CCamera::ZoomProgress()
{
int SmoothTick;
Client()->GetSmoothTick(&SmoothTick, NULL, 0);
return (SmoothTick - m_ZoomAnimStartTick) / (m_ZoomAnimEndTick - m_ZoomAnimStartTick);
}
bool CCamera::IsZooming()
{
return Client()->GameTick(g_Config.m_ClDummy) < m_ZoomAnimEndTick && m_ZoomAnimStartTick < m_ZoomAnimEndTick;
}
void CCamera::StartSmoothZoom(float ZoomStep)
{
// Check if we are in the middle of a smooth zoom already.
if(IsZooming())
{
// TODO: Implement
}
else
{
m_StartZoom = m_Zoom;
m_TargetZoom = m_StartZoom * ZoomStep;
m_ZoomAnimStartTick = Client()->GameTick(g_Config.m_ClDummy);
m_ZoomAnimEndTick = m_ZoomAnimStartTick + g_Config.m_ClSmoothZoomLength / 1000.f * Client()->GameTickSpeed();
}
((CCamera *)pUserData)->ChangeZoom(1.0f);
}

View file

@ -3,6 +3,7 @@
#ifndef GAME_CLIENT_COMPONENTS_CAMERA_H
#define GAME_CLIENT_COMPONENTS_CAMERA_H
#include <base/vmath.h>
#include <game/bezier.h>
#include <game/client/component.h>
class CCamera : public CComponent
@ -18,14 +19,20 @@ class CCamera : public CComponent
vec2 m_LastPos[2];
vec2 m_PrevCenter;
bool m_Zooming;
float m_ZoomSmoothingTarget;
CCubicBezier m_ZoomSmoothing;
float m_ZoomSmoothingStart;
float m_ZoomSmoothingEnd;
void ScaleZoom(float Factor);
void ChangeZoom(float Target);
float ZoomProgress(float CurrentTime) const;
public:
vec2 m_Center;
bool m_ZoomSet;
float m_StartZoom;
float m_Zoom;
float m_TargetZoom;
float m_ZoomAnimStartTick;
float m_ZoomAnimEndTick;
CCamera();
virtual void OnRender();
@ -35,11 +42,6 @@ public:
virtual void OnConsoleInit();
virtual void OnReset();
void StartSmoothZoom(float ZoomStep);
// Returns the zoom progress [0, 1]
float ZoomProgress();
bool IsZooming();
private:
static void ConZoomPlus(IConsole::IResult *pResult, void *pUserData);
static void ConZoomMinus(IConsole::IResult *pResult, void *pUserData);

View file

@ -82,8 +82,6 @@ MACRO_CONFIG_INT(ClAutoStatboardScreenshot, cl_auto_statboard_screenshot, 0, 0,
MACRO_CONFIG_INT(ClAutoStatboardScreenshotMax, cl_auto_statboard_screenshot_max, 10, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Maximum number of automatically created statboard screenshots (0 = no limit)")
MACRO_CONFIG_INT(ClDefaultZoom, cl_default_zoom, 10, 0, 20, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Default zoom level (10 default, min 0, max 20)")
MACRO_CONFIG_INT(ClSmoothZoom, cl_smooth_zoom, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Whether to enable smooth zoom.")
MACRO_CONFIG_INT(ClSmoothZoomLength, cl_smooth_zoom_length, 800, 1, 5000, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Zoom smooth animation length in miliseconds.")
MACRO_CONFIG_INT(ClPlayerUseCustomColor, player_use_custom_color, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Toggles usage of custom colors")
MACRO_CONFIG_COL(ClPlayerColorBody, player_color_body, 65408, CFGFLAG_CLIENT|CFGFLAG_SAVE|CFGFLAG_COLLIGHT, "Player body color")