Merge pull request #7303 from Marmare314/refactor-env-editor

Refactor `CEnvelope` class
This commit is contained in:
Robert Müller 2023-10-06 11:29:08 +00:00 committed by GitHub
commit 48a92f1eac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 207 additions and 151 deletions

View file

@ -2276,6 +2276,8 @@ if(CLIENT)
map_grid.h
map_view.cpp
map_view.h
mapitems/envelope.cpp
mapitems/envelope.h
mapitems/image.cpp
mapitems/image.h
mapitems/layer.h

View file

@ -87,6 +87,7 @@ enum
class IEnvelopePointAccess
{
public:
virtual ~IEnvelopePointAccess() = default;
virtual int NumPoints() const = 0;
virtual const CEnvPoint *GetPoint(int Index) const = 0;
virtual const CEnvPointBezier *GetBezier(int Index) const = 0;

View file

@ -5306,9 +5306,7 @@ void CEditor::UpdateZoomEnvelopeY(const CUIRect &View)
void CEditor::ResetZoomEnvelope(const std::shared_ptr<CEnvelope> &pEnvelope, int ActiveChannels)
{
pEnvelope->FindTopBottom(ActiveChannels);
float Top = pEnvelope->m_Top;
float Bottom = pEnvelope->m_Bottom;
auto [Bottom, Top] = pEnvelope->GetValueRange(ActiveChannels);
float EndTime = pEnvelope->EndTime();
float ValueRange = absolute(Top - Bottom);
@ -5599,7 +5597,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
if(DoButton_Editor(&s_NewSoundButton, "Sound+", 0, &Button, 0, "Creates a new sound envelope"))
{
m_Map.OnModify();
pNewEnv = m_Map.NewEnvelope(1);
pNewEnv = m_Map.NewEnvelope(CEnvelope::EType::SOUND);
}
ToolBar.VSplitRight(5.0f, &ToolBar, nullptr);
@ -5608,7 +5606,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
if(DoButton_Editor(&s_New4dButton, "Color+", 0, &Button, 0, "Creates a new color envelope"))
{
m_Map.OnModify();
pNewEnv = m_Map.NewEnvelope(4);
pNewEnv = m_Map.NewEnvelope(CEnvelope::EType::COLOR);
}
ToolBar.VSplitRight(5.0f, &ToolBar, nullptr);
@ -5617,7 +5615,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
if(DoButton_Editor(&s_New2dButton, "Pos.+", 0, &Button, 0, "Creates a new position envelope"))
{
m_Map.OnModify();
pNewEnv = m_Map.NewEnvelope(3);
pNewEnv = m_Map.NewEnvelope(CEnvelope::EType::POSITION);
}
if(m_SelectedEnvelope >= 0)
@ -5962,6 +5960,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
}
{
using namespace std::chrono_literals;
CTimeStep UnitsPerLineX = 1ms;
static const CTimeStep s_aUnitPerLineOptionsX[] = {5ms, 10ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, 2s, 5s, 10s, 15s, 30s, 1min};
for(CTimeStep Value : s_aUnitPerLineOptionsX)

View file

@ -11,6 +11,7 @@
#include <game/mapitems.h>
#include <game/mapitems_ex.h>
#include <game/editor/mapitems/envelope.h>
#include <game/editor/mapitems/layer.h>
#include <game/editor/mapitems/layer_front.h>
#include <game/editor/mapitems/layer_game.h>
@ -33,7 +34,6 @@
#include "map_view.h"
#include "smooth_value.h"
#include <chrono>
#include <deque>
#include <functional>
#include <map>
@ -41,8 +41,6 @@
#include <string>
#include <vector>
using namespace std::chrono_literals;
typedef std::function<void(int *pIndex)> FIndexModifyFunction;
// CEditor SPECIFIC
@ -57,146 +55,6 @@ enum
DIALOG_FILE,
};
class CEnvelope
{
class CEnvelopePointAccess : public IEnvelopePointAccess
{
std::vector<CEnvPoint_runtime> *m_pvPoints;
public:
CEnvelopePointAccess(std::vector<CEnvPoint_runtime> *pvPoints)
{
m_pvPoints = pvPoints;
}
int NumPoints() const override
{
return m_pvPoints->size();
}
const CEnvPoint *GetPoint(int Index) const override
{
if(Index < 0 || (size_t)Index >= m_pvPoints->size())
return nullptr;
return &m_pvPoints->at(Index);
}
const CEnvPointBezier *GetBezier(int Index) const override
{
if(Index < 0 || (size_t)Index >= m_pvPoints->size())
return nullptr;
return &m_pvPoints->at(Index).m_Bezier;
}
};
int m_Channels;
public:
std::vector<CEnvPoint_runtime> m_vPoints;
CEnvelopePointAccess m_PointsAccess;
char m_aName[32];
float m_Bottom, m_Top;
bool m_Synchronized;
CEnvelope(int Channels) :
m_PointsAccess(&m_vPoints)
{
SetChannels(Channels);
m_aName[0] = '\0';
m_Bottom = 0;
m_Top = 0;
m_Synchronized = false;
}
void Resort()
{
std::sort(m_vPoints.begin(), m_vPoints.end());
FindTopBottom(0xf);
}
void FindTopBottom(int ChannelMask)
{
m_Top = -1000000000.0f;
m_Bottom = 1000000000.0f;
CEnvPoint_runtime *pPrevPoint = nullptr;
for(auto &Point : m_vPoints)
{
for(int c = 0; c < m_Channels; c++)
{
if(ChannelMask & (1 << c))
{
{
// value handle
const float v = fx2f(Point.m_aValues[c]);
m_Top = maximum(m_Top, v);
m_Bottom = minimum(m_Bottom, v);
}
if(Point.m_Curvetype == CURVETYPE_BEZIER)
{
// out-tangent handle
const float v = fx2f(Point.m_aValues[c] + Point.m_Bezier.m_aOutTangentDeltaY[c]);
m_Top = maximum(m_Top, v);
m_Bottom = minimum(m_Bottom, v);
}
if(pPrevPoint != nullptr && pPrevPoint->m_Curvetype == CURVETYPE_BEZIER)
{
// in-tangent handle
const float v = fx2f(Point.m_aValues[c] + Point.m_Bezier.m_aInTangentDeltaY[c]);
m_Top = maximum(m_Top, v);
m_Bottom = minimum(m_Bottom, v);
}
}
}
pPrevPoint = &Point;
}
}
int Eval(float Time, ColorRGBA &Color)
{
CRenderTools::RenderEvalEnvelope(&m_PointsAccess, m_Channels, std::chrono::nanoseconds((int64_t)((double)Time * (double)std::chrono::nanoseconds(1s).count())), Color);
return m_Channels;
}
void AddPoint(int Time, int v0, int v1 = 0, int v2 = 0, int v3 = 0)
{
CEnvPoint_runtime p;
p.m_Time = Time;
p.m_aValues[0] = v0;
p.m_aValues[1] = v1;
p.m_aValues[2] = v2;
p.m_aValues[3] = v3;
p.m_Curvetype = CURVETYPE_LINEAR;
for(int c = 0; c < CEnvPoint::MAX_CHANNELS; c++)
{
p.m_Bezier.m_aInTangentDeltaX[c] = 0;
p.m_Bezier.m_aInTangentDeltaY[c] = 0;
p.m_Bezier.m_aOutTangentDeltaX[c] = 0;
p.m_Bezier.m_aOutTangentDeltaY[c] = 0;
}
m_vPoints.push_back(p);
Resort();
}
float EndTime() const
{
if(m_vPoints.empty())
return 0.0f;
return m_vPoints.back().m_Time / 1000.0f;
}
int GetChannels() const
{
return m_Channels;
}
void SetChannels(int Channels)
{
m_Channels = clamp<int>(Channels, 1, CEnvPoint::MAX_CHANNELS);
}
};
class CEditorImage;
class CEditorSound;
@ -271,10 +129,10 @@ public:
std::shared_ptr<class CLayerGame> m_pGameLayer;
std::shared_ptr<CLayerGroup> m_pGameGroup;
std::shared_ptr<CEnvelope> NewEnvelope(int Channels)
std::shared_ptr<CEnvelope> NewEnvelope(CEnvelope::EType Type)
{
OnModify();
std::shared_ptr<CEnvelope> pEnv = std::make_shared<CEnvelope>(Channels);
std::shared_ptr<CEnvelope> pEnv = std::make_shared<CEnvelope>(Type);
m_vpEnvelopes.push_back(pEnv);
return pEnv;
}

View file

@ -0,0 +1,148 @@
#include "envelope.h"
#include <algorithm>
#include <chrono>
#include <limits>
using namespace std::chrono_literals;
CEnvelope::CEnvelopePointAccess::CEnvelopePointAccess(std::vector<CEnvPoint_runtime> *pvPoints)
{
m_pvPoints = pvPoints;
}
int CEnvelope::CEnvelopePointAccess::NumPoints() const
{
return m_pvPoints->size();
}
const CEnvPoint *CEnvelope::CEnvelopePointAccess::GetPoint(int Index) const
{
if(Index < 0 || (size_t)Index >= m_pvPoints->size())
return nullptr;
return &m_pvPoints->at(Index);
}
const CEnvPointBezier *CEnvelope::CEnvelopePointAccess::GetBezier(int Index) const
{
if(Index < 0 || (size_t)Index >= m_pvPoints->size())
return nullptr;
return &m_pvPoints->at(Index).m_Bezier;
}
CEnvelope::CEnvelope(EType Type) :
m_Type(Type), m_PointsAccess(&m_vPoints) {}
CEnvelope::CEnvelope(int NumChannels) :
m_PointsAccess(&m_vPoints)
{
switch(NumChannels)
{
case 1:
m_Type = EType::SOUND;
break;
case 3:
m_Type = EType::POSITION;
break;
case 4:
m_Type = EType::COLOR;
break;
default:
dbg_assert(false, "invalid number of channels for envelope");
}
}
void CEnvelope::Resort()
{
std::sort(m_vPoints.begin(), m_vPoints.end());
}
std::pair<float, float> CEnvelope::GetValueRange(int ChannelMask)
{
float Top = -std::numeric_limits<float>::infinity();
float Bottom = std::numeric_limits<float>::infinity();
CEnvPoint_runtime *pPrevPoint = nullptr;
for(auto &Point : m_vPoints)
{
for(int c = 0; c < GetChannels(); c++)
{
if(ChannelMask & (1 << c))
{
{
// value handle
const float v = fx2f(Point.m_aValues[c]);
Top = maximum(Top, v);
Bottom = minimum(Bottom, v);
}
if(Point.m_Curvetype == CURVETYPE_BEZIER)
{
// out-tangent handle
const float v = fx2f(Point.m_aValues[c] + Point.m_Bezier.m_aOutTangentDeltaY[c]);
Top = maximum(Top, v);
Bottom = minimum(Bottom, v);
}
if(pPrevPoint != nullptr && pPrevPoint->m_Curvetype == CURVETYPE_BEZIER)
{
// in-tangent handle
const float v = fx2f(Point.m_aValues[c] + Point.m_Bezier.m_aInTangentDeltaY[c]);
Top = maximum(Top, v);
Bottom = minimum(Bottom, v);
}
}
}
pPrevPoint = &Point;
}
return {Bottom, Top};
}
int CEnvelope::Eval(float Time, ColorRGBA &Color)
{
CRenderTools::RenderEvalEnvelope(&m_PointsAccess, GetChannels(), std::chrono::nanoseconds((int64_t)((double)Time * (double)std::chrono::nanoseconds(1s).count())), Color);
return GetChannels();
}
void CEnvelope::AddPoint(int Time, int v0, int v1, int v2, int v3)
{
CEnvPoint_runtime p;
p.m_Time = Time;
p.m_aValues[0] = v0;
p.m_aValues[1] = v1;
p.m_aValues[2] = v2;
p.m_aValues[3] = v3;
p.m_Curvetype = CURVETYPE_LINEAR;
for(int c = 0; c < CEnvPoint::MAX_CHANNELS; c++)
{
p.m_Bezier.m_aInTangentDeltaX[c] = 0;
p.m_Bezier.m_aInTangentDeltaY[c] = 0;
p.m_Bezier.m_aOutTangentDeltaX[c] = 0;
p.m_Bezier.m_aOutTangentDeltaY[c] = 0;
}
m_vPoints.push_back(p);
Resort();
}
float CEnvelope::EndTime() const
{
if(m_vPoints.empty())
return 0.0f;
return m_vPoints.back().m_Time / 1000.0f;
}
int CEnvelope::GetChannels() const
{
switch(m_Type)
{
case EType::POSITION:
return 3;
case EType::COLOR:
return 4;
case EType::SOUND:
return 1;
default:
dbg_assert(false, "unknown envelope type");
dbg_break();
}
}

View file

@ -0,0 +1,48 @@
#ifndef GAME_EDITOR_MAPITEMS_ENVELOPE_H
#define GAME_EDITOR_MAPITEMS_ENVELOPE_H
#include <game/client/render.h>
#include <game/mapitems.h>
class CEnvelope
{
public:
std::vector<CEnvPoint_runtime> m_vPoints;
char m_aName[32] = "";
bool m_Synchronized = false;
enum class EType
{
POSITION,
COLOR,
SOUND
};
explicit CEnvelope(EType Type);
explicit CEnvelope(int NumChannels);
std::pair<float, float> GetValueRange(int ChannelMask);
int Eval(float Time, ColorRGBA &Color);
void AddPoint(int Time, int v0, int v1 = 0, int v2 = 0, int v3 = 0);
float EndTime() const;
int GetChannels() const;
private:
void Resort();
EType m_Type;
class CEnvelopePointAccess : public IEnvelopePointAccess
{
std::vector<CEnvPoint_runtime> *m_pvPoints;
public:
CEnvelopePointAccess(std::vector<CEnvPoint_runtime> *pvPoints);
int NumPoints() const override;
const CEnvPoint *GetPoint(int Index) const override;
const CEnvPointBezier *GetBezier(int Index) const override;
};
CEnvelopePointAccess m_PointsAccess;
};
#endif