Merge remote-tracking branch 'origin/master' into 1.10

This commit is contained in:
Wojtek Figat
2024-12-10 11:07:31 +01:00
105 changed files with 2570 additions and 653 deletions

View File

@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Json;
@@ -29,6 +28,7 @@ namespace FlaxEditor.GUI
internal Float2 _mousePos = Float2.Minimum;
internal bool _isMovingSelection;
internal bool _isMovingTangent;
internal bool _movedView;
internal bool _movedKeyframes;
private TangentPoint _movingTangent;
private Float2 _movingSelectionStart;
@@ -190,31 +190,28 @@ namespace FlaxEditor.GUI
// Moving view
if (_rightMouseDown)
{
var delta = location - _movingViewLastPos;
var movingViewPos = Parent.PointToParent(PointToParent(location));
var delta = movingViewPos - _movingViewLastPos;
if (_editor.CustomViewPanning != null)
delta = _editor.CustomViewPanning(delta);
delta *= GetUseModeMask(_editor.EnablePanning) * _editor.ViewScale;
delta *= GetUseModeMask(_editor.EnablePanning);
if (delta.LengthSquared > 0.01f)
{
_editor._mainPanel.ViewOffset += delta;
_movingViewLastPos = location;
_movingViewLastPos = movingViewPos;
_movedView = true;
if (_editor.CustomViewPanning != null)
{
Cursor = CursorType.SizeAll;
if (Cursor == CursorType.Default)
Cursor = CursorType.SizeAll;
}
else
{
switch (_editor.EnablePanning)
{
case UseMode.Vertical:
Cursor = CursorType.SizeNS;
break;
case UseMode.Horizontal:
Cursor = CursorType.SizeWE;
break;
case UseMode.On:
Cursor = CursorType.SizeAll;
break;
case UseMode.Vertical: Cursor = CursorType.SizeNS; break;
case UseMode.Horizontal: Cursor = CursorType.SizeWE; break;
case UseMode.On: Cursor = CursorType.SizeAll; break;
}
}
}
@@ -234,11 +231,10 @@ namespace FlaxEditor.GUI
else if (_isMovingTangent)
{
var viewRect = _editor._mainPanel.GetClientArea();
var direction = _movingTangent.IsIn ? -1.0f : 1.0f;
var k = _editor.GetKeyframe(_movingTangent.Index);
var kv = _editor.GetKeyframeValue(k);
var value = _editor.Accessor.GetCurveValue(ref kv, _movingTangent.Component);
_movingTangent.TangentValue = direction * (PointToKeyframes(location, ref viewRect).Y - value);
_movingTangent.TangentValue = PointToKeyframes(location, ref viewRect).Y - value;
_editor.UpdateTangents();
Cursor = CursorType.SizeNS;
_movedKeyframes = true;
@@ -299,7 +295,8 @@ namespace FlaxEditor.GUI
{
_rightMouseDown = true;
_rightMouseDownPos = location;
_movingViewLastPos = location;
_movedView = false;
_movingViewLastPos = Parent.PointToParent(PointToParent(location));
}
// Check if any node is under the mouse
@@ -444,7 +441,7 @@ namespace FlaxEditor.GUI
Cursor = CursorType.Default;
// Check if no move has been made at all
if (Float2.Distance(ref location, ref _rightMouseDownPos) < 2.0f)
if (!_movedView)
{
var selectionCount = _editor.SelectionCount;
var point = GetChildAt(location) as KeyframePoint;
@@ -512,6 +509,27 @@ namespace FlaxEditor.GUI
return true;
}
/// <inheritdoc />
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
if (base.OnMouseDoubleClick(location, button))
return true;
// Add keyframe on double click
var child = GetChildAt(location);
if (child is not KeyframePoint &&
child is not TangentPoint &&
_editor.KeyframesCount < _editor.MaxKeyframes)
{
var viewRect = _editor._mainPanel.GetClientArea();
var pos = PointToKeyframes(location, ref viewRect);
_editor.AddKeyframe(pos);
return true;
}
return false;
}
/// <inheritdoc />
public override bool OnMouseWheel(Float2 location, float delta)
{
@@ -519,10 +537,29 @@ namespace FlaxEditor.GUI
return true;
// Zoom in/out
if (_editor.EnableZoom != UseMode.Off && IsMouseOver && !_leftMouseDown && RootWindow.GetKey(KeyboardKeys.Control))
var zoom = RootWindow.GetKey(KeyboardKeys.Control);
var zoomAlt = RootWindow.GetKey(KeyboardKeys.Shift);
if (_editor.EnableZoom != UseMode.Off && IsMouseOver && !_leftMouseDown && (zoom || zoomAlt))
{
// TODO: preserve the view center point for easier zooming
_editor.ViewScale += GetUseModeMask(_editor.EnableZoom) * (delta * 0.1f);
// Cache mouse location in curve-space
var viewRect = _editor._mainPanel.GetClientArea();
var locationInKeyframes = PointToKeyframes(location, ref viewRect);
var locationInEditorBefore = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect);
// Scale relative to the curve size
var scale = new Float2(delta * 0.1f);
_editor._mainPanel.GetDesireClientArea(out var mainPanelArea);
var curveScale = mainPanelArea.Size / _editor._contents.Size;
scale *= curveScale;
if (zoomAlt)
scale.X = 0; // Scale Y axis only
scale *= GetUseModeMask(_editor.EnableZoom); // Mask scale depending on allowed usage
_editor.ViewScale += scale;
// Zoom towards the mouse position
var locationInEditorAfter = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect);
var locationInEditorDelta = locationInEditorAfter - locationInEditorBefore;
_editor.ViewOffset -= locationInEditorDelta;
return true;
}

