mirror of
https://github.com/ddnet/ddnet.git
synced 2024-09-20 01:24:18 +00:00
Add smoothing of camera zoom using a cubic polynomial
This commit is contained in:
parent
3578c74667
commit
cdd715fd53
|
@ -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
23
src/game/bezier.cpp
Normal 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
29
src/game/bezier.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in a new issue