Merge pull request #6885 from Marmare314/envelope_zoom

Zoomable envelope editor
This commit is contained in:
Robert Müller 2023-07-24 21:22:27 +00:00 committed by GitHub
commit de3b2b170c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 524 additions and 202 deletions

View file

@ -1082,7 +1082,7 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
static int s_ZoomOutButton = 0;
if(DoButton_FontIcon(&s_ZoomOutButton, "-", 0, &Button, 0, "[NumPad-] Zoom out", IGraphics::CORNER_L))
{
ChangeZoom(50.0f);
m_ZoomMapView.ChangeZoom(50.0f);
}
TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
@ -1091,14 +1091,14 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
{
m_EditorOffsetX = 0;
m_EditorOffsetY = 0;
SetZoom(100.0f);
m_ZoomMapView.SetZoom(100.0f);
}
TB_Top.VSplitLeft(20.0f, &Button, &TB_Top);
static int s_ZoomInButton = 0;
if(DoButton_FontIcon(&s_ZoomInButton, "+", 0, &Button, 0, "[NumPad+] Zoom in", IGraphics::CORNER_R))
{
ChangeZoom(-50.0f);
m_ZoomMapView.ChangeZoom(-50.0f);
}
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
@ -2518,7 +2518,7 @@ void CEditor::DoMapEditor(CUIRect View)
}
CLayerTiles *pT = static_cast<CLayerTiles *>(GetSelectedLayerType(0, LAYERTYPE_TILES));
if(m_ShowTileInfo != SHOW_TILE_OFF && pT && pT->m_Visible && m_Zoom <= 300.0f)
if(m_ShowTileInfo != SHOW_TILE_OFF && pT && pT->m_Visible && m_ZoomMapView.GetZoom() <= 300.0f)
{
GetSelectedGroup()->MapScreen();
pT->ShowInfo();
@ -5601,6 +5601,128 @@ void CEditor::RemoveUnusedEnvelopes()
}
}
void CEditor::ZoomAdaptOffsetX(float ZoomFactor, const CUIRect &View)
{
if(g_Config.m_EdZoomTarget)
{
float PosX = (UI()->MouseX() - View.x) / View.w;
m_OffsetEnvelopeX += (m_OffsetEnvelopeX - PosX) * (1.0f - ZoomFactor);
}
else
m_OffsetEnvelopeX += (m_OffsetEnvelopeX - 0.5f) * (1.0f - ZoomFactor);
}
void CEditor::UpdateZoomEnvelopeX(const CUIRect &View)
{
float OldZoom = m_ZoomEnvelopeX.GetZoom();
if(m_ZoomEnvelopeX.UpdateZoom())
ZoomAdaptOffsetX(m_ZoomEnvelopeX.GetZoom() / OldZoom, View);
}
void CEditor::ZoomAdaptOffsetY(float ZoomFactor, const CUIRect &View)
{
if(g_Config.m_EdZoomTarget)
{
float PosY = 1.0f - (UI()->MouseY() - View.y) / View.h;
m_OffsetEnvelopeY += (m_OffsetEnvelopeY - PosY) * (1.0f - ZoomFactor);
}
else
m_OffsetEnvelopeY += (m_OffsetEnvelopeY - 0.5f) * (1.0f - ZoomFactor);
}
void CEditor::UpdateZoomEnvelopeY(const CUIRect &View)
{
float OldZoom = m_ZoomEnvelopeY.GetZoom();
if(m_ZoomEnvelopeY.UpdateZoom())
ZoomAdaptOffsetY(m_ZoomEnvelopeY.GetZoom() / OldZoom, View);
}
void CEditor::ResetZoomEnvelope(CEnvelope *pEnvelope, int ActiveChannels)
{
pEnvelope->FindTopBottom(ActiveChannels);
float Top = pEnvelope->m_Top;
float Bottom = pEnvelope->m_Bottom;
float EndTime = pEnvelope->EndTime();
float ValueRange = absolute(Top - Bottom);
if(ValueRange < 0.1f)
{
// Set view to some sane default if range is too small
m_OffsetEnvelopeY = 0.5f - Top / 0.1f;
m_ZoomEnvelopeY.SetZoomInstant(0.1f);
}
else
{
m_ZoomEnvelopeY.SetZoomInstant(1.25f * ValueRange);
if(Top >= 0 && Bottom >= 0)
m_OffsetEnvelopeY = 0.1f - Bottom / m_ZoomEnvelopeY.GetZoom();
else if(Top <= 0 && Bottom <= 0)
m_OffsetEnvelopeY = 0.1f - Bottom / m_ZoomEnvelopeY.GetZoom();
else
m_OffsetEnvelopeY = 0.1f + 0.8f * absolute(Bottom) / ValueRange;
}
if(EndTime < 0.1f)
{
m_OffsetEnvelopeX = 0.5f - EndTime / 0.1f;
m_ZoomEnvelopeX.SetZoomInstant(0.1f);
}
else
{
m_ZoomEnvelopeX.SetZoomInstant(1.25f * EndTime);
m_OffsetEnvelopeX = 0.1f;
}
}
float fxt2f(int t)
{
return t / 1000.0f;
}
int f2fxt(float t)
{
return static_cast<int>(t * 1000.0f);
}
float CEditor::ScreenToEnvelopeX(const CUIRect &View, float x) const
{
return (x - View.x - View.w * m_OffsetEnvelopeX) / View.w * m_ZoomEnvelopeX.GetZoom();
}
float CEditor::EnvelopeToScreenX(const CUIRect &View, float x) const
{
return View.x + View.w * m_OffsetEnvelopeX + x / m_ZoomEnvelopeX.GetZoom() * View.w;
}
float CEditor::ScreenToEnvelopeY(const CUIRect &View, float y) const
{
return (View.h - y + View.y) / View.h * m_ZoomEnvelopeY.GetZoom() - m_OffsetEnvelopeY * m_ZoomEnvelopeY.GetZoom();
}
float CEditor::EnvelopeToScreenY(const CUIRect &View, float y) const
{
return View.y + View.h - y / m_ZoomEnvelopeY.GetZoom() * View.h - m_OffsetEnvelopeY * View.h;
}
float CEditor::ScreenToEnvelopeDX(const CUIRect &View, float dx)
{
return dx / Graphics()->ScreenWidth() * UI()->Screen()->w / View.w * m_ZoomEnvelopeX.GetZoom();
}
float CEditor::ScreenToEnvelopeDY(const CUIRect &View, float dy)
{
return dy / Graphics()->ScreenHeight() * UI()->Screen()->h / View.h * m_ZoomEnvelopeY.GetZoom();
}
void CEditor::RemoveTimeOffsetEnvelope(CEnvelope *pEnvelope)
{
int TimeOffset = pEnvelope->m_vPoints[0].m_Time;
for(auto &Point : pEnvelope->m_vPoints)
Point.m_Time -= TimeOffset;
m_OffsetEnvelopeX += fxt2f(TimeOffset) / m_ZoomEnvelopeX.GetZoom();
};
void CEditor::RenderEnvelopeEditor(CUIRect View)
{
RenderExtraEditorDragBar(View, &m_EnvelopeEditorSplit);
@ -5614,6 +5736,17 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
if(m_SelectedEnvelope >= 0 && m_SelectedEnvelope < (int)m_Map.m_vpEnvelopes.size())
pEnvelope = m_Map.m_vpEnvelopes[m_SelectedEnvelope];
enum
{
OP_NONE,
OP_DRAG_POINT,
OP_DRAG_POINT_X,
OP_DRAG_POINT_Y
};
static int s_Operation = OP_NONE;
static float s_AccurateDragValueX = 0.0f;
static float s_AccurateDragValueY = 0.0f;
CUIRect ToolBar, CurveBar, ColorBar;
View.HSplitTop(15.0f, &ToolBar, &View);
View.HSplitTop(15.0f, &CurveBar, &View);
@ -5623,6 +5756,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
bool CurrentEnvelopeSwitched = false;
// do the toolbar
static int s_ActiveChannels = 0xf;
{
CUIRect Button;
CEnvelope *pNewEnv = nullptr;
@ -5688,6 +5822,15 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
pEnvelope = m_Map.m_vpEnvelopes[m_SelectedEnvelope];
}
if(pEnvelope)
{
ToolBar.VSplitRight(5.0f, &ToolBar, nullptr);
ToolBar.VSplitRight(20.0f, &ToolBar, &Button);
static int s_ResetZoomButton = 0;
if(DoButton_FontIcon(&s_ResetZoomButton, FONT_ICON_MAGNIFYING_GLASS, 0, &Button, 0, "Reset zoom to default value", IGraphics::CORNER_ALL, 9.0f))
ResetZoomEnvelope(pEnvelope, s_ActiveChannels);
}
// Margin on the right side
ToolBar.VSplitRight(7.0f, &ToolBar, nullptr);
}
@ -5771,15 +5914,19 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
ShowColorBar = true;
View.HSplitTop(20.0f, &ColorBar, &View);
ColorBar.Margin(2.0f, &ColorBar);
RenderBackground(ColorBar, m_CheckerTexture, 16.0f, 1.0f);
}
RenderBackground(View, m_CheckerTexture, 32.0f, 0.1f);
if(pEnvelope)
{
if(m_ResetZoomEnvelope)
{
m_ResetZoomEnvelope = false;
ResetZoomEnvelope(pEnvelope, s_ActiveChannels);
}
static int s_EnvelopeEditorID = 0;
static int s_ActiveChannels = 0xf;
ColorRGBA aColors[] = {ColorRGBA(1, 0.2f, 0.2f), ColorRGBA(0.2f, 1, 0.2f), ColorRGBA(0.2f, 0.2f, 1), ColorRGBA(1, 1, 0.2f)};
@ -5833,22 +5980,6 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
ToolBar.VSplitLeft(40.0f, &Button, &ToolBar);
UI()->DoLabel(&Button, "Sync.", 10.0f, TEXTALIGN_ML);
float EndTime = pEnvelope->EndTime();
if(EndTime < 1)
EndTime = 1;
pEnvelope->FindTopBottom(s_ActiveChannels);
float Top = pEnvelope->m_Top;
float Bottom = pEnvelope->m_Bottom;
if(Top < 1)
Top = 1;
if(Bottom >= 0)
Bottom = 0;
float TimeScale = EndTime / View.w;
float ValueScale = (Top - Bottom) / View.h;
if(UI()->MouseInside(&View))
UI()->SetHotItem(&s_EnvelopeEditorID);
@ -5858,19 +5989,47 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
if(UI()->MouseButtonClicked(1))
{
// add point
int Time = (int)(((UI()->MouseX() - View.x) * TimeScale) * 1000.0f);
float Time = ScreenToEnvelopeX(View, UI()->MouseX());
ColorRGBA Channels;
pEnvelope->Eval(Time / 1000.0f, Channels);
pEnvelope->AddPoint(Time,
if(in_range(Time, 0.0f, pEnvelope->EndTime()))
pEnvelope->Eval(Time, Channels);
else
Channels = {0, 0, 0, 0};
pEnvelope->AddPoint(f2fxt(Time),
f2fx(Channels.r), f2fx(Channels.g),
f2fx(Channels.b), f2fx(Channels.a));
if(Time < 0)
RemoveTimeOffsetEnvelope(pEnvelope);
m_Map.OnModify();
}
else if(UI()->MouseButton(2) || (UI()->MouseButton(0) && Input()->ModifierIsPressed()))
{
m_OffsetEnvelopeX += UI()->MouseDeltaX() / Graphics()->ScreenWidth() * UI()->Screen()->w / View.w;
m_OffsetEnvelopeY -= UI()->MouseDeltaY() / Graphics()->ScreenHeight() * UI()->Screen()->h / View.h;
}
if(Input()->ShiftIsPressed())
{
if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN))
m_ZoomEnvelopeY.ChangeZoom(0.1f * m_ZoomEnvelopeY.GetZoom());
if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP))
m_ZoomEnvelopeY.ChangeZoom(-0.1f * m_ZoomEnvelopeY.GetZoom());
}
else
{
if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN))
m_ZoomEnvelopeX.ChangeZoom(0.1f * m_ZoomEnvelopeX.GetZoom());
if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP))
m_ZoomEnvelopeX.ChangeZoom(-0.1f * m_ZoomEnvelopeX.GetZoom());
}
m_ShowEnvelopePreview = SHOWENV_SELECTED;
m_pTooltip = "Press right mouse button to create a new point";
m_pTooltip = "Press right mouse button to create a new point. Use shift to change the zoom axis.";
}
UpdateZoomEnvelopeX(View);
UpdateZoomEnvelopeY(View);
// keep track of selected point to handle value/time text input
static const void *s_pSelectedPoint = nullptr;
@ -5886,36 +6045,36 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
for(int i = 0; i < (int)pEnvelope->m_vPoints.size(); i++)
{
const float PosX = pEnvelope->m_vPoints[i].m_Time / 1000.0f / EndTime;
const float PosY = (fx2f(pEnvelope->m_vPoints[i].m_aValues[c]) - Bottom) / (Top - Bottom);
float PosX = EnvelopeToScreenX(View, fxt2f(pEnvelope->m_vPoints[i].m_Time));
float PosY = EnvelopeToScreenY(View, fx2f(pEnvelope->m_vPoints[i].m_aValues[c]));
// Out-Tangent
if(pEnvelope->m_vPoints[i].m_Curvetype == CURVETYPE_BEZIER)
{
const float OutTangentX = PosX + pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c] / 1000.0f / EndTime;
const float OutTangentY = PosY + fx2f(pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaY[c]) / (Top - Bottom);
float TangentX = EnvelopeToScreenX(View, fxt2f(pEnvelope->m_vPoints[i].m_Time + pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c]));
float TangentY = EnvelopeToScreenY(View, fx2f(pEnvelope->m_vPoints[i].m_aValues[c] + pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaY[c]));
if(s_pSelectedPoint == &pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c] || (m_SelectedQuadEnvelope == m_SelectedEnvelope && m_SelectedEnvelopePoint == i))
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.4f);
else
Graphics()->SetColor(aColors[c].r, aColors[c].g, aColors[c].b, 0.4f);
IGraphics::CLineItem LineItem(View.x + PosX * View.w, View.y + View.h - PosY * View.h, View.x + OutTangentX * View.w, View.y + View.h - OutTangentY * View.h);
IGraphics::CLineItem LineItem(TangentX, TangentY, PosX, PosY);
Graphics()->LinesDraw(&LineItem, 1);
}
// In-Tangent
if(i > 0 && pEnvelope->m_vPoints[i - 1].m_Curvetype == CURVETYPE_BEZIER)
{
const float InTangentX = PosX + pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c] / 1000.0f / EndTime;
const float InTangentY = PosY + fx2f(pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaY[c]) / (Top - Bottom);
float TangentX = EnvelopeToScreenX(View, fxt2f(pEnvelope->m_vPoints[i].m_Time + pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c]));
float TangentY = EnvelopeToScreenY(View, fx2f(pEnvelope->m_vPoints[i].m_aValues[c] + pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaY[c]));
if(s_pSelectedPoint == &pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c] || (m_SelectedQuadEnvelope == m_SelectedEnvelope && m_SelectedEnvelopePoint == i))
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.4f);
else
Graphics()->SetColor(aColors[c].r, aColors[c].g, aColors[c].b, 0.4f);
IGraphics::CLineItem LineItem(View.x + PosX * View.w, View.y + View.h - PosY * View.h, View.x + InTangentX * View.w, View.y + View.h - InTangentY * View.h);
IGraphics::CLineItem LineItem(TangentX, TangentY, PosX, PosY);
Graphics()->LinesDraw(&LineItem, 1);
}
}
@ -5926,6 +6085,13 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
// render lines
{
float EndTimeTotal = maximum(0.000001f, pEnvelope->EndTime());
float EndX = clamp(EnvelopeToScreenX(View, EndTimeTotal), View.x, View.x + View.w);
float StartX = clamp(View.x + View.w * m_OffsetEnvelopeX, View.x, View.x + View.w);
float EndTime = ScreenToEnvelopeX(View, EndX);
float StartTime = ScreenToEnvelopeX(View, StartX);
UI()->ClipEnable(&View);
Graphics()->TextureClear();
Graphics()->LinesBegin();
@ -5936,23 +6102,26 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
else
Graphics()->SetColor(aColors[c].r * 0.5f, aColors[c].g * 0.5f, aColors[c].b * 0.5f, 1);
float PrevX = 0;
int Steps = static_cast<int>(((EndX - StartX) / UI()->Screen()->w) * Graphics()->ScreenWidth());
float StepTime = (EndTime - StartTime) / static_cast<float>(Steps);
float StepSize = (EndX - StartX) / static_cast<float>(Steps);
ColorRGBA Channels;
pEnvelope->Eval(0.000001f, Channels);
float PrevValue = Channels[c];
int Steps = (int)((View.w / UI()->Screen()->w) * Graphics()->ScreenWidth());
for(int i = 1; i <= Steps; i++)
pEnvelope->Eval(StartTime + StepTime, Channels);
float PrevY = EnvelopeToScreenY(View, Channels[c]);
for(int i = 2; i < Steps; i++)
{
float a = i / (float)Steps;
pEnvelope->Eval(a * EndTime, Channels);
float v = Channels[c];
v = (v - Bottom) / (Top - Bottom);
pEnvelope->Eval(StartTime + i * StepTime, Channels);
float CurrentY = EnvelopeToScreenY(View, Channels[c]);
IGraphics::CLineItem LineItem(View.x + PrevX * View.w, View.y + View.h - PrevValue * View.h, View.x + a * View.w, View.y + View.h - v * View.h);
IGraphics::CLineItem LineItem(
StartX + (i - 1) * StepSize,
PrevY,
StartX + i * StepSize,
CurrentY);
Graphics()->LinesDraw(&LineItem, 1);
PrevX = a;
PrevValue = v;
PrevY = CurrentY;
}
}
Graphics()->LinesEnd();
@ -5963,11 +6132,11 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
{
for(int i = 0; i < (int)pEnvelope->m_vPoints.size() - 1; i++)
{
float t0 = pEnvelope->m_vPoints[i].m_Time / 1000.0f / EndTime;
float t1 = pEnvelope->m_vPoints[i + 1].m_Time / 1000.0f / EndTime;
float t0 = fxt2f(pEnvelope->m_vPoints[i].m_Time);
float t1 = fxt2f(pEnvelope->m_vPoints[i + 1].m_Time);
CUIRect CurveButton;
CurveButton.x = CurveBar.x + (t0 + (t1 - t0) * 0.5f) * CurveBar.w;
CurveButton.x = EnvelopeToScreenX(View, t0 + (t1 - t0) * 0.5f);
CurveButton.y = CurveBar.y;
CurveButton.h = CurveBar.h;
CurveButton.w = CurveBar.h;
@ -5977,14 +6146,29 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
const char *pTypeName = "!?";
if(0 <= pEnvelope->m_vPoints[i].m_Curvetype && pEnvelope->m_vPoints[i].m_Curvetype < (int)std::size(apTypeName))
pTypeName = apTypeName[pEnvelope->m_vPoints[i].m_Curvetype];
if(CurveButton.x >= View.x)
{
if(DoButton_Editor(pID, pTypeName, 0, &CurveButton, 0, "Switch curve type (N = step, L = linear, S = slow, F = fast, M = smooth, B = bezier)"))
pEnvelope->m_vPoints[i].m_Curvetype = (pEnvelope->m_vPoints[i].m_Curvetype + 1) % NUM_CURVETYPES;
}
}
}
// render colorbar
if(ShowColorBar)
{
UI()->ClipEnable(&ColorBar);
float StartX = maximum(EnvelopeToScreenX(View, 0), ColorBar.x);
float EndX = EnvelopeToScreenX(View, pEnvelope->EndTime());
CUIRect BackgroundView{
StartX,
ColorBar.y,
minimum(EndX - StartX, ColorBar.x + ColorBar.w - StartX),
ColorBar.h};
RenderBackground(BackgroundView, m_CheckerTexture, 16.0f, 1.0f);
Graphics()->TextureClear();
Graphics()->QuadsBegin();
for(int i = 0; i < (int)pEnvelope->m_vPoints.size() - 1; i++)
@ -6004,13 +6188,14 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
IGraphics::CColorVertex(3, r0, g0, b0, a0)};
Graphics()->SetColorVertex(Array, 4);
float x0 = pEnvelope->m_vPoints[i].m_Time / 1000.0f / EndTime;
float x1 = pEnvelope->m_vPoints[i + 1].m_Time / 1000.0f / EndTime;
float x0 = EnvelopeToScreenX(View, fxt2f(pEnvelope->m_vPoints[i].m_Time));
float x1 = EnvelopeToScreenX(View, fxt2f(pEnvelope->m_vPoints[i + 1].m_Time));
IGraphics::CQuadItem QuadItem(ColorBar.x + x0 * ColorBar.w, ColorBar.y, (x1 - x0) * ColorBar.w, ColorBar.h);
IGraphics::CQuadItem QuadItem(x0, ColorBar.y, x1 - x0, ColorBar.h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
}
Graphics()->QuadsEnd();
UI()->ClipDisable();
}
// render handles
@ -6025,9 +6210,12 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
// update displayed text
s_CurValueInput.SetFloat(0.0f);
s_CurTimeInput.SetFloat(0.0f);
m_ResetZoomEnvelope = true;
}
{
UI()->ClipEnable(&View);
Graphics()->TextureClear();
Graphics()->QuadsBegin();
for(int c = 0; c < pEnvelope->GetChannels(); c++)
@ -6039,11 +6227,9 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
{
// point handle
{
const float PosX = pEnvelope->m_vPoints[i].m_Time / 1000.0f / EndTime;
const float PosY = (fx2f(pEnvelope->m_vPoints[i].m_aValues[c]) - Bottom) / (Top - Bottom);
CUIRect Final;
Final.x = View.x + PosX * View.w;
Final.y = View.y + View.h - PosY * View.h;
Final.x = EnvelopeToScreenX(View, fxt2f(pEnvelope->m_vPoints[i].m_Time));
Final.y = EnvelopeToScreenY(View, fx2f(pEnvelope->m_vPoints[i].m_aValues[c]));
Final.x -= 2.0f;
Final.y -= 2.0f;
Final.w = 4.0f;
@ -6051,7 +6237,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
const void *pID = &pEnvelope->m_vPoints[i].m_aValues[c];
if(UI()->MouseInside(&Final))
if(UI()->MouseInside(&Final) && UI()->MouseInside(&View))
UI()->SetHotItem(pID);
float ColorMod = 1.0f;
@ -6063,27 +6249,69 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
m_SelectedQuadEnvelope = -1;
m_SelectedEnvelopePoint = -1;
s_Operation = OP_NONE;
UI()->SetActiveItem(nullptr);
}
else
{
if(Input()->ShiftIsPressed())
{
if(i != 0)
if(s_Operation == OP_NONE || s_Operation == OP_DRAG_POINT_Y)
{
float DeltaX = m_MouseDeltaX * TimeScale * (Input()->ModifierIsPressed() ? 1.0f : 1000.0f);
DeltaX = DeltaX < 0 ? -std::ceil(-DeltaX) : std::ceil(DeltaX);
pEnvelope->m_vPoints[i].m_Time += (int)DeltaX;
s_Operation = OP_DRAG_POINT_X;
s_AccurateDragValueX = pEnvelope->m_vPoints[i].m_Time;
}
else
{
float DeltaX = ScreenToEnvelopeDX(View, UI()->MouseDeltaX()) * (Input()->ModifierIsPressed() ? 50.0f : 1000.0f);
s_AccurateDragValueX += DeltaX;
if(pEnvelope->m_vPoints[i].m_Time < pEnvelope->m_vPoints[i - 1].m_Time)
pEnvelope->m_vPoints[i].m_Time = std::round(s_AccurateDragValueX);
if(i == 0 && pEnvelope->m_vPoints[i].m_Time != 0)
{
RemoveTimeOffsetEnvelope(pEnvelope);
s_AccurateDragValueX = 0.0f;
}
if(i != 0 && pEnvelope->m_vPoints[i].m_Time < pEnvelope->m_vPoints[i - 1].m_Time)
{
pEnvelope->m_vPoints[i].m_Time = pEnvelope->m_vPoints[i - 1].m_Time + 1;
if(i + 1 != pEnvelope->m_vPoints.size() && pEnvelope->m_vPoints[i].m_Time > pEnvelope->m_vPoints[i + 1].m_Time)
s_AccurateDragValueX = pEnvelope->m_vPoints[i - 1].m_Time + 1;
}
if(i < pEnvelope->m_vPoints.size() - 1 && pEnvelope->m_vPoints[i].m_Time > pEnvelope->m_vPoints[i + 1].m_Time)
{
pEnvelope->m_vPoints[i].m_Time = pEnvelope->m_vPoints[i + 1].m_Time - 1;
s_AccurateDragValueX = pEnvelope->m_vPoints[i + 1].m_Time - 1;
}
pEnvelope->m_vPoints[i].m_Time = clamp(pEnvelope->m_vPoints[i].m_Time, f2fxt(ScreenToEnvelopeX(View, View.x)), f2fxt(ScreenToEnvelopeX(View, View.x + View.w)));
s_AccurateDragValueX = clamp<float>(s_AccurateDragValueX, f2fxt(ScreenToEnvelopeX(View, View.x)), f2fxt(ScreenToEnvelopeX(View, View.x + View.w)));
}
}
else
{
pEnvelope->m_vPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY * (Input()->ModifierIsPressed() ? 0.001f : ValueScale));
if(s_Operation == OP_NONE || s_Operation == OP_DRAG_POINT_X)
{
s_Operation = OP_DRAG_POINT_Y;
s_AccurateDragValueY = pEnvelope->m_vPoints[i].m_aValues[c];
}
else
{
float DeltaY = ScreenToEnvelopeDY(View, UI()->MouseDeltaY()) * (Input()->ModifierIsPressed() ? 51.2f : 1024.0f);
s_AccurateDragValueY -= DeltaY;
pEnvelope->m_vPoints[i].m_aValues[c] = std::round(s_AccurateDragValueY);
if(pEnvelope->GetChannels() == 4)
{
pEnvelope->m_vPoints[i].m_aValues[c] = clamp(pEnvelope->m_vPoints[i].m_aValues[c], 0, 1024);
s_AccurateDragValueY = clamp<float>(s_AccurateDragValueY, 0, 1024);
}
else
{
pEnvelope->m_vPoints[i].m_aValues[c] = clamp(pEnvelope->m_vPoints[i].m_aValues[c], f2fx(ScreenToEnvelopeY(View, View.y + View.h)), f2fx(ScreenToEnvelopeY(View, View.y)));
s_AccurateDragValueY = clamp<float>(s_AccurateDragValueY, f2fx(ScreenToEnvelopeY(View, View.y + View.h)), f2fx(ScreenToEnvelopeY(View, View.y)));
}
}
}
m_SelectedQuadEnvelope = m_SelectedEnvelope;
@ -6104,7 +6332,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
}
// remove point
if(UI()->MouseButtonClicked(1))
if(UI()->MouseButtonClicked(1) && pEnvelope->m_vPoints.size() > 2)
{
if(s_pSelectedPoint == pID)
{
@ -6165,18 +6393,12 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
// tangent handles for bezier curves
{
const float PosX = pEnvelope->m_vPoints[i].m_Time / 1000.0f / EndTime;
const float PosY = (fx2f(pEnvelope->m_vPoints[i].m_aValues[c]) - Bottom) / (Top - Bottom);
// Out-Tangent handle
if(pEnvelope->m_vPoints[i].m_Curvetype == CURVETYPE_BEZIER)
{
const float OutTangentX = PosX + (pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c] / 1000.0f / EndTime);
const float OutTangentY = PosY + fx2f(pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaY[c]) / (Top - Bottom);
CUIRect Final;
Final.x = View.x + OutTangentX * View.w;
Final.y = View.y + View.h - OutTangentY * View.h;
Final.x = EnvelopeToScreenX(View, fxt2f(pEnvelope->m_vPoints[i].m_Time + pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c]));
Final.y = EnvelopeToScreenY(View, fx2f(pEnvelope->m_vPoints[i].m_aValues[c] + pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaY[c]));
Final.x -= 2.0f;
Final.y -= 2.0f;
Final.w = 4.0f;
@ -6187,7 +6409,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
const void *pID = &pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c];
float ColorMod = 1.0f;
if(UI()->MouseInside(&Final))
if(UI()->MouseInside(&Final) && UI()->MouseInside(&View))
UI()->SetHotItem(pID);
if(UI()->CheckActiveItem(pID))
@ -6197,17 +6419,32 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
m_SelectedQuadEnvelope = -1;
m_SelectedEnvelopePoint = -1;
s_Operation = OP_NONE;
UI()->SetActiveItem(nullptr);
}
else
{
float DeltaX = m_MouseDeltaX * TimeScale * (Input()->ModifierIsPressed() ? 1.0f : 1000.0f);
DeltaX = DeltaX < 0 ? -std::ceil(-DeltaX) : std::ceil(DeltaX);
pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c] += (int)DeltaX;
pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaY[c] -= f2fx(m_MouseDeltaY * (Input()->ModifierIsPressed() ? 0.005f : ValueScale));
if(s_Operation == OP_NONE)
{
s_Operation = OP_DRAG_POINT;
s_AccurateDragValueX = pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c];
s_AccurateDragValueY = pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaY[c];
}
else
{
float DeltaX = ScreenToEnvelopeDX(View, UI()->MouseDeltaX()) * (Input()->ModifierIsPressed() ? 50.0f : 1000.0f);
float DeltaY = ScreenToEnvelopeDY(View, UI()->MouseDeltaY()) * (Input()->ModifierIsPressed() ? 51.2f : 1024.0f);
s_AccurateDragValueX += DeltaX;
s_AccurateDragValueY -= DeltaY;
pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c] = std::round(s_AccurateDragValueX);
pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaY[c] = std::round(s_AccurateDragValueY);
// clamp time value
pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c] = clamp<int>(pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c], 0, EndTime * 1000.0f - pEnvelope->m_vPoints[i].m_Time);
pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c] = clamp<int>(pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c], 0, f2fxt(ScreenToEnvelopeX(View, View.x + View.w)) - pEnvelope->m_vPoints[i].m_Time);
s_AccurateDragValueX = clamp<float>(s_AccurateDragValueX, 0, f2fxt(ScreenToEnvelopeX(View, View.x + View.w)) - pEnvelope->m_vPoints[i].m_Time);
}
m_SelectedQuadEnvelope = m_SelectedEnvelope;
m_ShowEnvelopePreview = SHOWENV_SELECTED;
@ -6242,7 +6479,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
if(pID == s_pSelectedPoint && UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))
{
pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c] = clamp<int>(s_CurTimeInput.GetFloat() * 1000.0f - pEnvelope->m_vPoints[i].m_Time, 0, EndTime * 1000.0f - pEnvelope->m_vPoints[i].m_Time);
pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaX[c] = clamp<int>(s_CurTimeInput.GetFloat() * 1000.0f - pEnvelope->m_vPoints[i].m_Time, 0, pEnvelope->m_vPoints[i + 1].m_Time - pEnvelope->m_vPoints[i].m_Time);
pEnvelope->m_vPoints[i].m_Bezier.m_aOutTangentDeltaY[c] = f2fx(s_CurValueInput.GetFloat()) - pEnvelope->m_vPoints[i].m_aValues[c];
Updated = true;
}
@ -6270,12 +6507,9 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
// In-Tangent handle
if(i > 0 && pEnvelope->m_vPoints[i - 1].m_Curvetype == CURVETYPE_BEZIER)
{
const float InTangentX = PosX + pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c] / 1000.0f / EndTime;
const float InTangentY = PosY + fx2f(pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaY[c]) / (Top - Bottom);
CUIRect Final;
Final.x = View.x + InTangentX * View.w;
Final.y = View.y + View.h - InTangentY * View.h;
Final.x = EnvelopeToScreenX(View, fxt2f(pEnvelope->m_vPoints[i].m_Time + pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c]));
Final.y = EnvelopeToScreenY(View, fx2f(pEnvelope->m_vPoints[i].m_aValues[c] + pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaY[c]));
Final.x -= 2.0f;
Final.y -= 2.0f;
Final.w = 4.0f;
@ -6286,7 +6520,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
const void *pID = &pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c];
float ColorMod = 1.0f;
if(UI()->MouseInside(&Final))
if(UI()->MouseInside(&Final) && UI()->MouseInside(&View))
UI()->SetHotItem(pID);
if(UI()->CheckActiveItem(pID))
@ -6296,17 +6530,32 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
m_SelectedQuadEnvelope = -1;
m_SelectedEnvelopePoint = -1;
s_Operation = OP_NONE;
UI()->SetActiveItem(nullptr);
}
else
{
float DeltaX = m_MouseDeltaX * TimeScale * (Input()->ModifierIsPressed() ? 1.0f : 1000.0f);
DeltaX = DeltaX < 0 ? -std::ceil(-DeltaX) : std::ceil(DeltaX);
pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c] += (int)DeltaX;
pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaY[c] -= f2fx(m_MouseDeltaY * (Input()->ModifierIsPressed() ? 0.005f : ValueScale));
if(s_Operation == OP_NONE)
{
s_Operation = OP_DRAG_POINT;
s_AccurateDragValueX = pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c];
s_AccurateDragValueY = pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaY[c];
}
else
{
float DeltaX = ScreenToEnvelopeDX(View, UI()->MouseDeltaX()) * (Input()->ModifierIsPressed() ? 50.0f : 1000.0f);
float DeltaY = ScreenToEnvelopeDY(View, UI()->MouseDeltaY()) * (Input()->ModifierIsPressed() ? 51.2f : 1024.0f);
s_AccurateDragValueX += DeltaX;
s_AccurateDragValueY -= DeltaY;
pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c] = std::round(s_AccurateDragValueX);
pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaY[c] = std::round(s_AccurateDragValueY);
// clamp time value
pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c] = clamp(pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c], -pEnvelope->m_vPoints[i].m_Time, 0);
pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c] = clamp(pEnvelope->m_vPoints[i].m_Bezier.m_aInTangentDeltaX[c], f2fxt(ScreenToEnvelopeX(View, View.x)) - pEnvelope->m_vPoints[i].m_Time, 0);
s_AccurateDragValueX = clamp<float>(s_AccurateDragValueX, f2fxt(ScreenToEnvelopeX(View, View.x)) - pEnvelope->m_vPoints[i].m_Time, 0);
}
m_SelectedQuadEnvelope = m_SelectedEnvelope;
m_ShowEnvelopePreview = SHOWENV_SELECTED;
@ -6369,6 +6618,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
}
}
Graphics()->QuadsEnd();
UI()->ClipDisable();
}
if(s_pSelectedPoint != nullptr)
@ -6658,7 +6908,7 @@ void CEditor::RenderMenubar(CUIRect MenuBar)
char aTimeStr[6];
str_timestamp_format(aTimeStr, sizeof(aTimeStr), "%H:%M");
str_format(aBuf, sizeof(aBuf), "X: %.1f, Y: %.1f, Z: %.1f, A: %.1f, G: %i %s", UI()->MouseWorldX() / 32.0f, UI()->MouseWorldY() / 32.0f, m_Zoom, m_AnimateSpeed, m_GridFactor, aTimeStr);
str_format(aBuf, sizeof(aBuf), "X: %.1f, Y: %.1f, Z: %.1f, A: %.1f, G: %i %s", UI()->MouseWorldX() / 32.0f, UI()->MouseWorldY() / 32.0f, m_ZoomMapView.GetZoom(), m_AnimateSpeed, m_GridFactor, aTimeStr);
UI()->DoLabel(&Info, aBuf, 10.0f, TEXTALIGN_MR);
static int s_CloseButton = 0;
@ -6715,14 +6965,14 @@ void CEditor::Render()
{
// handle zoom hotkeys
if(Input()->KeyPress(KEY_KP_MINUS))
ChangeZoom(50.0f);
m_ZoomMapView.ChangeZoom(50.0f);
if(Input()->KeyPress(KEY_KP_PLUS))
ChangeZoom(-50.0f);
m_ZoomMapView.ChangeZoom(-50.0f);
if(Input()->KeyPress(KEY_KP_MULTIPLY))
{
m_EditorOffsetX = 0;
m_EditorOffsetY = 0;
SetZoom(100.0f);
m_ZoomMapView.SetZoom(100.0f);
}
// handle brush save/load hotkeys
@ -6937,12 +7187,12 @@ void CEditor::Render()
if(m_Dialog == DIALOG_NONE && !UI()->IsPopupHovered() && (!m_GuiActive || UI()->MouseInside(&View)))
{
if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN))
ChangeZoom(20.0f);
m_ZoomMapView.ChangeZoom(20.0f);
if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP))
ChangeZoom(-20.0f);
m_ZoomMapView.ChangeZoom(-20.0f);
}
UpdateZoom();
UpdateZoomWorld();
// Popup menus must be rendered before the statusbar, because UI elements in
// popup menus can set tooltips, which are rendered in the status bar.
@ -7047,8 +7297,6 @@ void CEditor::Reset(bool CreateDefault)
m_EditorOffsetX = 0.0f;
m_EditorOffsetY = 0.0f;
m_Zoom = 200.0f;
m_Zooming = false;
m_WorldZoom = 1.0f;
m_MouseDeltaX = 0;
@ -7064,52 +7312,26 @@ void CEditor::Reset(bool CreateDefault)
m_ShowEnvelopePreview = SHOWENV_NONE;
m_ShiftBy = 1;
m_ResetZoomEnvelope = true;
}
int CEditor::GetLineDistance() const
{
if(m_Zoom <= 100.0f)
if(m_ZoomMapView.GetZoom() <= 100.0f)
return 16;
else if(m_Zoom <= 250.0f)
else if(m_ZoomMapView.GetZoom() <= 250.0f)
return 32;
else if(m_Zoom <= 450.0f)
else if(m_ZoomMapView.GetZoom() <= 450.0f)
return 64;
else if(m_Zoom <= 850.0f)
else if(m_ZoomMapView.GetZoom() <= 850.0f)
return 128;
else if(m_Zoom <= 1550.0f)
else if(m_ZoomMapView.GetZoom() <= 1550.0f)
return 256;
else
return 512;
}
void CEditor::SetZoom(float Target)
{
Target = clamp(Target, MinZoomLevel(), MaxZoomLevel());
const float Now = Client()->GlobalTime();
float Current = m_Zoom;
float Derivative = 0.0f;
if(m_Zooming)
{
const float Progress = ZoomProgress(Now);
Current = m_ZoomSmoothing.Evaluate(Progress);
Derivative = m_ZoomSmoothing.Derivative(Progress);
}
m_ZoomSmoothingTarget = Target;
m_ZoomSmoothing = CCubicBezier::With(Current, Derivative, 0.0f, m_ZoomSmoothingTarget);
m_ZoomSmoothingStart = Now;
m_ZoomSmoothingEnd = Now + g_Config.m_EdSmoothZoomTime / 1000.0f;
m_Zooming = true;
}
void CEditor::ChangeZoom(float Amount)
{
const float CurrentTarget = m_Zooming ? m_ZoomSmoothingTarget : m_Zoom;
SetZoom(CurrentTarget + Amount);
}
void CEditor::ZoomMouseTarget(float ZoomFactor)
{
// zoom to the current mouse position
@ -7130,47 +7352,14 @@ void CEditor::ZoomMouseTarget(float ZoomFactor)
m_WorldOffsetY += (Mwy - m_WorldOffsetY) * (1.0f - ZoomFactor);
}
void CEditor::UpdateZoom()
void CEditor::UpdateZoomWorld()
{
if(m_Zooming)
{
const float Time = Client()->GlobalTime();
const float OldLevel = m_Zoom;
if(Time >= m_ZoomSmoothingEnd)
{
m_Zoom = m_ZoomSmoothingTarget;
m_Zooming = false;
}
else
{
m_Zoom = m_ZoomSmoothing.Evaluate(ZoomProgress(Time));
if((OldLevel < m_ZoomSmoothingTarget && m_Zoom > m_ZoomSmoothingTarget) || (OldLevel > m_ZoomSmoothingTarget && m_Zoom < m_ZoomSmoothingTarget))
{
m_Zoom = m_ZoomSmoothingTarget;
m_Zooming = false;
}
}
m_Zoom = clamp(m_Zoom, MinZoomLevel(), MaxZoomLevel());
if(g_Config.m_EdZoomTarget)
ZoomMouseTarget(m_Zoom / OldLevel);
}
m_WorldZoom = m_Zoom / 100.0f;
}
float CEditor::MinZoomLevel() const
{
return 10.0f;
}
float CEditor::MaxZoomLevel() const
{
return g_Config.m_EdLimitMaxZoomLevel ? 2000.0f : std::numeric_limits<float>::max();
}
float CEditor::ZoomProgress(float CurrentTime) const
{
return (CurrentTime - m_ZoomSmoothingStart) / (m_ZoomSmoothingEnd - m_ZoomSmoothingStart);
float OldLevel = m_ZoomMapView.GetZoom();
m_ZoomMapView.SetZoomRange(10.0f, g_Config.m_EdLimitMaxZoomLevel ? 2000.0f : std::numeric_limits<float>::max());
float NewLevel = m_ZoomMapView.GetZoom();
if(m_ZoomMapView.UpdateZoom() && g_Config.m_EdZoomTarget)
ZoomMouseTarget(NewLevel / OldLevel);
m_WorldZoom = NewLevel / 100.0f;
}
void CEditor::Goto(float X, float Y)
@ -7813,3 +8002,90 @@ void CEditorMap::MakeTuneLayer(CLayer *pLayer)
m_pTuneLayer->m_pEditor = m_pEditor;
m_pTuneLayer->m_Texture = m_pEditor->GetTuneTexture();
}
CSmoothZoom::CSmoothZoom(float InitialZoom, float Min, float Max, CEditor *pEditor)
{
m_Zoom = InitialZoom;
m_MinZoomLevel = Min;
m_MaxZoomLevel = Max;
m_pEditor = pEditor;
m_Zooming = false;
}
void CSmoothZoom::SetZoom(float Target)
{
Target = clamp(Target, m_MinZoomLevel, m_MaxZoomLevel);
const float Now = m_pEditor->Client()->GlobalTime();
float Current = m_Zoom;
float Derivative = 0.0f;
if(m_Zooming)
{
const float Progress = ZoomProgress(Now);
Current = m_ZoomSmoothing.Evaluate(Progress);
Derivative = m_ZoomSmoothing.Derivative(Progress);
}
m_ZoomSmoothingTarget = Target;
m_ZoomSmoothing = CCubicBezier::With(Current, Derivative, 0.0f, m_ZoomSmoothingTarget);
m_ZoomSmoothingStart = Now;
m_ZoomSmoothingEnd = Now + g_Config.m_EdSmoothZoomTime / 1000.0f;
m_Zooming = true;
}
void CSmoothZoom::ChangeZoom(float Amount)
{
const float CurrentTarget = m_Zooming ? m_ZoomSmoothingTarget : m_Zoom;
SetZoom(CurrentTarget + Amount);
}
bool CSmoothZoom::UpdateZoom()
{
if(m_Zooming)
{
const float Time = m_pEditor->Client()->GlobalTime();
const float OldLevel = m_Zoom;
if(Time >= m_ZoomSmoothingEnd)
{
m_Zoom = m_ZoomSmoothingTarget;
m_Zooming = false;
}
else
{
m_Zoom = m_ZoomSmoothing.Evaluate(ZoomProgress(Time));
if((OldLevel < m_ZoomSmoothingTarget && m_Zoom > m_ZoomSmoothingTarget) || (OldLevel > m_ZoomSmoothingTarget && m_Zoom < m_ZoomSmoothingTarget))
{
m_Zoom = m_ZoomSmoothingTarget;
m_Zooming = false;
}
}
m_Zoom = clamp(m_Zoom, m_MinZoomLevel, m_MaxZoomLevel);
return true;
}
return false;
}
float CSmoothZoom::ZoomProgress(float CurrentTime) const
{
return (CurrentTime - m_ZoomSmoothingStart) / (m_ZoomSmoothingEnd - m_ZoomSmoothingStart);
}
float CSmoothZoom::GetZoom() const
{
return m_Zoom;
}
void CSmoothZoom::SetZoomRange(float Min, float Max)
{
m_MinZoomLevel = Min;
m_MaxZoomLevel = Max;
}
void CSmoothZoom::SetZoomInstant(float Target)
{
m_Zooming = false;
m_Zoom = Target;
}