View File

@@ -671,8 +671,22 @@ namespace FlaxEditor.GUI
/// <inheritdoc />
public override void ShowWholeCurve()
{
ViewScale = ApplyUseModeMask(EnableZoom, _mainPanel.Size / _contents.Size, ViewScale);
ViewOffset = ApplyUseModeMask(EnablePanning, -_mainPanel.ControlsBounds.Location, ViewOffset);
_mainPanel.GetDesireClientArea(out var mainPanelArea);
ViewScale = ApplyUseModeMask(EnableZoom, mainPanelArea.Size / _contents.Size, ViewScale);
Float2 minPos = Float2.Maximum;
foreach (var point in _points)
{
var pos = point.PointToParent(point.Location);
Float2.Min(ref minPos, ref pos, out minPos);
}
var minPosPoint = _contents.PointToParent(ref minPos);
var scroll = new Float2(_mainPanel.HScrollBar?.TargetValue ?? 0, _mainPanel.VScrollBar?.TargetValue ?? 0);
scroll = ApplyUseModeMask(EnablePanning, minPosPoint, scroll);
if (_mainPanel.HScrollBar != null)
_mainPanel.HScrollBar.TargetValue = scroll.X;
if (_mainPanel.VScrollBar != null)
_mainPanel.VScrollBar.TargetValue = scroll.Y;
UpdateKeyframes();
}
@@ -923,6 +937,11 @@ namespace FlaxEditor.GUI
KeyframesEditorUtils.Paste(this);
return true;
}
else if (options.FocusSelection.Process(this))
{
ShowWholeCurve();
return true;
}
return false;
}
@@ -1384,9 +1403,7 @@ namespace FlaxEditor.GUI
// Calculate bounds
var bounds = _points[0].Bounds;
for (var i = 1; i < _points.Count; i++)
{
bounds = Rectangle.Union(bounds, _points[i].Bounds);
}
// Adjust contents bounds to fill the curve area
if (EnablePanning != UseMode.Off || !ShowCollapsed)
@@ -1632,6 +1649,7 @@ namespace FlaxEditor.GUI
var o = _keyframes[p.Index - 1];
var oValue = Accessor.GetCurveValue(ref o.Value, p.Component);
var slope = (value - oValue) / (k.Time - o.Time);
slope = -slope;
Accessor.SetCurveValue(slope, ref k.TangentIn, p.Component);
}
@@ -2116,9 +2134,7 @@ namespace FlaxEditor.GUI
// Calculate bounds
var bounds = _points[0].Bounds;
for (int i = 1; i < _points.Count; i++)
{
bounds = Rectangle.Union(bounds, _points[i].Bounds);
}
// Adjust contents bounds to fill the curve area
if (EnablePanning != UseMode.Off || !ShowCollapsed)
@@ -2184,12 +2200,12 @@ namespace FlaxEditor.GUI
var tangent = t.TangentValue;
var direction = t.IsIn ? -1.0f : 1.0f;
var offset = 30.0f * direction;
var offset = 30.0f;
var location = GetKeyframePoint(ref k, selectedComponent);
t.Size = KeyframesSize / ViewScale;
t.Location = new Float2
(
location.X * UnitsPerSecond - t.Width * 0.5f + offset,
location.X * UnitsPerSecond - t.Width * 0.5f + offset * direction,
location.Y * -UnitsPerSecond - t.Height * 0.5f + curveContentAreaBounds.Height - offset * tangent
);
@@ -2265,14 +2281,13 @@ namespace FlaxEditor.GUI
var startTangent = Accessor.GetCurveValue(ref startK.TangentOut, component);
var endTangent = Accessor.GetCurveValue(ref endK.TangentIn, component);
var offset = (end.X - start.X) * 0.5f;
var tangentScale = (endK.Time - startK.Time) / 3.0f;
var p1 = PointFromKeyframes(start, ref viewRect);
var p2 = PointFromKeyframes(start + new Float2(offset, startTangent * offset), ref viewRect);
var p3 = PointFromKeyframes(end - new Float2(offset, endTangent * offset), ref viewRect);
var p2 = PointFromKeyframes(start + new Float2(0, startTangent * tangentScale), ref viewRect);
var p3 = PointFromKeyframes(end + new Float2(0, endTangent * tangentScale), ref viewRect);
var p4 = PointFromKeyframes(end, ref viewRect);
Render2D.DrawBezier(p1, p2, p3, p4, color);
Render2D.DrawSpline(p1, p2, p3, p4, color);
}
}
}

