From 2be6a6be9ec00b233345dfce4df786bd49b023be Mon Sep 17 00:00:00 2001
From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com>
Date: Tue, 18 Jul 2023 16:48:15 -0400
Subject: [PATCH] add basic options
---
.../CustomEditors/Dedicated/SplineEditor.cs | 326 ++++++++++++++++++
Source/Editor/SceneGraph/Actors/SplineNode.cs | 4 +-
2 files changed, 328 insertions(+), 2 deletions(-)
diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs
index 877342154..8f6dccd39 100644
--- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs
@@ -4,6 +4,8 @@ using FlaxEditor.Actions;
using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
using FlaxEngine.GUI;
+using FlaxEditor.CustomEditors.Elements;
+using System;
namespace FlaxEditor.CustomEditors.Dedicated
{
@@ -14,6 +16,214 @@ namespace FlaxEditor.CustomEditors.Dedicated
[CustomEditor(typeof(Spline)), DefaultEditor]
public class SplineEditor : ActorEditor
{
+ ///
+ /// Basis for creating tangent manipulation types for bezier curves.
+ ///
+ public abstract class TangentModeBase
+ {
+ ///
+ /// Called when user set selected tangent mode.
+ ///
+ /// Current spline selected on editor viewport.
+ /// Index of current keyframe selected on spline.
+ public abstract void OnSetMode(Spline spline, int index);
+
+ ///
+ /// Called when user select a keyframe (spline point) of current selected spline on editor viewport.
+ ///
+ /// Current spline selected on editor viewport.
+ /// Index of current keyframe selected on spline.
+ public abstract void OnSelectKeyframe(Spline spline, int index);
+
+ ///
+ /// Called when user select a tangent of current keyframe selected from spline.
+ ///
+ /// Current spline selected on editor viewport.
+ /// Index of current keyframe selected on spline.
+ public abstract void OnSelectTangent(Spline spline, int index);
+
+ ///
+ /// Called when the tangent in from current keyframe selected from spline is moved on editor viewport.
+ ///
+ /// Current spline selected on editor viewport.
+ /// Index of current keyframe selected on spline.
+ public abstract void OnMoveTangentIn(Spline spline, int index);
+
+ ///
+ /// Called when the tangent out from current keyframe selected from spline is moved on editor viewport.
+ ///
+ /// Current spline selected on editor viewport.
+ /// Current spline selected on editor viewport.
+ public abstract void OnMoveTangentOut(Spline spline, int index);
+ }
+
+ ///
+ /// Edit curve options manipulate the curve as free mode
+ ///
+ public sealed class FreeTangentMode : TangentModeBase
+ {
+ ///
+ public override void OnMoveTangentIn(Spline spline, int index) { }
+
+ ///
+ public override void OnMoveTangentOut(Spline spline, int index) { }
+
+ ///
+ public override void OnSelectKeyframe(Spline spline, int index) { }
+
+ ///
+ public override void OnSelectTangent(Spline spline, int index) { }
+
+ ///
+ public override void OnSetMode(Spline spline, int index) { }
+ }
+
+ ///
+ /// Edit curve options to set tangents to linear
+ ///
+ public sealed class LinearTangentMode : TangentModeBase
+ {
+ ///
+ public override void OnMoveTangentIn(Spline spline, int index) { }
+
+ ///
+ public override void OnMoveTangentOut(Spline spline, int index) { }
+
+ ///
+ public override void OnSelectKeyframe(Spline spline, int index) { }
+
+ ///
+ public override void OnSelectTangent(Spline spline, int index) { }
+
+ ///
+ public override void OnSetMode(Spline spline, int index)
+ {
+ SetKeyframeLinear(spline, index);
+ }
+
+ private void SetKeyframeLinear(Spline spline, int index)
+ {
+ var tangentIn = spline.GetSplineTangent(index, true);
+ var tangentOut = spline.GetSplineTangent(index, false);
+ tangentIn.Translation = spline.GetSplinePoint(index);
+ tangentOut.Translation = spline.GetSplinePoint(index);
+ spline.SetSplineTangent(index, tangentIn, true, false);
+ spline.SetSplineTangent(index, tangentOut, false, false);
+ spline.UpdateSpline();
+ }
+ }
+
+ ///
+ /// Edit curve options to align tangents of selected spline
+ ///
+ public sealed class AlignedTangentMode : TangentModeBase
+ {
+ ///
+ public override void OnSetMode(Spline spline, int index)
+ {
+ SmoothIfNotAligned(spline, index);
+ }
+
+ ///
+ public override void OnSelectKeyframe(Spline spline, int index)
+ {
+ SmoothIfNotAligned(spline, index);
+ }
+
+ ///
+ public override void OnSelectTangent(Spline selectedSpline, int index) { }
+
+ ///
+ public override void OnMoveTangentIn(Spline spline, int index)
+ {
+ SetPointAligned(spline, index, true);
+ }
+
+ ///
+ public override void OnMoveTangentOut(Spline spline, int index)
+ {
+ SetPointAligned(spline, index, false);
+ }
+
+ private void SmoothIfNotAligned(Spline spline, int index)
+ {
+ var keyframe = spline.GetSplineKeyframe(index);
+ var isAligned = Vector3.Dot(keyframe.TangentIn.Translation.Normalized, keyframe.TangentOut.Translation.Normalized) == 1f;
+
+ if (!isAligned)
+ {
+ SetPointSmooth(spline, index);
+ }
+ }
+
+ private 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 isLastKeyframe = index >= spline.SplinePointsCount - 1;
+ var isFirstKeyframe = index <= 0;
+
+ if (!isLastKeyframe && !isFirstKeyframe)
+ {
+ var nextKeyframe = spline.GetSplineKeyframe(++index);
+ var previousKeyframe = spline.GetSplineKeyframe(--index);
+
+ // calc form from Spline.cpp -> SetTangentsSmooth
+ var slop = (keyframe.Value.Translation - previousKeyframe.Value.Translation + nextKeyframe.Value.Translation - keyframe.Value.Translation).Normalized;
+
+ keyframe.TangentIn.Translation = -slop * tangentInSize;
+ keyframe.TangentOut.Translation = slop * tangentOutSize;
+ spline.SetSplineKeyframe(index, keyframe);
+ }
+ }
+
+ private void SetPointAligned(Spline spline, int index, bool isIn)
+ {
+ var keyframe = spline.GetSplineKeyframe(index);
+ var referenceTangent = isIn ? keyframe.TangentIn : keyframe.TangentOut;
+ var otherTangent = !isIn ? 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;
+
+ spline.SetSplineKeyframe(index, keyframe);
+ }
+ }
+
+ private TangentModeBase _currentTangentMode;
+
+ private ButtonElement _freeTangentButton;
+ private ButtonElement _linearTangentButton;
+ private ButtonElement _alignedTangentButton;
+
+ private bool _tanInChanged;
+ private bool _tanOutChanged;
+ private Vector3 _lastTanInPos;
+ private Vector3 _lastTanOutPos;
+ 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;
+
+ ///
+ /// Create a Spline editor
+ ///
+ public SplineEditor()
+ {
+ _currentTangentMode = new FreeTangentMode();
+ }
+
///
public override void Initialize(LayoutElementsContainer layout)
{
@@ -22,6 +232,21 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (Values.HasDifferentTypes == false)
{
layout.Space(10);
+
+ layout.Header("Selected spline point");
+ var selectedPointsGrid = layout.CustomContainer();
+ selectedPointsGrid.CustomControl.SlotsHorizontally = 3;
+ selectedPointsGrid.CustomControl.SlotsVertically = 1;
+
+ _linearTangentButton = selectedPointsGrid.Button("Linear");
+ _freeTangentButton = selectedPointsGrid.Button("Free");
+ _alignedTangentButton = selectedPointsGrid.Button("Aligned");
+
+ _linearTangentButton.Button.Clicked += SetModeLinear;
+ _freeTangentButton.Button.Clicked += SetModeFree;
+ _alignedTangentButton.Button.Clicked += SetModeAligned;
+
+ layout.Header("All spline points");
var grid = layout.CustomContainer();
grid.CustomControl.SlotsHorizontally = 2;
grid.CustomControl.SlotsVertically = 1;
@@ -30,6 +255,107 @@ namespace FlaxEditor.CustomEditors.Dedicated
}
}
+ ///
+ public override void Refresh()
+ {
+ base.Refresh();
+
+ UpdateSelectedPoint();
+ UpdateSelectedTangent();
+
+ var index = _lastPointSelected.Index;
+ var currentTangentInPosition = SelectedSpline.GetSplineLocalTangent(index, true).Translation;
+ var currentTangentOutPosition = SelectedSpline.GetSplineLocalTangent(index, false).Translation;
+
+ if (_selectedTangentIn != null)
+ {
+ _tanInChanged = _lastTanInPos != currentTangentInPosition;
+ _lastTanInPos = currentTangentInPosition;
+ }
+
+ if (_selectedTangentOut != null)
+ {
+ _tanOutChanged = _lastTanOutPos != currentTangentOutPosition;
+ _lastTanOutPos = currentTangentOutPosition;
+ }
+
+ if (_tanInChanged) _currentTangentMode.OnMoveTangentIn(SelectedSpline, index);
+ if (_tanOutChanged) _currentTangentMode.OnMoveTangentOut(SelectedSpline, index);
+
+ 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;
+ _tanInChanged = false;
+ _tanOutChanged = false;
+ }
+
+ private void SetModeLinear()
+ {
+ _currentTangentMode = new LinearTangentMode();
+ _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index);
+ }
+
+ private void SetModeFree()
+ {
+ _currentTangentMode = new FreeTangentMode();
+ _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index);
+ }
+
+ private void SetModeAligned()
+ {
+ _currentTangentMode = new AlignedTangentMode();
+ _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index);
+ }
+
+ private void UpdateSelectedPoint()
+ {
+ // works only if select one spline
+ if (Editor.Instance.SceneEditing.SelectionCount != 1) return;
+
+ var currentSelected = Editor.Instance.SceneEditing.Selection[0];
+
+ if (currentSelected == _lastPointSelected) return;
+ if (currentSelected is not SplineNode.SplinePointNode) return;
+
+ _lastPointSelected = currentSelected as SplineNode.SplinePointNode;
+ var index = _lastPointSelected.Index;
+ _currentTangentMode.OnSelectKeyframe(SelectedSpline, index);
+ }
+
+ private void UpdateSelectedTangent()
+ {
+ // works only if select one spline
+ if (_lastPointSelected == null || Editor.Instance.SceneEditing.SelectionCount != 1) return;
+
+ var currentSelected = Editor.Instance.SceneEditing.Selection[0];
+
+ if (currentSelected is not SplineNode.SplinePointTangentNode) return;
+ if (currentSelected == _selectedTangentIn) return;
+ if (currentSelected == _selectedTangentOut) return;
+
+ var index = _lastPointSelected.Index;
+
+ if (currentSelected.Transform == SelectedSpline.GetSplineTangent(index, true))
+ {
+ _selectedTangentIn = currentSelected as SplineNode.SplinePointTangentNode;
+ _currentTangentMode.OnSelectTangent(SelectedSpline, index);
+ return;
+ }
+
+ if (currentSelected.Transform == SelectedSpline.GetSplineTangent(index, false))
+ {
+ _selectedTangentOut = currentSelected as SplineNode.SplinePointTangentNode;
+ _currentTangentMode.OnSelectTangent(SelectedSpline, index);
+ return;
+ }
+
+ _selectedTangentIn = null;
+ _selectedTangentOut = null;
+ }
+
private void OnSetTangentsLinear()
{
var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled;
diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs
index eebee0cd9..3ba1d14c3 100644
--- a/Source/Editor/SceneGraph/Actors/SplineNode.cs
+++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs
@@ -21,7 +21,7 @@ namespace FlaxEditor.SceneGraph.Actors
[HideInEditor]
public sealed class SplineNode : ActorNode
{
- private sealed class SplinePointNode : ActorChildNode
+ public sealed class SplinePointNode : ActorChildNode
{
public unsafe SplinePointNode(SplineNode node, Guid id, int index)
: base(node, id, index)
@@ -219,7 +219,7 @@ namespace FlaxEditor.SceneGraph.Actors
}
}
- private sealed class SplinePointTangentNode : ActorChildNode
+ public sealed class SplinePointTangentNode : ActorChildNode
{
private SplineNode _node;
private int _index;