View file

@ -790,6 +790,34 @@ public:
const char *GetTempFileName() const { return m_aTempFileName; }
};
class CSmoothZoom
{
public:
CSmoothZoom(float InitialZoom, float Min, float Max, CEditor *pEditor);
void SetZoomRange(float Min, float Max);
void SetZoom(float Target);
void SetZoomInstant(float Target);
void ChangeZoom(float Amount);
bool UpdateZoom();
float GetZoom() const;
private:
float ZoomProgress(float CurrentTime) const;
bool m_Zooming;
float m_Zoom;
CCubicBezier m_ZoomSmoothing;
float m_ZoomSmoothingTarget;
float m_ZoomSmoothingStart;
float m_ZoomSmoothingEnd;
float m_MinZoomLevel;
float m_MaxZoomLevel;
CEditor *m_pEditor;
};
class CEditor : public IEditor
{
class IInput *m_pInput = nullptr;
@ -837,6 +865,9 @@ public:
CRenderTools *RenderTools() { return &m_RenderTools; }
CEditor() :
m_ZoomMapView(200.0f, 10.0f, 2000.0f, this),
m_ZoomEnvelopeX(1.0f, 0.1f, 600.0f, this),
m_ZoomEnvelopeY(640.0f, 0.1f, 32000.0f, this),
m_TilesetPicker(16, 16)
{
m_EntitiesTexture.Invalidate();
@ -885,10 +916,12 @@ public:
m_EditorOffsetX = 0.0f;
m_EditorOffsetY = 0.0f;
m_Zoom = 200.0f;
m_Zooming = false;
m_WorldZoom = 1.0f;
m_ResetZoomEnvelope = true;
m_OffsetEnvelopeX = 0.1f;
m_OffsetEnvelopeY = 0.5f;
m_ShowMousePointer = true;
m_MouseDeltaX = 0;
m_MouseDeltaY = 0;
@ -1158,14 +1191,17 @@ public:
float m_EditorOffsetY;
// Zooming
CCubicBezier m_ZoomSmoothing;
float m_ZoomSmoothingStart;
float m_ZoomSmoothingEnd;
bool m_Zooming;
float m_Zoom;
float m_ZoomSmoothingTarget;
CSmoothZoom m_ZoomMapView;
float m_WorldZoom;
CSmoothZoom m_ZoomEnvelopeX;
CSmoothZoom m_ZoomEnvelopeY;
bool m_ResetZoomEnvelope;
float m_OffsetEnvelopeX;
float m_OffsetEnvelopeY;
bool m_ShowMousePointer;
bool m_GuiActive;
@ -1464,13 +1500,23 @@ public:
int GetLineDistance() const;
// Zooming
void SetZoom(float Target);
void ChangeZoom(float Amount);
void ZoomMouseTarget(float ZoomFactor);
void UpdateZoom();
float ZoomProgress(float CurrentTime) const;
float MinZoomLevel() const;
float MaxZoomLevel() const;
void UpdateZoomWorld();
void ZoomAdaptOffsetX(float ZoomFactor, const CUIRect &View);
void UpdateZoomEnvelopeX(const CUIRect &View);
void ZoomAdaptOffsetY(float ZoomFactor, const CUIRect &View);
void UpdateZoomEnvelopeY(const CUIRect &View);
void ResetZoomEnvelope(CEnvelope *pEnvelope, int ActiveChannels);
void RemoveTimeOffsetEnvelope(CEnvelope *pEnvelope);
float ScreenToEnvelopeX(const CUIRect &View, float x) const;
float EnvelopeToScreenX(const CUIRect &View, float x) const;
float ScreenToEnvelopeY(const CUIRect &View, float y) const;
float EnvelopeToScreenY(const CUIRect &View, float y) const;
float ScreenToEnvelopeDX(const CUIRect &View, float dx);
float ScreenToEnvelopeDY(const CUIRect &View, float dy);
// DDRace