View File

@@ -0,0 +1,86 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI
{
sealed class Splitter : Control
{
private bool _clicked;
public Action<Float2> Moved;
public const float DefaultHeight = 5.0f;
public override void Draw()
{
var style = Style.Current;
if (IsMouseOver || _clicked)
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), _clicked ? style.BackgroundSelected : style.BackgroundHighlighted);
}
public override void OnEndMouseCapture()
{
base.OnEndMouseCapture();
_clicked = false;
}
public override void Defocus()
{
base.Defocus();
_clicked = false;
}
public override void OnMouseEnter(Float2 location)
{
base.OnMouseEnter(location);
Cursor = CursorType.SizeNS;
}
public override void OnMouseLeave()
{
Cursor = CursorType.Default;
base.OnMouseLeave();
}
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (button == MouseButton.Left)
{
_clicked = true;
Focus();
StartMouseCapture();
return true;
}
return base.OnMouseDown(location, button);
}
public override void OnMouseMove(Float2 location)
{
base.OnMouseMove(location);
if (_clicked)
{
Moved(location);
}
}
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (button == MouseButton.Left && _clicked)
{
_clicked = false;
EndMouseCapture();
return true;
}
return base.OnMouseUp(location, button);
}
}
}

View File

@@ -230,7 +230,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
continue;
// Draw all ticks
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1);
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 2);
var lStep = _tickSteps[l];
var lNextStep = _tickSteps[l + 1];
int startTick = Mathf.FloorToInt(min / lStep);

View File

@@ -1446,6 +1446,17 @@ namespace FlaxEditor.GUI.Timeline
{
GetTracks(SelectedTracks[i], tracks);
}
// Find the lowest track position for selection
int lowestTrackLocation = Tracks.Count - 1;
for (int i = 0; i < tracks.Count; i++)
{
var trackToDelete = tracks[i];
if (trackToDelete.TrackIndex < lowestTrackLocation)
{
lowestTrackLocation = trackToDelete.TrackIndex;
}
}
SelectedTracks.Clear();
if (withUndo && Undo != null && Undo.Enabled)
{
@@ -1468,6 +1479,18 @@ namespace FlaxEditor.GUI.Timeline
}
OnTracksChanged();
MarkAsEdited();
// Select track above deleted tracks unless track is first track
if (Tracks.Count > 0)
{
if (lowestTrackLocation - 1 >= 0)
Select(Tracks[lowestTrackLocation - 1]);
else
Select(Tracks[0]);
SelectedTracks[0].Focus();
}
}
/// <summary>
@@ -1655,6 +1678,14 @@ namespace FlaxEditor.GUI.Timeline
}
OnTracksChanged();
MarkAsEdited();
// Deselect and select new clones.
Deselect();
foreach (var clone in clones)
{
Select(clone, true);
}
SelectedTracks[0].Focus();
}

