diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index c33690e25..4e0234974 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.Collections.Generic; using FlaxEngine; using FlaxEngine.GUI; using FlaxEditor.Actions; @@ -16,6 +17,15 @@ namespace FlaxEditor.CustomEditors.Dedicated [CustomEditor(typeof(Spline)), DefaultEditor] public class SplineEditor : ActorEditor { + /// + /// Storage undo spline data + /// + private struct UndoData + { + public Spline spline; + public BezierCurve.Keyframe[] beforeKeyframes; + } + /// /// Basis for creating tangent manipulation types for bezier curves. /// @@ -151,17 +161,17 @@ namespace FlaxEditor.CustomEditors.Dedicated } } - private void SetPointAligned(Spline spline, int index, bool isIn) + private void SetPointAligned(Spline spline, int index, bool alignWithIn) { var keyframe = spline.GetSplineKeyframe(index); - var referenceTangent = isIn ? keyframe.TangentIn : keyframe.TangentOut; - var otherTangent = !isIn ? keyframe.TangentIn : keyframe.TangentOut; + var referenceTangent = alignWithIn ? keyframe.TangentIn : keyframe.TangentOut; + var otherTangent = !alignWithIn ? keyframe.TangentIn : keyframe.TangentOut; // inverse of reference tangent otherTangent.Translation = -referenceTangent.Translation.Normalized * otherTangent.Translation.Length; - if (isIn) keyframe.TangentOut = otherTangent; - if (!isIn) keyframe.TangentIn = otherTangent; + if (alignWithIn) keyframe.TangentOut = otherTangent; + if (!alignWithIn) keyframe.TangentIn = otherTangent; spline.SetSplineKeyframe(index, keyframe); } @@ -219,6 +229,10 @@ namespace FlaxEditor.CustomEditors.Dedicated public override void OnSelectTangent(Spline spline, int index) { } } + private readonly Color HighlightedColor = FlaxEngine.GUI.Style.Current.BackgroundSelected; + private readonly Color SelectedButtonColor = FlaxEngine.GUI.Style.Current.BackgroundSelected; + private readonly Color NormalButtonColor = FlaxEngine.GUI.Style.Current.BackgroundNormal; + private EditTangentOptionBase _currentTangentMode; private ButtonElement _freeTangentButton; @@ -226,43 +240,35 @@ namespace FlaxEditor.CustomEditors.Dedicated private ButtonElement _alignedTangentButton; private ButtonElement _smoothInTangentButton; private ButtonElement _smoothOutTangentButton; + private ButtonElement _setLinearAllTangentsButton; + private ButtonElement _setSmoothAllTangentsButton; private bool _tanInChanged; private bool _tanOutChanged; private Vector3 _lastTanInPos; private Vector3 _lastTanOutPos; + private Spline _selectedSpline; private SplineNode.SplinePointNode _selectedPoint; private SplineNode.SplinePointNode _lastPointSelected; private SplineNode.SplinePointTangentNode _selectedTangentIn; private SplineNode.SplinePointTangentNode _selectedTangentOut; - /// - /// Current selected spline on editor, if has - /// - public Spline SelectedSpline => !Values.HasDifferentValues && Values[0] is Spline ? (Spline)Values[0] : null; + private UndoData[] selectedSplinesUndoData; private bool HasPointSelected => _selectedPoint != null; private bool HasTangentsSelected => _selectedTangentIn != null || _selectedTangentOut != null; - private Color SelectedButtonColor => FlaxEngine.GUI.Style.Current.BackgroundSelected; - - private Color NormalButtonColor => FlaxEngine.GUI.Style.Current.BackgroundNormal; - - /// - /// Create a Spline editor - /// - public SplineEditor() - { - _currentTangentMode = new FreeTangentMode(); - } - /// public override void Initialize(LayoutElementsContainer layout) { base.Initialize(layout); + _currentTangentMode = new FreeTangentMode(); + if (Values.HasDifferentTypes == false) { + _selectedSpline = !Values.HasDifferentValues && Values[0] is Spline ? (Spline)Values[0] : null; + layout.Space(10); layout.Header("Selected spline point"); @@ -278,27 +284,65 @@ namespace FlaxEditor.CustomEditors.Dedicated _smoothInTangentButton = selectedPointsGrid.Button("Smooth In"); _smoothOutTangentButton = selectedPointsGrid.Button("Smooth Out"); + _linearTangentButton.Button.BackgroundColorHighlighted = HighlightedColor; + _freeTangentButton.Button.BackgroundColorHighlighted = HighlightedColor; + _alignedTangentButton.Button.BackgroundColorHighlighted = HighlightedColor; + _smoothInTangentButton.Button.BackgroundColorHighlighted = HighlightedColor; + _smoothOutTangentButton.Button.BackgroundColorHighlighted = HighlightedColor; + + _linearTangentButton.Button.Clicked += StartEditSpline; + _freeTangentButton.Button.Clicked += StartEditSpline; + _alignedTangentButton.Button.Clicked += StartEditSpline; + _smoothInTangentButton.Button.Clicked += StartEditSpline; + _smoothOutTangentButton.Button.Clicked += StartEditSpline; + _linearTangentButton.Button.Clicked += SetModeLinear; _freeTangentButton.Button.Clicked += SetModeFree; _alignedTangentButton.Button.Clicked += SetModeAligned; _smoothInTangentButton.Button.Clicked += SetModeSmoothIn; _smoothOutTangentButton.Button.Clicked += SetModeSmoothOut; - _linearTangentButton.Button.Clicked += UpdateButtonsColors; - _freeTangentButton.Button.Clicked += UpdateButtonsColors; - _alignedTangentButton.Button.Clicked += UpdateButtonsColors; - _smoothInTangentButton.Button.Clicked += UpdateButtonsColors; - _smoothOutTangentButton.Button.Clicked += UpdateButtonsColors; + _linearTangentButton.Button.Clicked += EndEditSpline; + _freeTangentButton.Button.Clicked += EndEditSpline; + _alignedTangentButton.Button.Clicked += EndEditSpline; + _smoothInTangentButton.Button.Clicked += EndEditSpline; + _smoothOutTangentButton.Button.Clicked += EndEditSpline; layout.Header("All spline points"); var grid = layout.CustomContainer(); grid.CustomControl.SlotsHorizontally = 2; grid.CustomControl.SlotsVertically = 1; - grid.Button("Set Linear Tangents").Button.Clicked += OnSetTangentsLinear; - grid.Button("Set Smooth Tangents").Button.Clicked += OnSetTangentsSmooth; + + _setLinearAllTangentsButton = grid.Button("Set Linear Tangents"); + _setSmoothAllTangentsButton = grid.Button("Set Smooth Tangents"); + _setLinearAllTangentsButton.Button.BackgroundColorHighlighted = HighlightedColor; + _setSmoothAllTangentsButton.Button.BackgroundColorHighlighted = HighlightedColor; + + _setLinearAllTangentsButton.Button.Clicked += StartEditSpline; + _setSmoothAllTangentsButton.Button.Clicked += StartEditSpline; + + _setLinearAllTangentsButton.Button.Clicked += OnSetTangentsLinear; + _setSmoothAllTangentsButton.Button.Clicked += OnSetTangentsSmooth; + + _setLinearAllTangentsButton.Button.Clicked += EndEditSpline; + _setSmoothAllTangentsButton.Button.Clicked += EndEditSpline; + + if (_selectedSpline) _selectedSpline.SplineUpdated += OnSplineEdited; } } + /// + protected override void Deinitialize() + { + if (_selectedSpline) _selectedSpline.SplineUpdated -= OnSplineEdited; + } + + private void OnSplineEdited() + { + SetSelectedTangentTypeAsCurrent(); + UpdateButtonsColors(); + } + /// public override void Refresh() { @@ -307,20 +351,22 @@ namespace FlaxEditor.CustomEditors.Dedicated UpdateSelectedPoint(); UpdateSelectedTangent(); + _linearTangentButton.Button.Enabled = CanEditTangent(); _freeTangentButton.Button.Enabled = CanSetTangentFree(); - _linearTangentButton.Button.Enabled = CanSetTangentMode(); _alignedTangentButton.Button.Enabled = CanSetTangentAligned(); _smoothInTangentButton.Button.Enabled = CanSetTangentSmoothIn(); _smoothOutTangentButton.Button.Enabled = CanSetTangentSmoothOut(); + _setLinearAllTangentsButton.Button.Enabled = CanEditTangent(); + _setSmoothAllTangentsButton.Button.Enabled = CanEditTangent(); - if (!CanSetTangentMode()) + if (!CanEditTangent()) { return; } var index = _lastPointSelected.Index; - var currentTangentInPosition = SelectedSpline.GetSplineLocalTangent(index, true).Translation; - var currentTangentOutPosition = SelectedSpline.GetSplineLocalTangent(index, false).Translation; + var currentTangentInPosition = _selectedSpline.GetSplineLocalTangent(index, true).Translation; + var currentTangentOutPosition = _selectedSpline.GetSplineLocalTangent(index, false).Translation; if (_selectedTangentIn != null) { @@ -334,76 +380,81 @@ namespace FlaxEditor.CustomEditors.Dedicated _lastTanOutPos = currentTangentOutPosition; } - if (_tanInChanged) _currentTangentMode.OnMoveTangentIn(SelectedSpline, index); - if (_tanOutChanged) _currentTangentMode.OnMoveTangentOut(SelectedSpline, index); + if (_tanInChanged) _currentTangentMode.OnMoveTangentIn(_selectedSpline, index); + if (_tanOutChanged) _currentTangentMode.OnMoveTangentOut(_selectedSpline, index); - currentTangentInPosition = SelectedSpline.GetSplineLocalTangent(index, true).Translation; - currentTangentOutPosition = SelectedSpline.GetSplineLocalTangent(index, false).Translation; + currentTangentInPosition = _selectedSpline.GetSplineLocalTangent(index, true).Translation; + currentTangentOutPosition = _selectedSpline.GetSplineLocalTangent(index, false).Translation; // update last tangents position after changes - if (SelectedSpline) _lastTanInPos = currentTangentInPosition; - if (SelectedSpline) _lastTanOutPos = currentTangentOutPosition; + if (_selectedSpline) _lastTanInPos = currentTangentInPosition; + if (_selectedSpline) _lastTanOutPos = currentTangentOutPosition; _tanInChanged = false; _tanOutChanged = false; } - private bool CanSetTangentMode() + private bool CanEditTangent() { return !HasDifferentTypes && !HasDifferentValues && (HasPointSelected || HasTangentsSelected); } private bool CanSetTangentSmoothIn() { - if (!CanSetTangentMode()) return false; + if (!CanEditTangent()) return false; return _lastPointSelected.Index != 0; } private bool CanSetTangentSmoothOut() { - if (!CanSetTangentMode()) return false; - return _lastPointSelected.Index < SelectedSpline.SplinePointsCount - 1; + if (!CanEditTangent()) return false; + return _lastPointSelected.Index < _selectedSpline.SplinePointsCount - 1; } private bool CanSetTangentFree() { - if (!CanSetTangentMode()) return false; - return _lastPointSelected.Index < SelectedSpline.SplinePointsCount - 1 && _lastPointSelected.Index != 0; + if (!CanEditTangent()) return false; + return _lastPointSelected.Index < _selectedSpline.SplinePointsCount - 1 && _lastPointSelected.Index != 0; } private bool CanSetTangentAligned() { - if (!CanSetTangentMode()) return false; - return _lastPointSelected.Index < SelectedSpline.SplinePointsCount - 1 && _lastPointSelected.Index != 0; + if (!CanEditTangent()) return false; + return _lastPointSelected.Index < _selectedSpline.SplinePointsCount - 1 && _lastPointSelected.Index != 0; } private void SetModeLinear() { + if (_currentTangentMode is LinearTangentMode) return; _currentTangentMode = new LinearTangentMode(); - _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index); + _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); } private void SetModeFree() { + if (_currentTangentMode is FreeTangentMode) return; _currentTangentMode = new FreeTangentMode(); - _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index); + _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); } private void SetModeAligned() { + if (_currentTangentMode is AlignedTangentMode) return; _currentTangentMode = new AlignedTangentMode(); - _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index); + _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); } private void SetModeSmoothIn() { + if (_currentTangentMode is SmoothInTangentMode) return; _currentTangentMode = new SmoothInTangentMode(); - _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index); + _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); } private void SetModeSmoothOut() { + if (_currentTangentMode is SmoothOutTangentMode) return; _currentTangentMode = new SmoothOutTangentMode(); - _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index); + _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); } private void UpdateSelectedPoint() @@ -429,7 +480,7 @@ namespace FlaxEditor.CustomEditors.Dedicated SetSelectedTangentTypeAsCurrent(); UpdateButtonsColors(); - _currentTangentMode.OnSelectKeyframe(SelectedSpline, index); + _currentTangentMode.OnSelectKeyframe(_selectedSpline, index); } else { @@ -462,20 +513,20 @@ namespace FlaxEditor.CustomEditors.Dedicated var index = _lastPointSelected.Index; - if (currentSelected.Transform == SelectedSpline.GetSplineTangent(index, true)) + if (currentSelected.Transform == _selectedSpline.GetSplineTangent(index, true)) { _selectedTangentIn = currentSelected as SplineNode.SplinePointTangentNode; _selectedTangentOut = null; - _currentTangentMode.OnSelectTangent(SelectedSpline, index); + _currentTangentMode.OnSelectTangent(_selectedSpline, index); return; } - if (currentSelected.Transform == SelectedSpline.GetSplineTangent(index, false)) + if (currentSelected.Transform == _selectedSpline.GetSplineTangent(index, false)) { _selectedTangentOut = currentSelected as SplineNode.SplinePointTangentNode; _selectedTangentIn = null; - _currentTangentMode.OnSelectTangent(SelectedSpline, index); + _currentTangentMode.OnSelectTangent(_selectedSpline, index); return; } @@ -485,7 +536,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private void UpdateButtonsColors() { - if (!CanSetTangentMode()) + if (!CanEditTangent()) { _linearTangentButton.Button.BackgroundColor = NormalButtonColor; _freeTangentButton.Button.BackgroundColor = NormalButtonColor; @@ -510,51 +561,67 @@ namespace FlaxEditor.CustomEditors.Dedicated private void SetSelectedTangentTypeAsCurrent() { - var isFree = IsFreeTangentMode(SelectedSpline, _lastPointSelected.Index); - var isLinear = IsLinearTangentMode(SelectedSpline, _lastPointSelected.Index); - var isAligned = IsAlignedTangentMode(SelectedSpline, _lastPointSelected.Index); - var isSmoothIn = IsSmoothInTangentMode(SelectedSpline, _lastPointSelected.Index); - var isSmoothOut = IsSmoothOutTangentMode(SelectedSpline, _lastPointSelected.Index); + if (_lastPointSelected == null || _selectedPoint == null) return; + if (IsLinearTangentMode(_selectedSpline, _lastPointSelected.Index)) SetModeLinear(); + else if (IsAlignedTangentMode(_selectedSpline, _lastPointSelected.Index)) SetModeAligned(); + else if (IsSmoothInTangentMode(_selectedSpline, _lastPointSelected.Index)) SetModeSmoothIn(); + else if (IsSmoothOutTangentMode(_selectedSpline, _lastPointSelected.Index)) SetModeSmoothOut(); + else if (IsFreeTangentMode(_selectedSpline, _lastPointSelected.Index)) SetModeFree(); + } - if (isFree) SetModeFree(); - else if (isLinear) SetModeLinear(); - else if (isAligned) SetModeAligned(); - else if (isSmoothIn) SetModeSmoothIn(); - else if (isSmoothOut) SetModeSmoothOut(); + private void StartEditSpline() + { + var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled; + + if (!enableUndo) + { + return; + } + + var splines = new List(); + + for (int i = 0; i < Values.Count; i++) + { + if (Values[i] is Spline spline) + { + splines.Add(new UndoData { + spline = spline, + beforeKeyframes = spline.SplineKeyframes.Clone() as BezierCurve.Keyframe[] + }); + } + } + + selectedSplinesUndoData = splines.ToArray(); + } + + private void EndEditSpline() + { + var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled; + + if (!enableUndo) + { + return; + } + + for (int i = 0; i < selectedSplinesUndoData.Length; i++) + { + var splineUndoData = selectedSplinesUndoData[i]; + Presenter.Undo.AddAction(new EditSplineAction(_selectedSpline, splineUndoData.beforeKeyframes)); + SplineNode.OnSplineEdited(splineUndoData.spline); + Editor.Instance.Scene.MarkSceneEdited(splineUndoData.spline.Scene); + } } private void OnSetTangentsLinear() { - var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled; - for (int i = 0; i < Values.Count; i++) - { - if (Values[i] is Spline spline) - { - var before = enableUndo ? (BezierCurve.Keyframe[])spline.SplineKeyframes.Clone() : null; - spline.SetTangentsLinear(); - if (enableUndo) - Presenter.Undo.AddAction(new EditSplineAction(spline, before)); - SplineNode.OnSplineEdited(spline); - Editor.Instance.Scene.MarkSceneEdited(spline.Scene); - } - } + _selectedSpline.SetTangentsLinear(); + _selectedSpline.UpdateSpline(); } private void OnSetTangentsSmooth() { - var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled; - for (int i = 0; i < Values.Count; i++) - { - if (Values[i] is Spline spline) - { - var before = enableUndo ? (BezierCurve.Keyframe[])spline.SplineKeyframes.Clone() : null; - spline.SetTangentsSmooth(); - if (enableUndo) - Presenter.Undo.AddAction(new EditSplineAction(spline, before)); - SplineNode.OnSplineEdited(spline); - Editor.Instance.Scene.MarkSceneEdited(spline.Scene); - } - } + _selectedSpline.SetTangentsSmooth(); + _selectedSpline.UpdateSpline(); } private static bool IsFreeTangentMode(Spline spline, int index) @@ -625,6 +692,7 @@ namespace FlaxEditor.CustomEditors.Dedicated } spline.SetSplineKeyframe(index, keyframe); + spline.UpdateSpline(); } private static void SetTangentSmoothIn(Spline spline, int index) @@ -642,6 +710,7 @@ namespace FlaxEditor.CustomEditors.Dedicated keyframe.TangentOut.Translation = Vector3.Zero; spline.SetSplineKeyframe(index, keyframe); + spline.UpdateSpline(); } private static void SetTangentSmoothOut(Spline spline, int index) @@ -660,15 +729,14 @@ namespace FlaxEditor.CustomEditors.Dedicated keyframe.TangentIn.Translation = Vector3.Zero; spline.SetSplineKeyframe(index, keyframe); + spline.UpdateSpline(); } private static void SetPointSmooth(Spline spline, int index) { var keyframe = spline.GetSplineKeyframe(index); - var tangentIn = keyframe.TangentIn; - var tangentOut = keyframe.TangentOut; - var tangentInSize = tangentIn.Translation.Length; - var tangentOutSize = tangentOut.Translation.Length; + var tangentInSize = keyframe.TangentIn.Translation.Length; + var tangentOutSize = keyframe.TangentOut.Translation.Length; var isLastKeyframe = index >= spline.SplinePointsCount - 1; var isFirstKeyframe = index <= 0; @@ -686,6 +754,7 @@ namespace FlaxEditor.CustomEditors.Dedicated keyframe.TangentIn.Translation = -slop * tangentInSize; keyframe.TangentOut.Translation = slop * tangentOutSize; spline.SetSplineKeyframe(index, keyframe); + spline.UpdateSpline(); } private static SplineNode.SplinePointNode GetSplinePointNode(Spline spline, int index)