Merge pull request #7355 from Robyt3/Client-Graph-SmoothTime-Refactoring

Refactor `CGraph` and `CSmoothTime`, move to separate compilation units, minor fixes to graph rendering
This commit is contained in:
Dennis Felsing 2023-10-17 20:56:05 +00:00 committed by GitHub
commit dad2c14abb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 328 additions and 271 deletions

View file

@ -2117,6 +2117,8 @@ if(CLIENT)
friends.h
ghost.cpp
ghost.h
graph.cpp
graph.h
graphics_defines.h
graphics_threaded.cpp
graphics_threaded.h
@ -2131,6 +2133,8 @@ if(CLIENT)
serverbrowser_http.h
serverbrowser_ping_cache.cpp
serverbrowser_ping_cache.h
smooth_time.cpp
smooth_time.h
sound.cpp
sound.h
sqlite.cpp

View file

@ -74,214 +74,6 @@ using namespace std::chrono_literals;
static const ColorRGBA gs_ClientNetworkPrintColor{0.7f, 1, 0.7f, 1.0f};
static const ColorRGBA gs_ClientNetworkErrPrintColor{1.0f, 0.25f, 0.25f, 1.0f};
void CGraph::Init(float Min, float Max)
{
SetMin(Min);
SetMax(Max);
m_Index = 0;
}
void CGraph::SetMin(float Min)
{
m_MinRange = m_Min = Min;
}
void CGraph::SetMax(float Max)
{
m_MaxRange = m_Max = Max;
}
void CGraph::Scale()
{
m_Min = m_MinRange;
m_Max = m_MaxRange;
for(auto Value : m_aValues)
{
if(Value > m_Max)
m_Max = Value;
else if(Value < m_Min)
m_Min = Value;
}
}
void CGraph::Add(float v, float r, float g, float b)
{
m_Index = (m_Index + 1) % MAX_VALUES;
InsertAt(m_Index, v, r, g, b);
}
void CGraph::InsertAt(size_t Index, float v, float r, float g, float b)
{
dbg_assert(Index < MAX_VALUES, "Index out of bounds");
m_aValues[Index] = v;
m_aColors[Index][0] = r;
m_aColors[Index][1] = g;
m_aColors[Index][2] = b;
}
void CGraph::Render(IGraphics *pGraphics, ITextRender *pTextRender, float x, float y, float w, float h, const char *pDescription)
{
pGraphics->TextureClear();
pGraphics->QuadsBegin();
pGraphics->SetColor(0.0f, 0.0f, 0.0f, 0.75f);
IGraphics::CQuadItem QuadItem(x, y, w, h);
pGraphics->QuadsDrawTL(&QuadItem, 1);
pGraphics->QuadsEnd();
pGraphics->LinesBegin();
pGraphics->SetColor(0.95f, 0.95f, 0.95f, 1.0f);
IGraphics::CLineItem LineItem(x, y + h / 2, x + w, y + h / 2);
pGraphics->LinesDraw(&LineItem, 1);
pGraphics->SetColor(0.5f, 0.5f, 0.5f, 0.75f);
IGraphics::CLineItem aLineItems[2] = {
IGraphics::CLineItem(x, y + (h * 3) / 4, x + w, y + (h * 3) / 4),
IGraphics::CLineItem(x, y + h / 4, x + w, y + h / 4)};
pGraphics->LinesDraw(aLineItems, std::size(aLineItems));
for(int i = 1; i < MAX_VALUES; i++)
{
float a0 = (i - 1) / (float)MAX_VALUES;
float a1 = i / (float)MAX_VALUES;
int i0 = (m_Index + i - 1) % MAX_VALUES;
int i1 = (m_Index + i) % MAX_VALUES;
float v0 = (m_aValues[i0] - m_Min) / (m_Max - m_Min);
float v1 = (m_aValues[i1] - m_Min) / (m_Max - m_Min);
IGraphics::CColorVertex aColorVertices[2] = {
IGraphics::CColorVertex(0, m_aColors[i0][0], m_aColors[i0][1], m_aColors[i0][2], 0.75f),
IGraphics::CColorVertex(1, m_aColors[i1][0], m_aColors[i1][1], m_aColors[i1][2], 0.75f)};
pGraphics->SetColorVertex(aColorVertices, std::size(aColorVertices));
IGraphics::CLineItem LineItem2(x + a0 * w, y + h - v0 * h, x + a1 * w, y + h - v1 * h);
pGraphics->LinesDraw(&LineItem2, 1);
}
pGraphics->LinesEnd();
const float FontSize = 12.0f;
const float Spacing = 2.0f;
pTextRender->Text(x + Spacing, y + h - FontSize - Spacing, FontSize, pDescription);
char aBuf[32];
str_format(aBuf, sizeof(aBuf), "%.2f", m_Max);
pTextRender->Text(x + w - pTextRender->TextWidth(FontSize, aBuf) - Spacing, y + Spacing, FontSize, aBuf);
str_format(aBuf, sizeof(aBuf), "%.2f", m_Min);
pTextRender->Text(x + w - pTextRender->TextWidth(FontSize, aBuf) - Spacing, y + h - FontSize - Spacing, FontSize, aBuf);
}
void CSmoothTime::Init(int64_t Target)
{
m_Snap = time_get();
m_Current = Target;
m_Target = Target;
m_SnapMargin = m_Snap;
m_CurrentMargin = 0;
m_TargetMargin = 0;
m_aAdjustSpeed[0] = 0.3f;
m_aAdjustSpeed[1] = 0.3f;
m_Graph.Init(0.0f, 0.5f);
}
void CSmoothTime::SetAdjustSpeed(int Direction, float Value)
{
m_aAdjustSpeed[Direction] = Value;
}
int64_t CSmoothTime::Get(int64_t Now)
{
int64_t c = m_Current + (Now - m_Snap);
int64_t t = m_Target + (Now - m_Snap);
// it's faster to adjust upward instead of downward
// we might need to adjust these abit
float AdjustSpeed = m_aAdjustSpeed[0];
if(t > c)
AdjustSpeed = m_aAdjustSpeed[1];
float a = ((Now - m_Snap) / (float)time_freq()) * AdjustSpeed;
if(a > 1.0f)
a = 1.0f;
int64_t r = c + (int64_t)((t - c) * a);
m_Graph.Add(a + 0.5f, 1, 1, 1);
return r + GetMargin(Now);
}
void CSmoothTime::UpdateInt(int64_t Target)
{
int64_t Now = time_get();
m_Current = Get(Now) - GetMargin(Now);
m_Snap = Now;
m_Target = Target - GetMargin(Now);
}
void CSmoothTime::Update(CGraph *pGraph, int64_t Target, int TimeLeft, int AdjustDirection)
{
int UpdateTimer = 1;
if(TimeLeft < 0)
{
int IsSpike = 0;
if(TimeLeft < -50)
{
IsSpike = 1;
m_SpikeCounter += 5;
if(m_SpikeCounter > 50)
m_SpikeCounter = 50;
}
if(IsSpike && m_SpikeCounter < 15)
{
// ignore this ping spike
UpdateTimer = 0;
pGraph->Add(TimeLeft, 1, 1, 0);
}
else
{
pGraph->Add(TimeLeft, 1, 0, 0);
if(m_aAdjustSpeed[AdjustDirection] < 30.0f)
m_aAdjustSpeed[AdjustDirection] *= 2.0f;
}
}
else
{
if(m_SpikeCounter)
m_SpikeCounter--;
pGraph->Add(TimeLeft, 0, 1, 0);
m_aAdjustSpeed[AdjustDirection] *= 0.95f;
if(m_aAdjustSpeed[AdjustDirection] < 2.0f)
m_aAdjustSpeed[AdjustDirection] = 2.0f;
}
if(UpdateTimer)
UpdateInt(Target);
}
int64_t CSmoothTime::GetMargin(int64_t Now)
{
int64_t TimePassed = Now - m_SnapMargin;
int64_t Diff = m_TargetMargin - m_CurrentMargin;
float a = clamp(TimePassed / (float)time_freq(), -1.f, 1.f);
int64_t Lim = maximum((int64_t)(a * absolute(Diff)), 1 + TimePassed / 100);
return m_CurrentMargin + (int64_t)clamp(Diff, -Lim, Lim);
}
void CSmoothTime::UpdateMargin(int64_t TargetMargin)
{
int64_t Now = time_get();
m_CurrentMargin = GetMargin(Now);
m_SnapMargin = Now;
m_TargetMargin = TargetMargin;
}
CClient::CClient() :
m_DemoPlayer(&m_SnapshotDelta, true, [&]() { UpdateDemoIntraTimers(); })
{
@ -1920,7 +1712,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
}
if(Target)
m_PredictedTime.Update(&m_InputtimeMarginGraph, Target, TimeLeft, 1);
m_PredictedTime.Update(&m_InputtimeMarginGraph, Target, TimeLeft, CSmoothTime::ADJUSTDIRECTION_UP);
}
else if(Msg == NETMSG_SNAP || Msg == NETMSG_SNAPSINGLE || Msg == NETMSG_SNAPEMPTY)
{
@ -2109,7 +1901,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
if(!Dummy)
{
m_PredictedTime.Init(GameTick * time_freq() / 50);
m_PredictedTime.SetAdjustSpeed(1, 1000.0f);
m_PredictedTime.SetAdjustSpeed(CSmoothTime::ADJUSTDIRECTION_UP, 1000.0f);
m_PredictedTime.UpdateMargin(PredictionMargin() * time_freq() / 1000);
}
m_aGameTime[Conn].Init((GameTick - 1) * time_freq() / 50);
@ -2136,7 +1928,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
int64_t Now = m_aGameTime[Conn].Get(time_get());
int64_t TickStart = GameTick * time_freq() / 50;
int64_t TimeLeft = (TickStart - Now) * 1000 / time_freq();
m_aGameTime[Conn].Update(&m_GametimeMarginGraph, (GameTick - 1) * time_freq() / 50, TimeLeft, 0);
m_aGameTime[Conn].Update(&m_GametimeMarginGraph, (GameTick - 1) * time_freq() / 50, TimeLeft, CSmoothTime::ADJUSTDIRECTION_DOWN);
}
if(m_aReceivedSnapshots[Conn] > 50 && !m_aCodeRunAfterJoin[Conn])
@ -3225,7 +3017,7 @@ void CClient::Run()
{
// update frametime
m_RenderFrameTime = (Now - m_LastRenderTime) / (float)time_freq();
m_FpsGraph.Add(1.0f / m_RenderFrameTime, 1, 1, 1);
m_FpsGraph.Add(1.0f / m_RenderFrameTime);
if(m_BenchmarkFile)
{

View file

@ -23,6 +23,9 @@
#include <engine/shared/network.h>
#include <engine/warning.h>
#include "graph.h"
#include "smooth_time.h"
class CDemoEdit;
class IDemoRecorder;
class CMsgPacker;
@ -40,60 +43,6 @@ class IUpdater;
#define CONNECTLINK_DOUBLE_SLASH "ddnet://"
#define CONNECTLINK_NO_SLASH "ddnet:"
class CGraph
{
public:
enum
{
MAX_VALUES = 128,
};
private:
float m_Min, m_Max;
float m_MinRange, m_MaxRange;
float m_aValues[MAX_VALUES];
float m_aColors[MAX_VALUES][3];
size_t m_Index;
public:
void Init(float Min, float Max);
void SetMin(float Min);
void SetMax(float Max);
void Scale();
void Add(float v, float r, float g, float b);
void InsertAt(size_t Index, float v, float r, float g, float b);
void Render(IGraphics *pGraphics, ITextRender *pTextRender, float x, float y, float w, float h, const char *pDescription);
};
class CSmoothTime
{
int64_t m_Snap;
int64_t m_Current;
int64_t m_Target;
int64_t m_SnapMargin;
int64_t m_CurrentMargin;
int64_t m_TargetMargin;
CGraph m_Graph;
int m_SpikeCounter;
float m_aAdjustSpeed[2]; // 0 = down, 1 = up
public:
void Init(int64_t Target);
void SetAdjustSpeed(int Direction, float Value);
int64_t Get(int64_t Now);
void UpdateInt(int64_t Target);
void Update(CGraph *pGraph, int64_t Target, int TimeLeft, int AdjustDirection);
int64_t GetMargin(int64_t Now);
void UpdateMargin(int64_t TargetMargin);
};
class CServerCapabilities
{
public:

105
src/engine/client/graph.cpp Normal file
View file

@ -0,0 +1,105 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <engine/graphics.h>
#include <engine/textrender.h>
#include "graph.h"
void CGraph::Init(float Min, float Max)
{
SetMin(Min);
SetMax(Max);
m_Index = 0;
for(auto &Entry : m_aEntries)
Entry.m_Initialized = false;
}
void CGraph::SetMin(float Min)
{
m_MinRange = m_Min = Min;
}
void CGraph::SetMax(float Max)
{
m_MaxRange = m_Max = Max;
}
void CGraph::Scale()
{
m_Min = m_MinRange;
m_Max = m_MaxRange;
for(auto &Entry : m_aEntries)
{
if(Entry.m_Value > m_Max)
m_Max = Entry.m_Value;
else if(Entry.m_Value < m_Min)
m_Min = Entry.m_Value;
}
}
void CGraph::Add(float Value, ColorRGBA Color)
{
InsertAt(m_Index, Value, Color);
m_Index = (m_Index + 1) % MAX_VALUES;
}
void CGraph::InsertAt(size_t Index, float Value, ColorRGBA Color)
{
dbg_assert(Index < MAX_VALUES, "Index out of bounds");
m_aEntries[Index].m_Initialized = true;
m_aEntries[Index].m_Value = Value;
m_aEntries[Index].m_Color = Color;
}
void CGraph::Render(IGraphics *pGraphics, ITextRender *pTextRender, float x, float y, float w, float h, const char *pDescription) const
{
pGraphics->TextureClear();
pGraphics->QuadsBegin();
pGraphics->SetColor(0.0f, 0.0f, 0.0f, 0.75f);
IGraphics::CQuadItem QuadItem(x, y, w, h);
pGraphics->QuadsDrawTL(&QuadItem, 1);
pGraphics->QuadsEnd();
pGraphics->LinesBegin();
pGraphics->SetColor(0.95f, 0.95f, 0.95f, 1.0f);
IGraphics::CLineItem LineItem(x, y + h / 2, x + w, y + h / 2);
pGraphics->LinesDraw(&LineItem, 1);
pGraphics->SetColor(0.5f, 0.5f, 0.5f, 0.75f);
IGraphics::CLineItem aLineItems[2] = {
IGraphics::CLineItem(x, y + (h * 3) / 4, x + w, y + (h * 3) / 4),
IGraphics::CLineItem(x, y + h / 4, x + w, y + h / 4)};
pGraphics->LinesDraw(aLineItems, std::size(aLineItems));
for(int i = 1; i < MAX_VALUES; i++)
{
const auto &Entry0 = m_aEntries[(m_Index + i - 1) % MAX_VALUES];
const auto &Entry1 = m_aEntries[(m_Index + i) % MAX_VALUES];
if(!Entry0.m_Initialized || !Entry1.m_Initialized)
continue;
float a0 = (i - 1) / (float)(MAX_VALUES - 1);
float a1 = i / (float)(MAX_VALUES - 1);
float v0 = (Entry0.m_Value - m_Min) / (m_Max - m_Min);
float v1 = (Entry1.m_Value - m_Min) / (m_Max - m_Min);
IGraphics::CColorVertex aColorVertices[2] = {
IGraphics::CColorVertex(0, Entry0.m_Color.r, Entry0.m_Color.g, Entry0.m_Color.b, Entry0.m_Color.a),
IGraphics::CColorVertex(1, Entry1.m_Color.r, Entry1.m_Color.g, Entry1.m_Color.b, Entry1.m_Color.a)};
pGraphics->SetColorVertex(aColorVertices, std::size(aColorVertices));
IGraphics::CLineItem LineItem2(x + a0 * w, y + h - v0 * h, x + a1 * w, y + h - v1 * h);
pGraphics->LinesDraw(&LineItem2, 1);
}
pGraphics->LinesEnd();
const float FontSize = 12.0f;
const float Spacing = 2.0f;
pTextRender->Text(x + Spacing, y + h - FontSize - Spacing, FontSize, pDescription);
char aBuf[32];
str_format(aBuf, sizeof(aBuf), "%.2f", m_Max);
pTextRender->Text(x + w - pTextRender->TextWidth(FontSize, aBuf) - Spacing, y + Spacing, FontSize, aBuf);
str_format(aBuf, sizeof(aBuf), "%.2f", m_Min);
pTextRender->Text(x + w - pTextRender->TextWidth(FontSize, aBuf) - Spacing, y + h - FontSize - Spacing, FontSize, aBuf);
}

45
src/engine/client/graph.h Normal file
View file

@ -0,0 +1,45 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef ENGINE_CLIENT_GRAPH_H
#define ENGINE_CLIENT_GRAPH_H
#include <base/color.h>
#include <cstddef>
class IGraphics;
class ITextRender;
class CGraph
{
public:
enum
{
MAX_VALUES = 128,
};
private:
struct SEntry
{
bool m_Initialized;
float m_Value;
ColorRGBA m_Color;
};
float m_Min, m_Max;
float m_MinRange, m_MaxRange;
SEntry m_aEntries[MAX_VALUES];
size_t m_Index;
public:
void Init(float Min, float Max);
void SetMin(float Min);
void SetMax(float Max);
void Scale();
void Add(float Value, ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.75f));
void InsertAt(size_t Index, float Value, ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.75f));
void Render(IGraphics *pGraphics, ITextRender *pTextRender, float x, float y, float w, float h, const char *pDescription) const;
};
#endif

View file

@ -0,0 +1,116 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/math.h>
#include <base/system.h>
#include "graph.h"
#include "smooth_time.h"
void CSmoothTime::Init(int64_t Target)
{
m_Snap = time_get();
m_Current = Target;
m_Target = Target;
m_SnapMargin = m_Snap;
m_CurrentMargin = 0;
m_TargetMargin = 0;
m_aAdjustSpeed[ADJUSTDIRECTION_DOWN] = 0.3f;
m_aAdjustSpeed[ADJUSTDIRECTION_UP] = 0.3f;
}
void CSmoothTime::SetAdjustSpeed(EAdjustDirection Direction, float Value)
{
m_aAdjustSpeed[Direction] = Value;
}
int64_t CSmoothTime::Get(int64_t Now) const
{
int64_t c = m_Current + (Now - m_Snap);
int64_t t = m_Target + (Now - m_Snap);
// it's faster to adjust upward instead of downward
// we might need to adjust these abit
float AdjustSpeed = m_aAdjustSpeed[ADJUSTDIRECTION_DOWN];
if(t > c)
AdjustSpeed = m_aAdjustSpeed[ADJUSTDIRECTION_UP];
float a = ((Now - m_Snap) / (float)time_freq()) * AdjustSpeed;
if(a > 1.0f)
a = 1.0f;
int64_t r = c + (int64_t)((t - c) * a);
return r + GetMargin(Now);
}
void CSmoothTime::UpdateInt(int64_t Target)
{
int64_t Now = time_get();
m_Current = Get(Now) - GetMargin(Now);
m_Snap = Now;
m_Target = Target - GetMargin(Now);
}
void CSmoothTime::Update(CGraph *pGraph, int64_t Target, int TimeLeft, EAdjustDirection AdjustDirection)
{
bool UpdateTimer = true;
if(TimeLeft < 0)
{
bool IsSpike = false;
if(TimeLeft < -50)
{
IsSpike = true;
m_SpikeCounter += 5;
if(m_SpikeCounter > 50)
m_SpikeCounter = 50;
}
if(IsSpike && m_SpikeCounter < 15)
{
// ignore this ping spike
UpdateTimer = false;
pGraph->Add(TimeLeft, ColorRGBA(1.0f, 1.0f, 0.0f, 0.75f));
}
else
{
pGraph->Add(TimeLeft, ColorRGBA(1.0f, 0.0f, 0.0f, 0.75f));
if(m_aAdjustSpeed[AdjustDirection] < 30.0f)
m_aAdjustSpeed[AdjustDirection] *= 2.0f;
}
}
else
{
if(m_SpikeCounter)
m_SpikeCounter--;
pGraph->Add(TimeLeft, ColorRGBA(0.0f, 1.0f, 0.0f, 0.75f));
m_aAdjustSpeed[AdjustDirection] *= 0.95f;
if(m_aAdjustSpeed[AdjustDirection] < 2.0f)
m_aAdjustSpeed[AdjustDirection] = 2.0f;
}
if(UpdateTimer)
UpdateInt(Target);
}
int64_t CSmoothTime::GetMargin(int64_t Now) const
{
int64_t TimePassed = Now - m_SnapMargin;
int64_t Diff = m_TargetMargin - m_CurrentMargin;
float a = clamp(TimePassed / (float)time_freq(), -1.f, 1.f);
int64_t Lim = maximum((int64_t)(a * absolute(Diff)), 1 + TimePassed / 100);
return m_CurrentMargin + (int64_t)clamp(Diff, -Lim, Lim);
}
void CSmoothTime::UpdateMargin(int64_t TargetMargin)
{
int64_t Now = time_get();
m_CurrentMargin = GetMargin(Now);
m_SnapMargin = Now;
m_TargetMargin = TargetMargin;
}

View file

@ -0,0 +1,46 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef ENGINE_CLIENT_SMOOTH_TIME_H
#define ENGINE_CLIENT_SMOOTH_TIME_H
#include <cstdint>
class CGraph;
class CSmoothTime
{
public:
enum EAdjustDirection
{
ADJUSTDIRECTION_DOWN = 0,
ADJUSTDIRECTION_UP,
NUM_ADJUSTDIRECTIONS,
};
private:
int64_t m_Snap;
int64_t m_Current;
int64_t m_Target;
int64_t m_SnapMargin;
int64_t m_CurrentMargin;
int64_t m_TargetMargin;
int m_SpikeCounter;
float m_aAdjustSpeed[NUM_ADJUSTDIRECTIONS];
public:
void Init(int64_t Target);
void SetAdjustSpeed(EAdjustDirection Direction, float Value);
int64_t Get(int64_t Now) const;
void UpdateInt(int64_t Target);
void Update(CGraph *pGraph, int64_t Target, int TimeLeft, EAdjustDirection AdjustDirection);
int64_t GetMargin(int64_t Now) const;
void UpdateMargin(int64_t TargetMargin);
};
#endif

View file

@ -187,12 +187,12 @@ void CDebugHud::RenderTuning()
const float RampedSpeed = Speed * Ramp;
if(RampedSpeed >= PreviousRampedSpeed)
{
m_RampGraph.InsertAt(i, RampedSpeed / 32, 0, 1, 0);
m_RampGraph.InsertAt(i, RampedSpeed / 32, ColorRGBA(0.0f, 1.0f, 0.0f, 0.75f));
m_SpeedTurningPoint = Speed;
}
else
{
m_RampGraph.InsertAt(i, RampedSpeed / 32, 1, 0, 0);
m_RampGraph.InsertAt(i, RampedSpeed / 32, ColorRGBA(1.0f, 0.0f, 0.0f, 0.75f));
}
PreviousRampedSpeed = RampedSpeed;
}
@ -209,16 +209,16 @@ void CDebugHud::RenderTuning()
const float RampedSpeed = Speed * Ramp;
if(RampedSpeed >= PreviousRampedSpeed)
{
m_ZoomedInGraph.InsertAt(i, RampedSpeed / 32, 0, 1, 0);
m_ZoomedInGraph.InsertAt(i, RampedSpeed / 32, ColorRGBA(0.0f, 1.0f, 0.0f, 0.75f));
m_SpeedTurningPoint = Speed;
}
else
{
m_ZoomedInGraph.InsertAt(i, RampedSpeed / 32, 1, 0, 0);
m_ZoomedInGraph.InsertAt(i, RampedSpeed / 32, ColorRGBA(1.0f, 0.0f, 0.0f, 0.75f));
}
if(i == 0)
{
m_ZoomedInGraph.SetMin(RampedSpeed);
m_ZoomedInGraph.SetMin(RampedSpeed / 32);
}
PreviousRampedSpeed = RampedSpeed;
}