View File

@@ -19,87 +19,6 @@ namespace FlaxEditor.GUI.Timeline.Tracks
/// <seealso cref="MemberTrack" />
public abstract class CurvePropertyTrackBase : MemberTrack, IKeyframesEditorContext
{
private sealed class Splitter : Control
{
private bool _clicked;
internal CurvePropertyTrackBase _track;
public override void Draw()
{
var style = Style.Current;
if (IsMouseOver || _clicked)
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), _clicked ? style.BackgroundSelected : style.BackgroundHighlighted);
}
public override void OnEndMouseCapture()
{
base.OnEndMouseCapture();
_clicked = false;
}
public override void Defocus()
{
base.Defocus();
_clicked = false;
}
public override void OnMouseEnter(Float2 location)
{
base.OnMouseEnter(location);
Cursor = CursorType.SizeNS;
}
public override void OnMouseLeave()
{
Cursor = CursorType.Default;
base.OnMouseLeave();
}
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (button == MouseButton.Left)
{
_clicked = true;
Focus();
StartMouseCapture();
return true;
}
return base.OnMouseDown(location, button);
}
public override void OnMouseMove(Float2 location)
{
base.OnMouseMove(location);
if (_clicked)
{
var height = Mathf.Clamp(PointToParent(location).Y, 40.0f, 1000.0f);
if (!Mathf.NearEqual(height, _track._expandedHeight))
{
_track.Height = _track._expandedHeight = height;
_track.Timeline.ArrangeTracks();
}
}
}
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (button == MouseButton.Left && _clicked)
{
_clicked = false;
EndMouseCapture();
return true;
}
return base.OnMouseUp(location, button);
}
}
private byte[] _curveEditingStartData;
private float _expandedHeight = 120.0f;
private Splitter _splitter;
@@ -251,12 +170,21 @@ namespace FlaxEditor.GUI.Timeline.Tracks
{
_splitter = new Splitter
{
_track = this,
Moved = OnSplitterMoved,
Parent = Curve,
};
}
var splitterHeight = 5.0f;
_splitter.Bounds = new Rectangle(0, Curve.Height - splitterHeight, Curve.Width, splitterHeight);
_splitter.Bounds = new Rectangle(0, Curve.Height - Splitter.DefaultHeight, Curve.Width, Splitter.DefaultHeight);
}
}
private void OnSplitterMoved(Float2 location)
{
var height = Mathf.Clamp(PointToParent(location).Y, 40.0f, 1000.0f);
if (!Mathf.NearEqual(height, _expandedHeight))
{
Height = _expandedHeight = height;
Timeline.ArrangeTracks();
}
}

View File

@@ -109,6 +109,19 @@ namespace FlaxEditor.GUI.Timeline.Tracks
MaxMediaCount = 1;
}
/// <inheritdoc />
public override void OnDuplicated(Track clone)
{
base.OnDuplicated(clone);
// Clone overriden parameters
if (clone is ParticleEmitterTrack cloneTrack)
{
foreach (var e in ParametersOverrides)
cloneTrack.ParametersOverrides.Add(e.Key, e.Value);
}
}
/// <inheritdoc />
public override void OnDestroy()
{

View File

@@ -61,6 +61,9 @@ namespace FlaxEditor.GUI.Timeline.Undo
_timeline.AddTrack(track, false);
track.TrackIndex = _order;
_timeline.OnTracksOrderChanged();
_timeline.Focus();
_timeline.Select(track);
track.Focus();
}
private void Remove()
@@ -68,10 +71,11 @@ namespace FlaxEditor.GUI.Timeline.Undo
var track = _timeline.FindTrack(_name);
if (track == null)
{
Editor.LogWarning($"Cannot remove track {_name}. It doesn't already exists.");
Editor.LogWarning($"Cannot remove track {_name}. It doesn't exist.");
return;
}
_timeline.Delete(track, false);
_timeline.Focus();
}
public string ActionString => _isAdd ? "Add track" : "Remove track";