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 01/93] 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; From 2fac4c9284c394db180fcab07987972c31d4cf36 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Tue, 18 Jul 2023 20:28:28 -0400 Subject: [PATCH 02/93] add smooth tangent on first and last keyframe --- .../CustomEditors/Dedicated/SplineEditor.cs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 8f6dccd39..4ddf5c26c 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -19,7 +19,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// /// Basis for creating tangent manipulation types for bezier curves. /// - public abstract class TangentModeBase + private abstract class TangentModeBase { /// /// Called when user set selected tangent mode. @@ -60,7 +60,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// /// Edit curve options manipulate the curve as free mode /// - public sealed class FreeTangentMode : TangentModeBase + private sealed class FreeTangentMode : TangentModeBase { /// public override void OnMoveTangentIn(Spline spline, int index) { } @@ -81,7 +81,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// /// Edit curve options to set tangents to linear /// - public sealed class LinearTangentMode : TangentModeBase + private sealed class LinearTangentMode : TangentModeBase { /// public override void OnMoveTangentIn(Spline spline, int index) { } @@ -116,7 +116,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// /// Edit curve options to align tangents of selected spline /// - public sealed class AlignedTangentMode : TangentModeBase + private sealed class AlignedTangentMode : TangentModeBase { /// public override void OnSetMode(Spline spline, int index) @@ -167,18 +167,19 @@ namespace FlaxEditor.CustomEditors.Dedicated var isLastKeyframe = index >= spline.SplinePointsCount - 1; var isFirstKeyframe = index <= 0; - if (!isLastKeyframe && !isFirstKeyframe) - { - var nextKeyframe = spline.GetSplineKeyframe(++index); - var previousKeyframe = spline.GetSplineKeyframe(--index); + // force smooth it's linear point + if (tangentInSize == 0f && !isFirstKeyframe) tangentInSize = 100; + if (tangentOutSize == 0f && !isLastKeyframe) tangentOutSize = 100; - // calc form from Spline.cpp -> SetTangentsSmooth - var slop = (keyframe.Value.Translation - previousKeyframe.Value.Translation + nextKeyframe.Value.Translation - keyframe.Value.Translation).Normalized; + var nextKeyframe = !isLastKeyframe ? spline.GetSplineKeyframe(index + 1) : keyframe; + var previousKeyframe = !isFirstKeyframe ? spline.GetSplineKeyframe(index - 1) : keyframe; - keyframe.TangentIn.Translation = -slop * tangentInSize; - keyframe.TangentOut.Translation = slop * tangentOutSize; - spline.SetSplineKeyframe(index, keyframe); - } + // 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) From 590a892f16345217231cdca96bfdad2cf08acfd7 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:54:30 -0400 Subject: [PATCH 03/93] set spline point as selected when has a tangent selected and set as linear --- Source/Editor/CustomEditors/Dedicated/SplineEditor.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 4ddf5c26c..4dc0643d3 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -6,6 +6,7 @@ using FlaxEngine; using FlaxEngine.GUI; using FlaxEditor.CustomEditors.Elements; using System; +using FlaxEditor.SceneGraph; namespace FlaxEditor.CustomEditors.Dedicated { @@ -99,6 +100,14 @@ namespace FlaxEditor.CustomEditors.Dedicated public override void OnSetMode(Spline spline, int index) { SetKeyframeLinear(spline, index); + + // if has a tangent selected, change the selection to tangent parent (a spline point / keyframe) + var currentSelection = Editor.Instance.SceneEditing.Selection; + if (currentSelection.Count == 1 && currentSelection[0] is SplineNode.SplinePointTangentNode) + { + var selectedTangentNode = currentSelection[0] as SplineNode.SplinePointTangentNode; + Editor.Instance.SceneEditing.Select(selectedTangentNode.ParentNode); + } } private void SetKeyframeLinear(Spline spline, int index) From 1ec2df1a298ecf9981dbb2f0e7afe2d76aed07b7 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Wed, 19 Jul 2023 08:43:11 -0400 Subject: [PATCH 04/93] disable edit tangents buttons when don't have any tangent or point selected --- .../CustomEditors/Dedicated/SplineEditor.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 4dc0643d3..63f608eaa 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -226,6 +226,12 @@ namespace FlaxEditor.CustomEditors.Dedicated /// public Spline SelectedSpline => !Values.HasDifferentValues && Values[0] is Spline ? (Spline)Values[0] : null; + private bool HasTangentsSelected => _selectedTangentIn != null || _selectedTangentOut != null; + + private bool HasPointSelected => _lastPointSelected != null; + + private bool CanSetTangentMode => HasPointSelected || HasTangentsSelected; + /// /// Create a Spline editor /// @@ -273,6 +279,15 @@ namespace FlaxEditor.CustomEditors.Dedicated UpdateSelectedPoint(); UpdateSelectedTangent(); + _freeTangentButton.Button.Enabled = CanSetTangentMode; + _linearTangentButton.Button.Enabled = CanSetTangentMode; + _alignedTangentButton.Button.Enabled = CanSetTangentMode; + + if (_lastPointSelected == null) + { + return; + } + var index = _lastPointSelected.Index; var currentTangentInPosition = SelectedSpline.GetSplineLocalTangent(index, true).Translation; var currentTangentOutPosition = SelectedSpline.GetSplineLocalTangent(index, false).Translation; @@ -366,6 +381,8 @@ namespace FlaxEditor.CustomEditors.Dedicated _selectedTangentOut = null; } + + private void OnSetTangentsLinear() { var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled; From a42dc8f434277d7285049a94acf5791b944cbfee Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Wed, 19 Jul 2023 08:44:22 -0400 Subject: [PATCH 05/93] change tangent edit option base class name --- Source/Editor/CustomEditors/Dedicated/SplineEditor.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 63f608eaa..9d8ead2a6 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -20,7 +20,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// /// Basis for creating tangent manipulation types for bezier curves. /// - private abstract class TangentModeBase + private abstract class EditTangentOptionBase { /// /// Called when user set selected tangent mode. @@ -61,7 +61,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// /// Edit curve options manipulate the curve as free mode /// - private sealed class FreeTangentMode : TangentModeBase + private sealed class FreeTangentMode : EditTangentOptionBase { /// public override void OnMoveTangentIn(Spline spline, int index) { } @@ -82,7 +82,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// /// Edit curve options to set tangents to linear /// - private sealed class LinearTangentMode : TangentModeBase + private sealed class LinearTangentMode : EditTangentOptionBase { /// public override void OnMoveTangentIn(Spline spline, int index) { } @@ -125,7 +125,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// /// Edit curve options to align tangents of selected spline /// - private sealed class AlignedTangentMode : TangentModeBase + private sealed class AlignedTangentMode : EditTangentOptionBase { /// public override void OnSetMode(Spline spline, int index) @@ -207,7 +207,7 @@ namespace FlaxEditor.CustomEditors.Dedicated } } - private TangentModeBase _currentTangentMode; + private EditTangentOptionBase _currentTangentMode; private ButtonElement _freeTangentButton; private ButtonElement _linearTangentButton; From 95c934d6b4c80f073af17d0b300d77e6f2d6e6dc Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Wed, 19 Jul 2023 09:07:40 -0400 Subject: [PATCH 06/93] se button color when set tangent mode --- .../CustomEditors/Dedicated/SplineEditor.cs | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 9d8ead2a6..5650f9b8f 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -1,12 +1,10 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using FlaxEditor.Actions; -using FlaxEditor.SceneGraph.Actors; using FlaxEngine; using FlaxEngine.GUI; +using FlaxEditor.Actions; +using FlaxEditor.SceneGraph.Actors; using FlaxEditor.CustomEditors.Elements; -using System; -using FlaxEditor.SceneGraph; namespace FlaxEditor.CustomEditors.Dedicated { @@ -232,6 +230,16 @@ namespace FlaxEditor.CustomEditors.Dedicated private bool CanSetTangentMode => HasPointSelected || HasTangentsSelected; + private Color SelectedButtonColor => FlaxEngine.GUI.Style.Current.BackgroundSelected; + + private Color NormalButtonColor => FlaxEngine.GUI.Style.Current.BackgroundNormal; + + private bool IsFreeTangentMode => _currentTangentMode is FreeTangentMode; + + private bool IsLinearTangentMode => _currentTangentMode is LinearTangentMode; + + private bool IsAlignedTangentMode => _currentTangentMode is AlignedTangentMode; + /// /// Create a Spline editor /// @@ -262,6 +270,10 @@ namespace FlaxEditor.CustomEditors.Dedicated _freeTangentButton.Button.Clicked += SetModeFree; _alignedTangentButton.Button.Clicked += SetModeAligned; + _linearTangentButton.Button.Clicked += UpdateButtonsColors; + _freeTangentButton.Button.Clicked += UpdateButtonsColors; + _alignedTangentButton.Button.Clicked += UpdateButtonsColors; + layout.Header("All spline points"); var grid = layout.CustomContainer(); grid.CustomControl.SlotsHorizontally = 2; @@ -381,7 +393,12 @@ namespace FlaxEditor.CustomEditors.Dedicated _selectedTangentOut = null; } - + private void UpdateButtonsColors() + { + _linearTangentButton.Button.BackgroundColor = IsLinearTangentMode ? SelectedButtonColor : NormalButtonColor; + _freeTangentButton.Button.BackgroundColor = IsFreeTangentMode ? SelectedButtonColor : NormalButtonColor; + _alignedTangentButton.Button.BackgroundColor = IsAlignedTangentMode ? SelectedButtonColor : NormalButtonColor; + } private void OnSetTangentsLinear() { From 7036e77d60067164ed7ac0926d22765116b54898 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Wed, 19 Jul 2023 10:20:30 -0400 Subject: [PATCH 07/93] fix auto smooth point on click --- Source/Editor/CustomEditors/Dedicated/SplineEditor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 5650f9b8f..a3b0495cf 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -155,7 +155,8 @@ namespace FlaxEditor.CustomEditors.Dedicated 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; + var angle = Vector3.Dot(keyframe.TangentIn.Translation.Normalized, keyframe.TangentOut.Translation.Normalized); + var isAligned = angle <= -0.99f; if (!isAligned) { From 7855db951ef1236bdce98ae93906dbac76ed5408 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Wed, 19 Jul 2023 16:01:05 -0400 Subject: [PATCH 08/93] auto detect tangent type when click on spline keyframe --- .../CustomEditors/Dedicated/SplineEditor.cs | 68 ++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index a3b0495cf..b4cac340f 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -235,12 +235,6 @@ namespace FlaxEditor.CustomEditors.Dedicated private Color NormalButtonColor => FlaxEngine.GUI.Style.Current.BackgroundNormal; - private bool IsFreeTangentMode => _currentTangentMode is FreeTangentMode; - - private bool IsLinearTangentMode => _currentTangentMode is LinearTangentMode; - - private bool IsAlignedTangentMode => _currentTangentMode is AlignedTangentMode; - /// /// Create a Spline editor /// @@ -348,6 +342,43 @@ namespace FlaxEditor.CustomEditors.Dedicated _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index); } + private bool IsFreeTangentMode(Spline spline, int index) + { + if (IsLinearTangentMode(spline, index) || IsAlignedTangentMode(spline, index)) + { + return false; + } + + return true; + } + + private bool IsLinearTangentMode(Spline spline, int index) + { + var keyframe = spline.GetSplineKeyframe(index); + return keyframe.TangentIn.Translation.Length == 0 && keyframe.TangentOut.Translation.Length == 0; + } + + private bool IsAlignedTangentMode(Spline spline, int index) + { + var keyframe = spline.GetSplineKeyframe(index); + var tangentIn = keyframe.TangentIn.Translation; + var tangentOut = keyframe.TangentOut.Translation; + + if (tangentIn.Length == 0 || tangentOut.Length == 0) + { + return false; + } + + var angleBetweenTwoTangents = Vector3.Dot(tangentIn.Normalized,tangentOut.Normalized); + + if (angleBetweenTwoTangents < -0.99f) + { + return true; + } + + return false; + } + private void UpdateSelectedPoint() { // works only if select one spline @@ -360,6 +391,10 @@ namespace FlaxEditor.CustomEditors.Dedicated _lastPointSelected = currentSelected as SplineNode.SplinePointNode; var index = _lastPointSelected.Index; + + SetSelectedTangentTypeAsCurrent(); + UpdateButtonsColors(); + _currentTangentMode.OnSelectKeyframe(SelectedSpline, index); } @@ -396,9 +431,24 @@ namespace FlaxEditor.CustomEditors.Dedicated private void UpdateButtonsColors() { - _linearTangentButton.Button.BackgroundColor = IsLinearTangentMode ? SelectedButtonColor : NormalButtonColor; - _freeTangentButton.Button.BackgroundColor = IsFreeTangentMode ? SelectedButtonColor : NormalButtonColor; - _alignedTangentButton.Button.BackgroundColor = IsAlignedTangentMode ? SelectedButtonColor : NormalButtonColor; + var isFree = IsFreeTangentMode(SelectedSpline, _lastPointSelected.Index); + var isLinear = IsLinearTangentMode(SelectedSpline, _lastPointSelected.Index); + var isAligned = IsAlignedTangentMode(SelectedSpline, _lastPointSelected.Index); + + _linearTangentButton.Button.BackgroundColor = isLinear ? SelectedButtonColor : NormalButtonColor; + _freeTangentButton.Button.BackgroundColor = isFree ? SelectedButtonColor : NormalButtonColor; + _alignedTangentButton.Button.BackgroundColor = isAligned ? SelectedButtonColor : NormalButtonColor; + } + + private void SetSelectedTangentTypeAsCurrent() + { + var isFree = IsFreeTangentMode(SelectedSpline, _lastPointSelected.Index); + var isLinear = IsLinearTangentMode(SelectedSpline, _lastPointSelected.Index); + var isAligned = IsAlignedTangentMode(SelectedSpline, _lastPointSelected.Index); + + if (isFree) SetModeFree(); + else if (isLinear) SetModeLinear(); + else if (isAligned) SetModeAligned(); } private void OnSetTangentsLinear() From 8a9b76a468d740636af96ddb3d9afdffaaa56770 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Wed, 19 Jul 2023 16:28:15 -0400 Subject: [PATCH 09/93] clean code --- .../CustomEditors/Dedicated/SplineEditor.cs | 80 +++++++++---------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index b4cac340f..694a51bf9 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -154,11 +154,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private void SmoothIfNotAligned(Spline spline, int index) { - var keyframe = spline.GetSplineKeyframe(index); - var angle = Vector3.Dot(keyframe.TangentIn.Translation.Normalized, keyframe.TangentOut.Translation.Normalized); - var isAligned = angle <= -0.99f; - - if (!isAligned) + if (!IsAlignedTangentMode(spline, index)) { SetPointSmooth(spline, index); } @@ -342,43 +338,6 @@ namespace FlaxEditor.CustomEditors.Dedicated _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index); } - private bool IsFreeTangentMode(Spline spline, int index) - { - if (IsLinearTangentMode(spline, index) || IsAlignedTangentMode(spline, index)) - { - return false; - } - - return true; - } - - private bool IsLinearTangentMode(Spline spline, int index) - { - var keyframe = spline.GetSplineKeyframe(index); - return keyframe.TangentIn.Translation.Length == 0 && keyframe.TangentOut.Translation.Length == 0; - } - - private bool IsAlignedTangentMode(Spline spline, int index) - { - var keyframe = spline.GetSplineKeyframe(index); - var tangentIn = keyframe.TangentIn.Translation; - var tangentOut = keyframe.TangentOut.Translation; - - if (tangentIn.Length == 0 || tangentOut.Length == 0) - { - return false; - } - - var angleBetweenTwoTangents = Vector3.Dot(tangentIn.Normalized,tangentOut.Normalized); - - if (angleBetweenTwoTangents < -0.99f) - { - return true; - } - - return false; - } - private void UpdateSelectedPoint() { // works only if select one spline @@ -484,5 +443,42 @@ namespace FlaxEditor.CustomEditors.Dedicated } } } + + private static bool IsFreeTangentMode(Spline spline, int index) + { + if (IsLinearTangentMode(spline, index) || IsAlignedTangentMode(spline, index)) + { + return false; + } + + return true; + } + + private static bool IsLinearTangentMode(Spline spline, int index) + { + var keyframe = spline.GetSplineKeyframe(index); + return keyframe.TangentIn.Translation.Length == 0 && keyframe.TangentOut.Translation.Length == 0; + } + + private static bool IsAlignedTangentMode(Spline spline, int index) + { + var keyframe = spline.GetSplineKeyframe(index); + var tangentIn = keyframe.TangentIn.Translation; + var tangentOut = keyframe.TangentOut.Translation; + + if (tangentIn.Length == 0 || tangentOut.Length == 0) + { + return false; + } + + var angleBetweenTwoTangents = Vector3.Dot(tangentIn.Normalized, tangentOut.Normalized); + + if (angleBetweenTwoTangents < -0.99f) + { + return true; + } + + return false; + } } } From e40c0be00db33386c137ded6c5044a5e3398d9cf Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Thu, 20 Jul 2023 09:25:48 -0400 Subject: [PATCH 10/93] fix set type on press edit buttons not working --- Source/Editor/CustomEditors/Dedicated/SplineEditor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 694a51bf9..e26a05f69 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -390,9 +390,9 @@ namespace FlaxEditor.CustomEditors.Dedicated private void UpdateButtonsColors() { - var isFree = IsFreeTangentMode(SelectedSpline, _lastPointSelected.Index); - var isLinear = IsLinearTangentMode(SelectedSpline, _lastPointSelected.Index); - var isAligned = IsAlignedTangentMode(SelectedSpline, _lastPointSelected.Index); + var isFree = _currentTangentMode is FreeTangentMode; + var isLinear = _currentTangentMode is LinearTangentMode; + var isAligned = _currentTangentMode is AlignedTangentMode; _linearTangentButton.Button.BackgroundColor = isLinear ? SelectedButtonColor : NormalButtonColor; _freeTangentButton.Button.BackgroundColor = isFree ? SelectedButtonColor : NormalButtonColor; From 902a3c1eed974f9ca1ef58c47cc5c7cf013b9b83 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Thu, 20 Jul 2023 09:36:05 -0400 Subject: [PATCH 11/93] smooth point on set linear point to free --- .../CustomEditors/Dedicated/SplineEditor.cs | 88 ++++++++++--------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index e26a05f69..4df665b15 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -61,6 +61,15 @@ namespace FlaxEditor.CustomEditors.Dedicated /// private sealed class FreeTangentMode : EditTangentOptionBase { + /// + public override void OnSetMode(Spline spline, int index) + { + if (IsLinearTangentMode(spline, index)) + { + SetPointSmooth(spline, index); + } + } + /// public override void OnMoveTangentIn(Spline spline, int index) { } @@ -72,9 +81,6 @@ namespace FlaxEditor.CustomEditors.Dedicated /// public override void OnSelectTangent(Spline spline, int index) { } - - /// - public override void OnSetMode(Spline spline, int index) { } } /// @@ -82,18 +88,6 @@ namespace FlaxEditor.CustomEditors.Dedicated /// private sealed class LinearTangentMode : EditTangentOptionBase { - /// - 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) { @@ -108,6 +102,18 @@ namespace FlaxEditor.CustomEditors.Dedicated } } + /// + 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) { } + private void SetKeyframeLinear(Spline spline, int index) { var tangentIn = spline.GetSplineTangent(index, true); @@ -160,32 +166,6 @@ namespace FlaxEditor.CustomEditors.Dedicated } } - 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; - - // force smooth it's linear point - if (tangentInSize == 0f && !isFirstKeyframe) tangentInSize = 100; - if (tangentOutSize == 0f && !isLastKeyframe) tangentOutSize = 100; - - var nextKeyframe = !isLastKeyframe ? spline.GetSplineKeyframe(index + 1) : keyframe; - var previousKeyframe = !isFirstKeyframe ? spline.GetSplineKeyframe(index - 1) : keyframe; - - // 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); @@ -480,5 +460,31 @@ namespace FlaxEditor.CustomEditors.Dedicated return false; } + + 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 isLastKeyframe = index >= spline.SplinePointsCount - 1; + var isFirstKeyframe = index <= 0; + + // force smooth it's linear point + if (tangentInSize == 0f && !isFirstKeyframe) tangentInSize = 100; + if (tangentOutSize == 0f && !isLastKeyframe) tangentOutSize = 100; + + var nextKeyframe = !isLastKeyframe ? spline.GetSplineKeyframe(index + 1) : keyframe; + var previousKeyframe = !isFirstKeyframe ? spline.GetSplineKeyframe(index - 1) : keyframe; + + // 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); + } } } From 50f16881409b76085bd476514fd80c97ab7c1246 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Thu, 20 Jul 2023 10:52:07 -0400 Subject: [PATCH 12/93] add smooth in tangent and smooth out tangent --- .../CustomEditors/Dedicated/SplineEditor.cs | 136 +++++++++++++++++- 1 file changed, 134 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 4df665b15..8c3d03b2b 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -182,11 +182,100 @@ namespace FlaxEditor.CustomEditors.Dedicated } } + /// + /// Edit curve options manipulate the curve setting selected point + /// tangent in as smoothed but tangent out as linear + /// + private sealed class SmoothInTangentMode : EditTangentOptionBase + { + /// + public override void OnSetMode(Spline spline, int index) + { + var keyframe = spline.GetSplineKeyframe(index); + + // auto smooth tangent if's linear + if (keyframe.TangentIn.Translation.Length == 0) + { + var isLastKeyframe = index == spline.SplinePointsCount - 1; + var isFirstKeyframe = index == 0; + + if (!isLastKeyframe) + { + var nexKeyframe = spline.GetSplineKeyframe(index + 1); + var directionToNextKeyframe = keyframe.Value.WorldToLocalVector(keyframe.Value.Translation - nexKeyframe.Value.Translation); + directionToNextKeyframe = directionToNextKeyframe.Normalized * 100f; + keyframe.TangentIn.Translation = directionToNextKeyframe; + } + } + + keyframe.TangentOut.Translation = Vector3.Zero; + + spline.SetSplineKeyframe(index, keyframe); + } + + /// + 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) { } + } + + /// + /// Edit curve options manipulate the curve setting selected point + /// tangent in as linear but tangent out as smoothed + /// + private sealed class SmoothOutTangentMode : EditTangentOptionBase + { + /// + public override void OnSetMode(Spline spline, int index) + { + var keyframe = spline.GetSplineKeyframe(index); + + // auto smooth tangent if's linear + if (keyframe.TangentOut.Translation.Length == 0) + { + var isFirstKeyframe = index == 0; + + if (!isFirstKeyframe) + { + var previousKeyframe = spline.GetSplineKeyframe(index - 1); + var directionToPreviousKeyframe = keyframe.Value.WorldToLocalVector(keyframe.Value.Translation - previousKeyframe.Value.Translation); + directionToPreviousKeyframe = directionToPreviousKeyframe.Normalized * 100f; + keyframe.TangentOut.Translation = directionToPreviousKeyframe; + } + } + + keyframe.TangentIn.Translation = Vector3.Zero; + + spline.SetSplineKeyframe(index, keyframe); + } + + /// + 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) { } + } + private EditTangentOptionBase _currentTangentMode; private ButtonElement _freeTangentButton; private ButtonElement _linearTangentButton; private ButtonElement _alignedTangentButton; + private ButtonElement _smoothInTangentButton; + private ButtonElement _smoothOutTangentButton; private bool _tanInChanged; private bool _tanOutChanged; @@ -231,19 +320,25 @@ namespace FlaxEditor.CustomEditors.Dedicated layout.Header("Selected spline point"); var selectedPointsGrid = layout.CustomContainer(); selectedPointsGrid.CustomControl.SlotsHorizontally = 3; - selectedPointsGrid.CustomControl.SlotsVertically = 1; + selectedPointsGrid.CustomControl.SlotsVertically = 2; _linearTangentButton = selectedPointsGrid.Button("Linear"); _freeTangentButton = selectedPointsGrid.Button("Free"); _alignedTangentButton = selectedPointsGrid.Button("Aligned"); + _smoothInTangentButton = selectedPointsGrid.Button("Smooth In"); + _smoothOutTangentButton = selectedPointsGrid.Button("Smooth Out"); _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; layout.Header("All spline points"); var grid = layout.CustomContainer(); @@ -265,6 +360,8 @@ namespace FlaxEditor.CustomEditors.Dedicated _freeTangentButton.Button.Enabled = CanSetTangentMode; _linearTangentButton.Button.Enabled = CanSetTangentMode; _alignedTangentButton.Button.Enabled = CanSetTangentMode; + _smoothInTangentButton.Button.Enabled = CanSetTangentMode; + _smoothOutTangentButton.Button.Enabled = CanSetTangentMode; if (_lastPointSelected == null) { @@ -318,6 +415,18 @@ namespace FlaxEditor.CustomEditors.Dedicated _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index); } + private void SetModeSmoothIn() + { + _currentTangentMode = new SmoothInTangentMode(); + _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index); + } + + private void SetModeSmoothOut() + { + _currentTangentMode = new SmoothOutTangentMode(); + _currentTangentMode.OnSetMode(SelectedSpline, _lastPointSelected.Index); + } + private void UpdateSelectedPoint() { // works only if select one spline @@ -373,10 +482,14 @@ namespace FlaxEditor.CustomEditors.Dedicated var isFree = _currentTangentMode is FreeTangentMode; var isLinear = _currentTangentMode is LinearTangentMode; var isAligned = _currentTangentMode is AlignedTangentMode; + var isSmoothIn = _currentTangentMode is SmoothInTangentMode; + var isSmoothOut = _currentTangentMode is SmoothOutTangentMode; _linearTangentButton.Button.BackgroundColor = isLinear ? SelectedButtonColor : NormalButtonColor; _freeTangentButton.Button.BackgroundColor = isFree ? SelectedButtonColor : NormalButtonColor; _alignedTangentButton.Button.BackgroundColor = isAligned ? SelectedButtonColor : NormalButtonColor; + _smoothInTangentButton.Button.BackgroundColor = isSmoothIn ? SelectedButtonColor : NormalButtonColor; + _smoothOutTangentButton.Button.BackgroundColor = isSmoothOut ? SelectedButtonColor : NormalButtonColor; } private void SetSelectedTangentTypeAsCurrent() @@ -384,10 +497,14 @@ namespace FlaxEditor.CustomEditors.Dedicated 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 (isFree) SetModeFree(); else if (isLinear) SetModeLinear(); else if (isAligned) SetModeAligned(); + else if (isSmoothIn) SetModeSmoothIn(); + else if (isSmoothOut) SetModeSmoothIn(); } private void OnSetTangentsLinear() @@ -426,7 +543,10 @@ namespace FlaxEditor.CustomEditors.Dedicated private static bool IsFreeTangentMode(Spline spline, int index) { - if (IsLinearTangentMode(spline, index) || IsAlignedTangentMode(spline, index)) + if (IsLinearTangentMode(spline, index) || + IsAlignedTangentMode(spline, index) || + IsSmoothInTangentMode(spline, index) || + IsSmoothOutTangentMode(spline, index)) { return false; } @@ -461,6 +581,18 @@ namespace FlaxEditor.CustomEditors.Dedicated return false; } + private static bool IsSmoothInTangentMode(Spline spline, int index) + { + var keyframe = spline.GetSplineKeyframe(index); + return keyframe.TangentIn.Translation.Length > 0 && keyframe.TangentOut.Translation.Length == 0; + } + + private static bool IsSmoothOutTangentMode(Spline spline, int index) + { + var keyframe = spline.GetSplineKeyframe(index); + return keyframe.TangentOut.Translation.Length > 0 && keyframe.TangentIn.Translation.Length == 0; + } + private static void SetPointSmooth(Spline spline, int index) { var keyframe = spline.GetSplineKeyframe(index); From e6715ad095b10283cac12d5a4bea05892c3df9dd Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Thu, 20 Jul 2023 13:05:56 -0400 Subject: [PATCH 13/93] add methods to auto select tangents or points on change edit mode on spline editor --- .../CustomEditors/Dedicated/SplineEditor.cs | 69 ++++++++++++++++--- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 8c3d03b2b..6d954949c 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -3,6 +3,7 @@ using FlaxEngine; using FlaxEngine.GUI; using FlaxEditor.Actions; +using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph.Actors; using FlaxEditor.CustomEditors.Elements; @@ -93,13 +94,8 @@ namespace FlaxEditor.CustomEditors.Dedicated { SetKeyframeLinear(spline, index); - // if has a tangent selected, change the selection to tangent parent (a spline point / keyframe) - var currentSelection = Editor.Instance.SceneEditing.Selection; - if (currentSelection.Count == 1 && currentSelection[0] is SplineNode.SplinePointTangentNode) - { - var selectedTangentNode = currentSelection[0] as SplineNode.SplinePointTangentNode; - Editor.Instance.SceneEditing.Select(selectedTangentNode.ParentNode); - } + // change the selection to tangent parent (a spline point / keyframe) + SetSelectSplinePointNode(spline, index); } /// @@ -197,7 +193,6 @@ namespace FlaxEditor.CustomEditors.Dedicated if (keyframe.TangentIn.Translation.Length == 0) { var isLastKeyframe = index == spline.SplinePointsCount - 1; - var isFirstKeyframe = index == 0; if (!isLastKeyframe) { @@ -211,6 +206,7 @@ namespace FlaxEditor.CustomEditors.Dedicated keyframe.TangentOut.Translation = Vector3.Zero; spline.SetSplineKeyframe(index, keyframe); + SetSelectTangentIn(spline, index); } /// @@ -254,6 +250,7 @@ namespace FlaxEditor.CustomEditors.Dedicated keyframe.TangentIn.Translation = Vector3.Zero; spline.SetSplineKeyframe(index, keyframe); + SetSelectTangentOut(spline, index); } /// @@ -618,5 +615,61 @@ namespace FlaxEditor.CustomEditors.Dedicated keyframe.TangentOut.Translation = slop * tangentOutSize; spline.SetSplineKeyframe(index, keyframe); } + + private static SplineNode.SplinePointNode GetSplinePointNode(Spline spline, int index) + { + return (SplineNode.SplinePointNode)SceneGraphFactory.FindNode(spline.ID).ChildNodes[index]; + } + + private static SplineNode.SplinePointTangentNode GetSplineTangentInNode(Spline spline, int index) + { + var point = GetSplinePointNode(spline, index); + var tangentIn = spline.GetSplineTangent(index, true); + var tangentNodes = point.ChildNodes; + + // find tangent in node comparing all child nodes position + for (int i = 0; i < tangentNodes.Count; i++) + { + if (tangentNodes[i].Transform.Translation == tangentIn.Translation) + { + return (SplineNode.SplinePointTangentNode)tangentNodes[i]; + } + } + + return null; + } + + private static SplineNode.SplinePointTangentNode GetSplineTangentOutNode(Spline spline, int index) + { + var point = GetSplinePointNode(spline, index); + var tangentOut = spline.GetSplineTangent(index, false); + var tangentNodes = point.ChildNodes; + + // find tangent out node comparing all child nodes position + for (int i = 0; i < tangentNodes.Count; i++) + { + if (tangentNodes[i].Transform.Translation == tangentOut.Translation) + { + return (SplineNode.SplinePointTangentNode)tangentNodes[i]; + } + } + + return null; + } + + private static void SetSelectSplinePointNode(Spline spline, int index) + { + Editor.Instance.SceneEditing.Select(GetSplinePointNode(spline, index)); + } + + private static void SetSelectTangentIn(Spline spline, int index) + { + Editor.Instance.SceneEditing.Select(GetSplineTangentInNode(spline, index)); + } + + private static void SetSelectTangentOut(Spline spline, int index) + { + Editor.Instance.SceneEditing.Select(GetSplineTangentOutNode(spline, index)); + } } } From 4cf2bba84061c4edb5a982fa214f83ed79f484f9 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Thu, 20 Jul 2023 13:19:18 -0400 Subject: [PATCH 14/93] fix edit buttons style on spline editor --- Source/Editor/CustomEditors/Dedicated/SplineEditor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 6d954949c..0803f93d9 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -319,6 +319,8 @@ namespace FlaxEditor.CustomEditors.Dedicated selectedPointsGrid.CustomControl.SlotsHorizontally = 3; selectedPointsGrid.CustomControl.SlotsVertically = 2; + selectedPointsGrid.Control.Size *= new Float2(1, 2); + _linearTangentButton = selectedPointsGrid.Button("Linear"); _freeTangentButton = selectedPointsGrid.Button("Free"); _alignedTangentButton = selectedPointsGrid.Button("Aligned"); From 5c7faf7262cf02bad1d7d0e58263af0abf070210 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Thu, 20 Jul 2023 16:40:05 -0400 Subject: [PATCH 15/93] disable edit tangent options when deselect tangents or spline points on spline editor --- .../CustomEditors/Dedicated/SplineEditor.cs | 62 +++++++++++++++---- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 0803f93d9..ba0eff06f 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -278,6 +278,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private bool _tanOutChanged; private Vector3 _lastTanInPos; private Vector3 _lastTanOutPos; + private SplineNode.SplinePointNode _selectedPoint; private SplineNode.SplinePointNode _lastPointSelected; private SplineNode.SplinePointTangentNode _selectedTangentIn; private SplineNode.SplinePointTangentNode _selectedTangentOut; @@ -289,7 +290,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private bool HasTangentsSelected => _selectedTangentIn != null || _selectedTangentOut != null; - private bool HasPointSelected => _lastPointSelected != null; + private bool HasPointSelected => _selectedPoint != null; private bool CanSetTangentMode => HasPointSelected || HasTangentsSelected; @@ -362,7 +363,7 @@ namespace FlaxEditor.CustomEditors.Dedicated _smoothInTangentButton.Button.Enabled = CanSetTangentMode; _smoothOutTangentButton.Button.Enabled = CanSetTangentMode; - if (_lastPointSelected == null) + if (!CanSetTangentMode) { return; } @@ -429,30 +430,54 @@ namespace FlaxEditor.CustomEditors.Dedicated private void UpdateSelectedPoint() { // works only if select one spline - if (Editor.Instance.SceneEditing.SelectionCount != 1) return; + if (Editor.Instance.SceneEditing.SelectionCount != 1) + { + _selectedPoint = null; + UpdateButtonsColors(); + return; + } var currentSelected = Editor.Instance.SceneEditing.Selection[0]; - if (currentSelected == _lastPointSelected) return; - if (currentSelected is not SplineNode.SplinePointNode) return; + if (currentSelected == _selectedPoint) return; + if (currentSelected is SplineNode.SplinePointNode) + { + _selectedPoint = currentSelected as SplineNode.SplinePointNode; + _lastPointSelected = _selectedPoint; - _lastPointSelected = currentSelected as SplineNode.SplinePointNode; - var index = _lastPointSelected.Index; + var index = _lastPointSelected.Index; - SetSelectedTangentTypeAsCurrent(); - UpdateButtonsColors(); + SetSelectedTangentTypeAsCurrent(); + UpdateButtonsColors(); - _currentTangentMode.OnSelectKeyframe(SelectedSpline, index); + _currentTangentMode.OnSelectKeyframe(SelectedSpline, index); + } + else + { + _selectedPoint = null; + UpdateButtonsColors(); + } } private void UpdateSelectedTangent() { // works only if select one spline - if (_lastPointSelected == null || Editor.Instance.SceneEditing.SelectionCount != 1) return; + if (_lastPointSelected == null || Editor.Instance.SceneEditing.SelectionCount != 1) + { + _selectedTangentIn = null; + _selectedTangentOut = null; + return; + } var currentSelected = Editor.Instance.SceneEditing.Selection[0]; - if (currentSelected is not SplineNode.SplinePointTangentNode) return; + if (currentSelected is not SplineNode.SplinePointTangentNode) + { + _selectedTangentIn = null; + _selectedTangentOut = null; + return; + } + if (currentSelected == _selectedTangentIn) return; if (currentSelected == _selectedTangentOut) return; @@ -461,13 +486,16 @@ namespace FlaxEditor.CustomEditors.Dedicated if (currentSelected.Transform == SelectedSpline.GetSplineTangent(index, true)) { _selectedTangentIn = currentSelected as SplineNode.SplinePointTangentNode; + _selectedTangentOut = null; _currentTangentMode.OnSelectTangent(SelectedSpline, index); + return; } if (currentSelected.Transform == SelectedSpline.GetSplineTangent(index, false)) { _selectedTangentOut = currentSelected as SplineNode.SplinePointTangentNode; + _selectedTangentIn = null; _currentTangentMode.OnSelectTangent(SelectedSpline, index); return; } @@ -478,6 +506,16 @@ namespace FlaxEditor.CustomEditors.Dedicated private void UpdateButtonsColors() { + if (!CanSetTangentMode) + { + _linearTangentButton.Button.BackgroundColor = NormalButtonColor; + _freeTangentButton.Button.BackgroundColor = NormalButtonColor; + _alignedTangentButton.Button.BackgroundColor = NormalButtonColor; + _smoothInTangentButton.Button.BackgroundColor = NormalButtonColor; + _smoothOutTangentButton.Button.BackgroundColor = NormalButtonColor; + return; + } + var isFree = _currentTangentMode is FreeTangentMode; var isLinear = _currentTangentMode is LinearTangentMode; var isAligned = _currentTangentMode is AlignedTangentMode; From 008f219883b60add1247617aec69cad9a1abf6f5 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Fri, 21 Jul 2023 10:25:55 -0400 Subject: [PATCH 16/93] organize smooth in and smooth out tangents code --- .../CustomEditors/Dedicated/SplineEditor.cs | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index ba0eff06f..9b5d420b3 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -187,25 +187,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// public override void OnSetMode(Spline spline, int index) { - var keyframe = spline.GetSplineKeyframe(index); - - // auto smooth tangent if's linear - if (keyframe.TangentIn.Translation.Length == 0) - { - var isLastKeyframe = index == spline.SplinePointsCount - 1; - - if (!isLastKeyframe) - { - var nexKeyframe = spline.GetSplineKeyframe(index + 1); - var directionToNextKeyframe = keyframe.Value.WorldToLocalVector(keyframe.Value.Translation - nexKeyframe.Value.Translation); - directionToNextKeyframe = directionToNextKeyframe.Normalized * 100f; - keyframe.TangentIn.Translation = directionToNextKeyframe; - } - } - - keyframe.TangentOut.Translation = Vector3.Zero; - - spline.SetSplineKeyframe(index, keyframe); + SetSelectTangentIn(spline, index); SetSelectTangentIn(spline, index); } @@ -231,25 +213,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// public override void OnSetMode(Spline spline, int index) { - var keyframe = spline.GetSplineKeyframe(index); - - // auto smooth tangent if's linear - if (keyframe.TangentOut.Translation.Length == 0) - { - var isFirstKeyframe = index == 0; - - if (!isFirstKeyframe) - { - var previousKeyframe = spline.GetSplineKeyframe(index - 1); - var directionToPreviousKeyframe = keyframe.Value.WorldToLocalVector(keyframe.Value.Translation - previousKeyframe.Value.Translation); - directionToPreviousKeyframe = directionToPreviousKeyframe.Normalized * 100f; - keyframe.TangentOut.Translation = directionToPreviousKeyframe; - } - } - - keyframe.TangentIn.Translation = Vector3.Zero; - - spline.SetSplineKeyframe(index, keyframe); + SetTangentSmoothOut(spline, index); SetSelectTangentOut(spline, index); } @@ -630,6 +594,51 @@ namespace FlaxEditor.CustomEditors.Dedicated return keyframe.TangentOut.Translation.Length > 0 && keyframe.TangentIn.Translation.Length == 0; } + private static void SetTangentSmoothIn(Spline spline, int index) + { + var keyframe = spline.GetSplineKeyframe(index); + + // auto smooth tangent if's linear + if (keyframe.TangentIn.Translation.Length == 0) + { + var isLastKeyframe = index == spline.SplinePointsCount - 1; + + if (!isLastKeyframe) + { + var nexKeyframe = spline.GetSplineKeyframe(index + 1); + var directionToNextKeyframe = keyframe.Value.WorldToLocalVector(keyframe.Value.Translation - nexKeyframe.Value.Translation); + directionToNextKeyframe = directionToNextKeyframe.Normalized * 100f; + keyframe.TangentIn.Translation = directionToNextKeyframe; + } + } + + keyframe.TangentOut.Translation = Vector3.Zero; + spline.SetSplineKeyframe(index, keyframe); + } + + private static void SetTangentSmoothOut(Spline spline, int index) + { + var keyframe = spline.GetSplineKeyframe(index); + + // auto smooth tangent if's linear + if (keyframe.TangentOut.Translation.Length == 0) + { + var isFirstKeyframe = index == 0; + + if (!isFirstKeyframe) + { + var previousKeyframe = spline.GetSplineKeyframe(index - 1); + var directionToPreviousKeyframe = keyframe.Value.WorldToLocalVector(keyframe.Value.Translation - previousKeyframe.Value.Translation); + directionToPreviousKeyframe = directionToPreviousKeyframe.Normalized * 100f; + keyframe.TangentOut.Translation = directionToPreviousKeyframe; + } + } + + keyframe.TangentIn.Translation = Vector3.Zero; + + spline.SetSplineKeyframe(index, keyframe); + } + private static void SetPointSmooth(Spline spline, int index) { var keyframe = spline.GetSplineKeyframe(index); From 9b0c6a070a9d64a0c0e0cc4478a86af752cdcf82 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 21 Jul 2023 13:26:51 -0500 Subject: [PATCH 17/93] Add `InputEvent` and `InputAxis` editors and changed `UICanvas` to use them. --- .../CustomEditors/Editors/InputEditor.cs | 151 ++++++++++++++++++ Source/Engine/UI/GUI/CanvasRootControl.cs | 10 +- Source/Engine/UI/UICanvas.cs | 118 +++++++++----- 3 files changed, 236 insertions(+), 43 deletions(-) create mode 100644 Source/Editor/CustomEditors/Editors/InputEditor.cs diff --git a/Source/Editor/CustomEditors/Editors/InputEditor.cs b/Source/Editor/CustomEditors/Editors/InputEditor.cs new file mode 100644 index 000000000..0c00900b6 --- /dev/null +++ b/Source/Editor/CustomEditors/Editors/InputEditor.cs @@ -0,0 +1,151 @@ + +using System; +using System.Collections.Generic; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.CustomEditors.Editors +{ + /// + /// Default implementation of the inspector used to edit input event properties. + /// + [CustomEditor(typeof(InputEvent)), DefaultEditor] + public class InputEventEditor : CustomEditor + { + private Dropdown _dropdown; + + /// + public override DisplayStyle Style => DisplayStyle.Inline; + + /// + public override void Initialize(LayoutElementsContainer layout) + { + var dropdownElement = layout.Custom(); + _dropdown = dropdownElement.CustomControl; + var eventNames = new List(); + foreach (var mapping in Input.ActionMappings) + { + if (eventNames.Contains(mapping.Name)) + continue; + eventNames.Add(mapping.Name); + } + _dropdown.Items = eventNames; + var value = Values[0] as InputEvent; + if (value != null) + { + if (eventNames.Contains(value.Name)) + { + _dropdown.SelectedItem = value.Name; + } + } + + _dropdown.SelectedIndexChanged += OnSelectedIndexChanged; + } + + private void OnSelectedIndexChanged(Dropdown dropdown) + { + SetValue(new InputEvent(dropdown.SelectedItem)); + } + + /// + public override void Refresh() + { + base.Refresh(); + + if (HasDifferentValues) + { + + } + else + { + if (Values[0] is InputEvent eventValue) + { + if (_dropdown.Items.Contains(eventValue.Name) && string.Equals(_dropdown.SelectedItem, eventValue.Name, StringComparison.Ordinal)) + { + _dropdown.SelectedItem = eventValue.Name; + } + } + } + } + + /// + protected override void Deinitialize() + { + if (_dropdown != null) + _dropdown.SelectedIndexChanged -= OnSelectedIndexChanged; + _dropdown = null; + base.Deinitialize(); + } + } + + /// + /// Default implementation of the inspector used to edit input axis properties. + /// + [CustomEditor(typeof(InputAxis)), DefaultEditor] + public class InputAxisEditor : CustomEditor + { + private Dropdown _dropdown; + + /// + public override DisplayStyle Style => DisplayStyle.Inline; + + /// + public override void Initialize(LayoutElementsContainer layout) + { + var dropdownElement = layout.Custom(); + _dropdown = dropdownElement.CustomControl; + var axisNames = new List(); + foreach (var mapping in Input.AxisMappings) + { + if (axisNames.Contains(mapping.Name)) + continue; + axisNames.Add(mapping.Name); + } + _dropdown.Items = axisNames; + var value = Values[0] as InputAxis; + if (value != null) + { + if (axisNames.Contains(value.Name)) + { + _dropdown.SelectedItem = value.Name; + } + } + _dropdown.SelectedIndexChanged += OnSelectedIndexChanged; + } + + private void OnSelectedIndexChanged(Dropdown dropdown) + { + SetValue(new InputAxis(dropdown.SelectedItem)); + } + + /// + public override void Refresh() + { + base.Refresh(); + + if (HasDifferentValues) + { + + } + else + { + if (Values[0] is InputAxis axisValue) + { + if (_dropdown.Items.Contains(axisValue.Name) && string.Equals(_dropdown.SelectedItem, axisValue.Name, StringComparison.Ordinal)) + { + _dropdown.SelectedItem = axisValue.Name; + } + } + } + } + + /// + protected override void Deinitialize() + { + if (_dropdown != null) + _dropdown.SelectedIndexChanged -= OnSelectedIndexChanged; + _dropdown = null; + base.Deinitialize(); + } + } +} diff --git a/Source/Engine/UI/GUI/CanvasRootControl.cs b/Source/Engine/UI/GUI/CanvasRootControl.cs index 408c46b0f..b4fa067e3 100644 --- a/Source/Engine/UI/GUI/CanvasRootControl.cs +++ b/Source/Engine/UI/GUI/CanvasRootControl.cs @@ -199,11 +199,11 @@ namespace FlaxEngine.GUI // UI navigation if (_canvas.ReceivesEvents) { - UpdateNavigation(deltaTime, _canvas.NavigationInputActionUp, NavDirection.Up, ref _navigationHeldTimeUp, ref _navigationRateTimeUp); - UpdateNavigation(deltaTime, _canvas.NavigationInputActionDown, NavDirection.Down, ref _navigationHeldTimeDown, ref _navigationRateTimeDown); - UpdateNavigation(deltaTime, _canvas.NavigationInputActionLeft, NavDirection.Left, ref _navigationHeldTimeLeft, ref _navigationRateTimeLeft); - UpdateNavigation(deltaTime, _canvas.NavigationInputActionRight, NavDirection.Right, ref _navigationHeldTimeRight, ref _navigationRateTimeRight); - UpdateNavigation(deltaTime, _canvas.NavigationInputActionSubmit, ref _navigationHeldTimeSubmit, ref _navigationRateTimeSubmit, SubmitFocused); + UpdateNavigation(deltaTime, _canvas.NavigateUp.Name, NavDirection.Up, ref _navigationHeldTimeUp, ref _navigationRateTimeUp); + UpdateNavigation(deltaTime, _canvas.NavigateDown.Name, NavDirection.Down, ref _navigationHeldTimeDown, ref _navigationRateTimeDown); + UpdateNavigation(deltaTime, _canvas.NavigateLeft.Name, NavDirection.Left, ref _navigationHeldTimeLeft, ref _navigationRateTimeLeft); + UpdateNavigation(deltaTime, _canvas.NavigateRight.Name, NavDirection.Right, ref _navigationHeldTimeRight, ref _navigationRateTimeRight); + UpdateNavigation(deltaTime, _canvas.NavigateSubmit.Name, ref _navigationHeldTimeSubmit, ref _navigationRateTimeSubmit, SubmitFocused); } else { diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index 87fc9506f..024fb8be0 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -263,39 +263,39 @@ namespace FlaxEngine public float NavigationInputRepeatRate { get; set; } = 0.05f; /// - /// The name of the input action for performing UI navigation Up (from Input Settings). + /// The input action for performing UI navigation Up (from Input Settings). /// [EditorOrder(510), EditorDisplay("Navigation", "Navigate Up")] - [Tooltip("The name of the input action for performing UI navigation Up (from Input Settings).")] - public string NavigationInputActionUp { get; set; } = "NavigateUp"; + [Tooltip("The input action for performing UI navigation Up (from Input Settings).")] + public InputEvent NavigateUp { get; set; } = new InputEvent("NavigateUp"); /// - /// The name of the input action for performing UI navigation Down (from Input Settings). + /// The input action for performing UI navigation Down (from Input Settings). /// [EditorOrder(520), EditorDisplay("Navigation", "Navigate Down")] - [Tooltip("The name of the input action for performing UI navigation Down (from Input Settings).")] - public string NavigationInputActionDown { get; set; } = "NavigateDown"; + [Tooltip("The input action for performing UI navigation Down (from Input Settings).")] + public InputEvent NavigateDown { get; set; } = new InputEvent("NavigateDown"); /// - /// The name of the input action for performing UI navigation Left (from Input Settings). + /// The input action for performing UI navigation Left (from Input Settings). /// [EditorOrder(530), EditorDisplay("Navigation", "Navigate Left")] - [Tooltip("The name of the input action for performing UI navigation Left (from Input Settings).")] - public string NavigationInputActionLeft { get; set; } = "NavigateLeft"; - + [Tooltip("The input action for performing UI navigation Left (from Input Settings).")] + public InputEvent NavigateLeft { get; set; } = new InputEvent("NavigateLeft"); + /// - /// The name of the input action for performing UI navigation Right (from Input Settings). + /// The input action for performing UI navigation Right (from Input Settings). /// [EditorOrder(540), EditorDisplay("Navigation", "Navigate Right")] - [Tooltip("The name of the input action for performing UI navigation Right (from Input Settings).")] - public string NavigationInputActionRight { get; set; } = "NavigateRight"; + [Tooltip("The input action for performing UI navigation Right (from Input Settings).")] + public InputEvent NavigateRight { get; set; } = new InputEvent("NavigateRight"); /// - /// The name of the input action for performing UI navigation Submit (from Input Settings). + /// The input action for performing UI navigation Submit (from Input Settings). /// - [EditorOrder(540), EditorDisplay("Navigation", "Navigate Submit")] - [Tooltip("The name of the input action for performing UI navigation Submit (from Input Settings).")] - public string NavigationInputActionSubmit { get; set; } = "NavigateSubmit"; + [EditorOrder(550), EditorDisplay("Navigation", "Navigate Submit")] + [Tooltip("The input action for performing UI navigation Submit (from Input Settings).")] + public InputEvent NavigateSubmit { get; set; } = new InputEvent("NavigateSubmit"); #endregion @@ -620,14 +620,36 @@ namespace FlaxEngine jsonWriter.WriteValue(NavigationInputRepeatDelay); jsonWriter.WritePropertyName("NavigationInputRepeatRate"); jsonWriter.WriteValue(NavigationInputRepeatRate); - jsonWriter.WritePropertyName("NavigationInputActionUp"); - jsonWriter.WriteValue(NavigationInputActionUp); - jsonWriter.WritePropertyName("NavigationInputActionDown"); - jsonWriter.WriteValue(NavigationInputActionDown); - jsonWriter.WritePropertyName("NavigationInputActionLeft"); - jsonWriter.WriteValue(NavigationInputActionLeft); - jsonWriter.WritePropertyName("NavigationInputActionRight"); - jsonWriter.WriteValue(NavigationInputActionRight); + + jsonWriter.WritePropertyName("NavigateUp"); + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("Name"); + jsonWriter.WriteValue(NavigateUp.Name); + jsonWriter.WriteEndObject(); + + jsonWriter.WritePropertyName("NavigateDown"); + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("Name"); + jsonWriter.WriteValue(NavigateDown.Name); + jsonWriter.WriteEndObject(); + + jsonWriter.WritePropertyName("NavigateLeft"); + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("Name"); + jsonWriter.WriteValue(NavigateLeft.Name); + jsonWriter.WriteEndObject(); + + jsonWriter.WritePropertyName("NavigateRight"); + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("Name"); + jsonWriter.WriteValue(NavigateRight.Name); + jsonWriter.WriteEndObject(); + + jsonWriter.WritePropertyName("NavigateSubmit"); + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("Name"); + jsonWriter.WriteValue(NavigateSubmit.Name); + jsonWriter.WriteEndObject(); jsonWriter.WriteEndObject(); } @@ -713,27 +735,47 @@ namespace FlaxEngine jsonWriter.WritePropertyName("NavigationInputRepeatRate"); jsonWriter.WriteValue(NavigationInputRepeatRate); } - if (NavigationInputActionUp != other.NavigationInputActionUp) + if (NavigateUp.Name != other.NavigateUp.Name) { - jsonWriter.WritePropertyName("NavigationInputActionUp"); - jsonWriter.WriteValue(NavigationInputActionUp); + jsonWriter.WritePropertyName("NavigateUp"); + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("Name"); + jsonWriter.WriteValue(NavigateUp.Name); + jsonWriter.WriteEndObject(); } - if (NavigationInputActionDown != other.NavigationInputActionDown) + if (NavigateDown.Name != other.NavigateDown.Name) { - jsonWriter.WritePropertyName("NavigationInputActionDown"); - jsonWriter.WriteValue(NavigationInputActionDown); + jsonWriter.WritePropertyName("NavigateDown"); + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("Name"); + jsonWriter.WriteValue(NavigateDown.Name); + jsonWriter.WriteEndObject(); } - if (NavigationInputActionLeft != other.NavigationInputActionLeft) + if (NavigateLeft.Name != other.NavigateLeft.Name) { - jsonWriter.WritePropertyName("NavigationInputActionLeft"); - jsonWriter.WriteValue(NavigationInputActionLeft); + jsonWriter.WritePropertyName("NavigateLeft"); + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("Name"); + jsonWriter.WriteValue(NavigateLeft.Name); + jsonWriter.WriteEndObject(); } - if (NavigationInputActionRight != other.NavigationInputActionRight) + if (NavigateRight.Name != other.NavigateRight.Name) { - jsonWriter.WritePropertyName("NavigationInputActionRight"); - jsonWriter.WriteValue(NavigationInputActionRight); + jsonWriter.WritePropertyName("NavigateRight"); + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("Name"); + jsonWriter.WriteValue(NavigateRight.Name); + jsonWriter.WriteEndObject(); } - + if (NavigateSubmit.Name != other.NavigateSubmit.Name) + { + jsonWriter.WritePropertyName("NavigateSubmit"); + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("Name"); + jsonWriter.WriteValue(NavigateSubmit.Name); + jsonWriter.WriteEndObject(); + } + jsonWriter.WriteEndObject(); } From 0ba9f7c51dd5721fe97c79a49ded9795770aebe9 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 21 Jul 2023 13:33:00 -0500 Subject: [PATCH 18/93] Small simplification of null checking for input editors --- Source/Editor/CustomEditors/Editors/InputEditor.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/InputEditor.cs b/Source/Editor/CustomEditors/Editors/InputEditor.cs index 0c00900b6..3f8aaf093 100644 --- a/Source/Editor/CustomEditors/Editors/InputEditor.cs +++ b/Source/Editor/CustomEditors/Editors/InputEditor.cs @@ -30,8 +30,7 @@ namespace FlaxEditor.CustomEditors.Editors eventNames.Add(mapping.Name); } _dropdown.Items = eventNames; - var value = Values[0] as InputEvent; - if (value != null) + if (Values[0] is InputEvent value) { if (eventNames.Contains(value.Name)) { @@ -102,8 +101,7 @@ namespace FlaxEditor.CustomEditors.Editors axisNames.Add(mapping.Name); } _dropdown.Items = axisNames; - var value = Values[0] as InputAxis; - if (value != null) + if (Values[0] is InputAxis value) { if (axisNames.Contains(value.Name)) { From 6872f35add9c03ea2ca5e42438ea3eba189b9d07 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Fri, 21 Jul 2023 21:00:08 -0400 Subject: [PATCH 19/93] don't let set smooth tangent in/out on start/end spline if isn't loop --- .../CustomEditors/Dedicated/SplineEditor.cs | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 9b5d420b3..56aaf5d7c 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -187,7 +187,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// public override void OnSetMode(Spline spline, int index) { - SetSelectTangentIn(spline, index); + SetTangentSmoothIn(spline, index); SetSelectTangentIn(spline, index); } @@ -252,11 +252,8 @@ namespace FlaxEditor.CustomEditors.Dedicated /// public Spline SelectedSpline => !Values.HasDifferentValues && Values[0] is Spline ? (Spline)Values[0] : null; - private bool HasTangentsSelected => _selectedTangentIn != null || _selectedTangentOut != null; - private bool HasPointSelected => _selectedPoint != null; - - private bool CanSetTangentMode => HasPointSelected || HasTangentsSelected; + private bool HasTangentsSelected => _selectedTangentIn != null || _selectedTangentOut != null; private Color SelectedButtonColor => FlaxEngine.GUI.Style.Current.BackgroundSelected; @@ -321,18 +318,19 @@ namespace FlaxEditor.CustomEditors.Dedicated UpdateSelectedPoint(); UpdateSelectedTangent(); - _freeTangentButton.Button.Enabled = CanSetTangentMode; - _linearTangentButton.Button.Enabled = CanSetTangentMode; - _alignedTangentButton.Button.Enabled = CanSetTangentMode; - _smoothInTangentButton.Button.Enabled = CanSetTangentMode; - _smoothOutTangentButton.Button.Enabled = CanSetTangentMode; + _freeTangentButton.Button.Enabled = CanSetTangentMode(); + _linearTangentButton.Button.Enabled = CanSetTangentMode(); + _alignedTangentButton.Button.Enabled = CanSetTangentMode(); + _smoothInTangentButton.Button.Enabled = CanSetTangentSmoothIn(); + _smoothOutTangentButton.Button.Enabled = CanSetTangentSmoothOut(); - if (!CanSetTangentMode) + if (!CanSetTangentMode()) { return; } var index = _lastPointSelected.Index; + var currentTangentInPosition = SelectedSpline.GetSplineLocalTangent(index, true).Translation; var currentTangentOutPosition = SelectedSpline.GetSplineLocalTangent(index, false).Translation; @@ -361,6 +359,23 @@ namespace FlaxEditor.CustomEditors.Dedicated _tanOutChanged = false; } + private bool CanSetTangentMode() + { + return HasPointSelected || HasTangentsSelected; + } + + private bool CanSetTangentSmoothIn() + { + if (!CanSetTangentMode()) return false; + return _lastPointSelected.Index != 0; + } + + private bool CanSetTangentSmoothOut() + { + if (!CanSetTangentMode()) return false; + return _lastPointSelected.Index < SelectedSpline.SplinePointsCount - 1; + } + private void SetModeLinear() { _currentTangentMode = new LinearTangentMode(); @@ -470,7 +485,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private void UpdateButtonsColors() { - if (!CanSetTangentMode) + if (!CanSetTangentMode()) { _linearTangentButton.Button.BackgroundColor = NormalButtonColor; _freeTangentButton.Button.BackgroundColor = NormalButtonColor; @@ -505,7 +520,7 @@ namespace FlaxEditor.CustomEditors.Dedicated else if (isLinear) SetModeLinear(); else if (isAligned) SetModeAligned(); else if (isSmoothIn) SetModeSmoothIn(); - else if (isSmoothOut) SetModeSmoothIn(); + else if (isSmoothOut) SetModeSmoothOut(); } private void OnSetTangentsLinear() @@ -601,15 +616,10 @@ namespace FlaxEditor.CustomEditors.Dedicated // auto smooth tangent if's linear if (keyframe.TangentIn.Translation.Length == 0) { - var isLastKeyframe = index == spline.SplinePointsCount - 1; - - if (!isLastKeyframe) - { - var nexKeyframe = spline.GetSplineKeyframe(index + 1); - var directionToNextKeyframe = keyframe.Value.WorldToLocalVector(keyframe.Value.Translation - nexKeyframe.Value.Translation); - directionToNextKeyframe = directionToNextKeyframe.Normalized * 100f; - keyframe.TangentIn.Translation = directionToNextKeyframe; - } + var previousKeyframe = spline.GetSplineKeyframe(index - 1); + var tangentDirection = keyframe.Value.WorldToLocalVector(previousKeyframe.Value.Translation - keyframe.Value.Translation); + tangentDirection = tangentDirection.Normalized * 100f; + keyframe.TangentIn.Translation = tangentDirection; } keyframe.TangentOut.Translation = Vector3.Zero; @@ -623,15 +633,10 @@ namespace FlaxEditor.CustomEditors.Dedicated // auto smooth tangent if's linear if (keyframe.TangentOut.Translation.Length == 0) { - var isFirstKeyframe = index == 0; - - if (!isFirstKeyframe) - { - var previousKeyframe = spline.GetSplineKeyframe(index - 1); - var directionToPreviousKeyframe = keyframe.Value.WorldToLocalVector(keyframe.Value.Translation - previousKeyframe.Value.Translation); - directionToPreviousKeyframe = directionToPreviousKeyframe.Normalized * 100f; - keyframe.TangentOut.Translation = directionToPreviousKeyframe; - } + var nextKeyframe = spline.GetSplineKeyframe(index + 1); + var tangentDirection = keyframe.Value.WorldToLocalVector(nextKeyframe.Value.Translation - keyframe.Value.Translation); + tangentDirection = tangentDirection.Normalized * 100f; + keyframe.TangentOut.Translation = tangentDirection; } keyframe.TangentIn.Translation = Vector3.Zero; From 1bd7210107069270c419a62ab8a4642540f9b367 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Fri, 21 Jul 2023 21:05:36 -0400 Subject: [PATCH 20/93] don't let aligned or free mode on first or end spline point --- .../CustomEditors/Dedicated/SplineEditor.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 56aaf5d7c..d83aa1acc 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -318,9 +318,9 @@ namespace FlaxEditor.CustomEditors.Dedicated UpdateSelectedPoint(); UpdateSelectedTangent(); - _freeTangentButton.Button.Enabled = CanSetTangentMode(); + _freeTangentButton.Button.Enabled = CanSetTangentFree(); _linearTangentButton.Button.Enabled = CanSetTangentMode(); - _alignedTangentButton.Button.Enabled = CanSetTangentMode(); + _alignedTangentButton.Button.Enabled = CanSetTangentAligned(); _smoothInTangentButton.Button.Enabled = CanSetTangentSmoothIn(); _smoothOutTangentButton.Button.Enabled = CanSetTangentSmoothOut(); @@ -376,6 +376,18 @@ namespace FlaxEditor.CustomEditors.Dedicated return _lastPointSelected.Index < SelectedSpline.SplinePointsCount - 1; } + private bool CanSetTangentFree() + { + if (!CanSetTangentMode()) 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; + } + private void SetModeLinear() { _currentTangentMode = new LinearTangentMode(); From f8da0708e1f0645b40d7527bfc9b0389b491f2a5 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Fri, 21 Jul 2023 22:11:15 -0400 Subject: [PATCH 21/93] add suport to loop spline on set tangent linear --- .../CustomEditors/Dedicated/SplineEditor.cs | 30 +++++++++++-------- Source/Editor/SceneGraph/Actors/SplineNode.cs | 1 + 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index d83aa1acc..ca442d66f 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -109,17 +109,6 @@ namespace FlaxEditor.CustomEditors.Dedicated /// public override void OnSelectTangent(Spline spline, int 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(); - } } /// @@ -330,7 +319,6 @@ namespace FlaxEditor.CustomEditors.Dedicated } var index = _lastPointSelected.Index; - var currentTangentInPosition = SelectedSpline.GetSplineLocalTangent(index, true).Translation; var currentTangentOutPosition = SelectedSpline.GetSplineLocalTangent(index, false).Translation; @@ -621,6 +609,24 @@ namespace FlaxEditor.CustomEditors.Dedicated return keyframe.TangentOut.Translation.Length > 0 && keyframe.TangentIn.Translation.Length == 0; } + private static void SetKeyframeLinear(Spline spline, int index) + { + var keyframe = spline.GetSplineKeyframe(index); + keyframe.TangentIn.Translation = Vector3.Zero; + keyframe.TangentOut.Translation = Vector3.Zero; + + var lastSplineIndex = spline.SplinePointsCount - 1; + if (index == lastSplineIndex && spline.IsLoop) + { + var lastPoint = spline.GetSplineKeyframe(lastSplineIndex); + lastPoint.TangentIn.Translation = Vector3.Zero; + lastPoint.TangentOut.Translation = Vector3.Zero; + spline.SetSplineKeyframe(lastSplineIndex, lastPoint); + } + + spline.SetSplineKeyframe(index, keyframe); + } + private static void SetTangentSmoothIn(Spline spline, int index) { var keyframe = spline.GetSplineKeyframe(index); diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 3ba1d14c3..1c47c7d98 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -257,6 +257,7 @@ namespace FlaxEditor.SceneGraph.Actors public override void OnDebugDraw(ViewportDebugDrawData data) { + Debug.Log(Name); // Draw spline and spline point ParentNode.OnDebugDraw(data); From bd8c5985b12ed919d4da91a198c21814683f53c1 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Fri, 21 Jul 2023 22:11:30 -0400 Subject: [PATCH 22/93] remove debug --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 1c47c7d98..3ba1d14c3 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -257,7 +257,6 @@ namespace FlaxEditor.SceneGraph.Actors public override void OnDebugDraw(ViewportDebugDrawData data) { - Debug.Log(Name); // Draw spline and spline point ParentNode.OnDebugDraw(data); From 1a72aeeb24635cd87fd091bc0a263e2baf072abc Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Fri, 21 Jul 2023 22:15:43 -0400 Subject: [PATCH 23/93] disable edit tangent buttons when select multiple values --- Source/Editor/CustomEditors/Dedicated/SplineEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index ca442d66f..c33690e25 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -349,7 +349,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private bool CanSetTangentMode() { - return HasPointSelected || HasTangentsSelected; + return !HasDifferentTypes && !HasDifferentValues && (HasPointSelected || HasTangentsSelected); } private bool CanSetTangentSmoothIn() From c79c91a19a90b0f6fd10c33aae0f45f52e4eddd1 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Sun, 23 Jul 2023 20:27:45 -0400 Subject: [PATCH 24/93] add undo / redo to edit spline options and some small changes --- .../CustomEditors/Dedicated/SplineEditor.cs | 263 +++++++++++------- 1 file changed, 166 insertions(+), 97 deletions(-) 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) From 56285e544e4996265b254e7c4c9effc448bb324b Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 24 Jul 2023 08:54:53 -0400 Subject: [PATCH 25/93] disable set all linear / smooth when select many splines --- .../Editor/CustomEditors/Dedicated/SplineEditor.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 4e0234974..8d2644378 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -356,8 +356,8 @@ namespace FlaxEditor.CustomEditors.Dedicated _alignedTangentButton.Button.Enabled = CanSetTangentAligned(); _smoothInTangentButton.Button.Enabled = CanSetTangentSmoothIn(); _smoothOutTangentButton.Button.Enabled = CanSetTangentSmoothOut(); - _setLinearAllTangentsButton.Button.Enabled = CanEditTangent(); - _setSmoothAllTangentsButton.Button.Enabled = CanEditTangent(); + _setLinearAllTangentsButton.Button.Enabled = CanSetAllTangentsLinear(); + _setSmoothAllTangentsButton.Button.Enabled = CanSetAllTangentsSmooth(); if (!CanEditTangent()) { @@ -422,6 +422,16 @@ namespace FlaxEditor.CustomEditors.Dedicated return _lastPointSelected.Index < _selectedSpline.SplinePointsCount - 1 && _lastPointSelected.Index != 0; } + private bool CanSetAllTangentsSmooth() + { + return _selectedSpline != null; + } + + private bool CanSetAllTangentsLinear() + { + return _selectedSpline != null; + } + private void SetModeLinear() { if (_currentTangentMode is LinearTangentMode) return; From 6a65ccd5efd3b807d0138308d33cc6574ec480ea Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Mon, 24 Jul 2023 22:10:12 +0200 Subject: [PATCH 26/93] rev1 --- Source/Editor/Content/ContentFilter.cs | 262 ++++++++++++++++++ .../Content/GUI/ContentNavigationBar.cs | 27 ++ .../Content/GUI/ContentNavigationButton.cs | 2 +- .../GUI/ContentNavigationButtonSeparator.cs | 85 ++++++ .../Content/GUI/ContentSettingsDropdown.cs | 95 +++++++ Source/Editor/Content/GUI/ContentView.cs | 9 +- Source/Editor/Content/Items/ContentItem.cs | 8 +- Source/Editor/GUI/ComboBox.cs | 72 +++-- .../Windows/ContentWindow.Navigation.cs | 10 +- Source/Editor/Windows/ContentWindow.cs | 59 ++-- 10 files changed, 588 insertions(+), 41 deletions(-) create mode 100644 Source/Editor/Content/ContentFilter.cs create mode 100644 Source/Editor/Content/GUI/ContentNavigationBar.cs create mode 100644 Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs create mode 100644 Source/Editor/Content/GUI/ContentSettingsDropdown.cs diff --git a/Source/Editor/Content/ContentFilter.cs b/Source/Editor/Content/ContentFilter.cs new file mode 100644 index 000000000..2cbd78e65 --- /dev/null +++ b/Source/Editor/Content/ContentFilter.cs @@ -0,0 +1,262 @@ +using FlaxEditor.Content; +using FlaxEditor.Content.GUI; +using FlaxEngine; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +namespace FlaxEditor.Content +{ + /// + /// class with is controling visability of items in content window + /// + internal class ContentFilter + { + #region Filters Config + /// + /// all suported files by engine Content Folder + /// + public static readonly List SuportedFileExtencionsInContentFolder = new List() + { + ".flax", + ".json", + ".scene", + ".prefab", + }; + /// + /// all suported files by engine Source Folder + /// + public static readonly List SuportedFileExtencionsInSourceFolder = new List() + { + ".shader", + ".cs", + ".h", + ".cpp", + }; + /// + /// ignores folders in source folder (top layer), + /// for example obj folder is default c# project folder + /// + internal static readonly List HideFoldersInSourceFolder = new List() + { + "obj", // default c# project folder + "Properties", // c# project stuff ? + }; + /// + /// ignores files in source folder (top layer), + /// + internal static readonly List HideFilesInSourceFolder = new List() //dont edit + { + "Game.csproj", //solucion file + "Game.Gen.cs", //auto-generated not be edited + "GameEditorTarget.Build.cs", + "GameTarget.Build.cs", + }; + #endregion + internal static ContentItem gameSettings; + internal static List buildFiles; + + internal static ContentTreeNode settings; + internal static ContentTreeNode shaders; + internal static ProjectTreeNode engine; + internal static List plugins = new(); + internal static List FilterFolder(ContentFolder folder) + { + return FilterFolder(folder, SuportedFileExtencionsInContentFolder.ToArray(), SuportedFileExtencionsInSourceFolder.ToArray()); + } + internal static List FilterFolder(ContentFolder folder, string[] FileExtencionsInContentFolder, string[] ExtencionsInSourceFolder) + { + if (folder.CanHaveAssets) + { + for (int i = 0; i < folder.Children.Count; i++) + { + bool Visible = false; + for (int j = 0; j < FileExtencionsInContentFolder.Length; j++) + { + if ((folder.Children[i].Path.EndsWith(FileExtencionsInContentFolder[j]) || folder.Children[i].IsFolder)) + { + if (folder.Children[i].Visible) + { + Visible = true; + } + break; + } + } + folder.Children[i].Visible = Visible; + } + } + else if (folder.CanHaveScripts) + { + for (int i = 0; i < folder.Children.Count; i++) + { + bool Visible = false; + for (int j = 0; j < ExtencionsInSourceFolder.Length; j++) + { + if ((folder.Children[i].Path.EndsWith(ExtencionsInSourceFolder[j]) || folder.Children[i].IsFolder)) + { + if (folder.Children[i].Visible) + { + Visible = true; + } + break; + } + } + folder.Children[i].Visible = Visible; + } + } + return folder.Children; + } + internal static ProjectTreeNode Filter(ProjectTreeNode tree) + { + var content = tree.Children[0] as ContentTreeNode; + var source = tree.Children[1] as ContentTreeNode; + //filter content folder (top layer) + buildFiles = new(); + for (int i = 0; i < content.Folder.Children.Count; i++) + { + if (content.Folder.Children[i].FileName == "GameSettings.json") + { + gameSettings = content.Folder.Children[i]; + content.Folder.Children[i].Visible = false; + break; + } + } + + int hindenCount = 0; + + for (int i = content.Children.Count - 1; i >= 0; i--)//we are starting from back it faster + { + var node = content.Children[i] as ContentTreeNode; + if (node.Folder.FileName == "Settings") + { + settings = node; + hindenCount++; + node.Visible = false; + node.Folder.Visible = false; + } + if (node.Folder.FileName == "Shaders") + { + shaders = node; + hindenCount++; + node.Visible = false; + node.Folder.Visible = false; + + } + if (hindenCount == 2) + break; + } + + + //----------------------------------------------------------------------------------------------------- + + //filter source folder (top layer) + hindenCount = 0; + for (int i = 0; i < source.Folder.Children.Count; i++) + { + for (int j = 0; j < HideFilesInSourceFolder.Count; j++) + { + if (source.Folder.Children[i].FileName == HideFilesInSourceFolder[j]) + { + source.Folder.Children[i].Visible = false; + hindenCount++; + if(i > 1) + { + buildFiles.Add(source.Folder.Children[i]); + } + if (HideFilesInSourceFolder.Count == hindenCount) goto HideFilesInSourceFolderComplited; + break; + } + } + } + HideFilesInSourceFolderComplited: + hindenCount = 0; + for (int i = source.Children.Count - 1; i >= 0; i--) + { + var node = source.Children[i] as ContentTreeNode; + for (int j = 0; j < HideFoldersInSourceFolder.Count; j++) + { + if (node.Folder.FileName == HideFoldersInSourceFolder[j]) + { + node.Visible = false; + node.Folder.Visible = false; + hindenCount++; + if (HideFoldersInSourceFolder.Count == hindenCount) goto HideFoldersInSourceFolderComplited; + break; + } + } + } + HideFoldersInSourceFolderComplited: + //content + return tree; + } + internal static void UpdateFilterVisability(ContentSettingsDropdown dropdown) + { + engine.Visible = false; + engine.Folder.Visible = false; + + foreach (var item in plugins) + { + item.Visible = false; + item.Folder.Visible = false; + } + foreach (var item in buildFiles) + { + item.Visible = false; + } + gameSettings.Visible = false; + settings.Visible = false; + settings.Folder.Visible = false; + + shaders.Visible = false; + shaders.Folder.Visible = false; + + for (int i = 0; i < dropdown.Selection.Count; i++) + { + if (dropdown.Selection[i] == 0) //Show Engine Content + { + engine.Visible = true; + engine.Folder.Visible = true; + Editor.Instance.Windows.ContentWin.RefreshView(engine); + } + + if (dropdown.Selection[i] == 1)//Show Plugin Content + { + foreach (var item in plugins) + { + item.Visible = true; + item.Folder.Visible = true; + Editor.Instance.Windows.ContentWin.RefreshView(item); + } + } + + if (dropdown.Selection[i] == 2)//Show Build Files + { + foreach (var item in buildFiles) + { + item.Visible = true; + } + } + + if (dropdown.Selection[i] == 3)//Show Game Settings + { + gameSettings.Visible = true; + settings.Visible = true; + settings.Folder.Visible = true; + Editor.Instance.Windows.ContentWin.RefreshView((ContentTreeNode)settings.Parent); + } + + if (dropdown.Selection[i] == 4)//"Show Shader Source" + { + shaders.Visible = true; + shaders.Folder.Visible = true; + Editor.Instance.Windows.ContentWin.RefreshView(shaders); + } + } + + + + } + } +} diff --git a/Source/Editor/Content/GUI/ContentNavigationBar.cs b/Source/Editor/Content/GUI/ContentNavigationBar.cs new file mode 100644 index 000000000..673ac806e --- /dev/null +++ b/Source/Editor/Content/GUI/ContentNavigationBar.cs @@ -0,0 +1,27 @@ +using FlaxEditor.GUI; +using FlaxEngine; + +namespace FlaxEditor.Content.GUI +{ + internal class ContentNavigationBar : NavigationBar + { + ToolStrip _toolstrip; + internal float ofssetFromRightEdge = 80; + internal ContentNavigationBar(ToolStrip toolstrip) : base() + { + _toolstrip = toolstrip; + } + /// + protected override void Arrange() + { + base.Arrange(); + var lastToolstripButton = _toolstrip.LastButton; + var parentSize = Parent.Size; + Bounds = new Rectangle + ( + new Float2(lastToolstripButton.Right, 0), + new Float2(parentSize.X - X - ofssetFromRightEdge, _toolstrip.Height) + ); + } + } +} diff --git a/Source/Editor/Content/GUI/ContentNavigationButton.cs b/Source/Editor/Content/GUI/ContentNavigationButton.cs index d37e1629b..beec247da 100644 --- a/Source/Editor/Content/GUI/ContentNavigationButton.cs +++ b/Source/Editor/Content/GUI/ContentNavigationButton.cs @@ -31,7 +31,7 @@ namespace FlaxEditor.Content.GUI : base(x, y, height) { TargetNode = targetNode; - Text = targetNode.NavButtonLabel + "/"; + Text = targetNode.NavButtonLabel; } /// diff --git a/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs b/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs new file mode 100644 index 000000000..cad63066d --- /dev/null +++ b/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs @@ -0,0 +1,85 @@ +using FlaxEditor; +using FlaxEditor.GUI; +using FlaxEditor.GUI.ContextMenu; +using FlaxEngine; +using FlaxEngine.GUI; +using System.Collections.Generic; + +namespace FlaxEditor.Content.GUI +{ + internal class ContentNavigationButtonSeparator : ComboBox + { + public ContentNavigationButton Target { get; } + public ContentNavigationButtonSeparator(ContentNavigationButton target, float x, float y,float height) + { + Target = target; + Bounds = new Rectangle(x, y, 16, height); + Offsets = new Margin(Bounds.X, Bounds.Width, Bounds.Y, Bounds.Height); + UpdateTransform(); + + MaximumItemsInViewCount = 20; + var style = Style.Current; + BackgroundColor = style.BackgroundNormal; + BackgroundColorHighlighted = BackgroundColor; + BackgroundColorSelected = BackgroundColor; + } + protected override ContextMenu OnCreatePopup() + { + UpdateDropDownItems(); + return base.OnCreatePopup(); + } + internal void UpdateDropDownItems() + { + ClearItems(); + _items = new(); + for (int i = 0; i < Target.TargetNode.Children.Count; i++) + { + if (Target.TargetNode.Children[i] is ContentTreeNode node) + { + if (node.Folder.VisibleInHierarchy) // respect the filter set by ContentFilterConfig.Filter(...) + AddItem(node.Folder.ShortName); + } + } + } + /// + public override void Draw() + { + // Cache data + var style = Style.Current; + var clientRect = new Rectangle(Float2.Zero, Size); + // Draw background + if (IsDragOver) + { + Render2D.FillRectangle(clientRect, Style.Current.BackgroundSelected * 0.6f); + } + else if (_mouseDown) + { + Render2D.FillRectangle(clientRect, style.BackgroundSelected); + } + else if (IsMouseOver) + { + Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); + } + + Render2D.DrawSprite(Editor.Instance.Icons.ArrowRight12, new Rectangle(clientRect.Location.X, clientRect.Y + ((Size.Y / 2f)/2f), Size.X, Size.X), EnabledInHierarchy ? Style.Current.Foreground : Style.Current.ForegroundDisabled); + } + protected override void OnLayoutMenuButton(ref ContextMenuButton button, int index, bool construct = false) + { + var style = Style.Current; + button.Icon = Editor.Instance.Icons.FolderClosed32; + if (_tooltips != null && _tooltips.Length > index) + { + button.TooltipText = _tooltips[index]; + } + } + protected override void OnItemClicked(int index) + { + base.OnItemClicked(index); + if (Target.TargetNode.Children[index] is ContentTreeNode node) + { + Editor.Instance.Windows.ContentWin.Navigate(node); + } + // Navigate calls the OnDestroy at some point dont place code below or editor will crash + } + } +} diff --git a/Source/Editor/Content/GUI/ContentSettingsDropdown.cs b/Source/Editor/Content/GUI/ContentSettingsDropdown.cs new file mode 100644 index 000000000..04722a674 --- /dev/null +++ b/Source/Editor/Content/GUI/ContentSettingsDropdown.cs @@ -0,0 +1,95 @@ +using FlaxEditor.Content; +using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.GUI; +using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.States; +using FlaxEditor.Utilities; +using FlaxEngine; +using FlaxEngine.GUI; +using FlaxEngine.Json; +using System.Collections.Generic; +using System.Linq; + +namespace FlaxEditor.Content.GUI +{ + class ContentSettingsDropdown : ComboBox + { + internal readonly List Settings = new() + { + "Show Engine Content", + "Show Plugin Content", + "Show Build Files", + "Show Game Settings", + "Show Shader Source" + }; + public ContentSettingsDropdown() + { + SupportMultiSelect = true; + MaximumItemsInViewCount = 20; + var style = Style.Current; + BackgroundColor = style.BackgroundNormal; + BackgroundColorHighlighted = BackgroundColor; + BackgroundColorSelected = BackgroundColor; + + } + protected override ContextMenu OnCreatePopup() + { + UpdateDropDownItems(); + return base.OnCreatePopup(); + } + internal void UpdateDropDownItems() + { + ClearItems(); + + for (int i = 0; i < Settings.Count; i++) + { + AddItem(Settings[i]); + } + } + /// + public override void Draw() + { + // Cache data + var style = Style.Current; + var clientRect = new Rectangle(Float2.Zero, Size); + // Draw background + if (IsDragOver) + { + Render2D.FillRectangle(clientRect, Style.Current.BackgroundSelected * 0.6f); + } + else if (_mouseDown) + { + Render2D.FillRectangle(clientRect, style.BackgroundSelected); + } + else if (IsMouseOver) + { + Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); + } + float size = (Size.Y / 1.5f); //Icon size + // Draw text + Render2D.DrawText(Font.GetFont(), "Settings", new Rectangle(size, 0, Size.X - Size.Y, Size.Y), TextColor, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawSprite(Editor.Instance.Icons.Settings12, new Rectangle((Size.Y - size) / 2.0f, (Size.Y - size) / 2.0f, size, size)); + } + protected override void OnLayoutMenuButton(ref ContextMenuButton button, int index, bool construct = false) + { + var style = Style.Current; + if (_selectedIndices.Contains(index)) + { + button.Icon = style.CheckBoxTick; + } + else + { + button.Icon = SpriteHandle.Default; + } + if (_tooltips != null && _tooltips.Length > index) + { + button.TooltipText = _tooltips[index]; + } + } + protected override void OnSelectedIndexChanged() + { + ContentFilter.UpdateFilterVisability(this); + base.OnSelectedIndexChanged(); + } + } +} diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 6065ca9f8..9dca03953 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -261,11 +261,14 @@ namespace FlaxEditor.Content.GUI ClearItems(); // Add references and link items - _items.AddRange(items); for (int i = 0; i < items.Count; i++) { - items[i].Parent = this; - items[i].AddReference(this); + if (items[i].Visible) + { + items[i].Parent = this; + items[i].AddReference(this); + _items.Add(items[i]); + } } if (selection != null) { diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 66825fb42..377c6dac0 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -441,6 +441,11 @@ namespace FlaxEditor.Content { get { + if (!Visible) + { + return Rectangle.Empty; + } + var view = Parent as ContentView; var size = Size; switch (view?.ViewType ?? ContentViewType.Tiles) @@ -448,7 +453,7 @@ namespace FlaxEditor.Content case ContentViewType.Tiles: { var textHeight = DefaultTextHeight * size.X / DefaultWidth; - return new Rectangle(0, size.Y - textHeight, size.X, textHeight); + return new Rectangle(0, size.Y - textHeight, size.X - 2, textHeight); } case ContentViewType.List: { @@ -642,6 +647,7 @@ namespace FlaxEditor.Content /// public override void Draw() { + // Cache data var size = Size; var style = Style.Current; diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 2481316d0..da38268b2 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using FlaxEngine; using FlaxEngine.GUI; +using FlaxEditor.GUI.ContextMenu; namespace FlaxEditor.GUI { @@ -396,14 +397,23 @@ namespace FlaxEditor.GUI _popupMenu.ButtonClicked += btn => { OnItemClicked((int)btn.Tag); - _popupMenu?.Hide(); + if (SupportMultiSelect) + { + UpdateButtons(); + _popupMenu?.PerformLayout(); + } + else + { + _popupMenu?.Hide(); + }//[nori_sc] don't hide it Support MultiSelect is on actions per min is important for UX, if some one wont to set more then 5 elements in multi select menu let them do it }; } // Check if menu hs been already shown if (_popupMenu.Visible) { - _popupMenu.Hide(); + if (!SupportMultiSelect) + _popupMenu.Hide(); return; } @@ -412,31 +422,59 @@ namespace FlaxEditor.GUI // Check if has any items if (_items.Count > 0) { - // Setup items list + UpdateButtons(); + // Show dropdown list + _popupMenu.MinimumWidth = Width; + _popupMenu.Show(this, new Float2(1, Height)); + } + } + /// + /// update buttons layout and repains + /// + private void UpdateButtons() + { + if (_popupMenu.Items.Count() != _items.Count) + { var itemControls = _popupMenu.Items.ToArray(); foreach (var e in itemControls) e.Dispose(); if (Sorted) _items.Sort(); - var style = Style.Current; for (int i = 0; i < _items.Count; i++) { var btn = _popupMenu.AddButton(_items[i]); - if (_selectedIndices.Contains(i)) - { - btn.Icon = style.CheckBoxTick; - } - if (_tooltips != null && _tooltips.Length > i) - { - btn.TooltipText = _tooltips[i]; - } - + OnLayoutMenuButton(ref btn, i, true); btn.Tag = i; } - - // Show dropdown list - _popupMenu.MinimumWidth = Width; - _popupMenu.Show(this, new Float2(1, Height)); + } + else + { + var itemControls = _popupMenu.Items.ToArray(); + if (Sorted) + _items.Sort(); + for (int i = 0; i < _items.Count; i++) + { + if (itemControls[i] is ContextMenuButton btn) + { + btn.Text = _items[i]; + OnLayoutMenuButton(ref btn, i, true); + } + } + } + } + /// + /// caled when button is created or repainted u can overite it to give the button custom look + /// + /// button refrance + /// curent item index + /// true if button is created else it is repainting the button + protected virtual void OnLayoutMenuButton(ref FlaxEditor.GUI.ContextMenu.ContextMenuButton button,int index, bool construct = false) + { + var style = Style.Current; + button.Checked = _selectedIndices.Contains(index); + if (_tooltips != null && _tooltips.Length > index) + { + button.TooltipText = _tooltips[index]; } } diff --git a/Source/Editor/Windows/ContentWindow.Navigation.cs b/Source/Editor/Windows/ContentWindow.Navigation.cs index ad690c2d0..ca3de992d 100644 --- a/Source/Editor/Windows/ContentWindow.Navigation.cs +++ b/Source/Editor/Windows/ContentWindow.Navigation.cs @@ -196,12 +196,18 @@ namespace FlaxEditor.Windows } float x = NavigationBar.DefaultButtonsMargin; float h = _toolStrip.ItemsHeight - 2 * ToolStrip.DefaultMarginV; - for (int i = nodes.Count - 1; i >= 0; i--) + for (int i = nodes.Count - 2; i >= 0; i--) { - var button = new ContentNavigationButton(nodes[i], x, ToolStrip.DefaultMarginV, h); + var button = new ContentNavigationButton(nodes[i], x - 100, ToolStrip.DefaultMarginV, h); button.PerformLayout(); x += button.Width + NavigationBar.DefaultButtonsMargin; _navigationBar.AddChild(button); + if (i == 0) + continue; + var buttonSeparator = new ContentNavigationButtonSeparator(button, x, ToolStrip.DefaultMarginV, h); + buttonSeparator.PerformLayout(); + x += buttonSeparator.Width + NavigationBar.DefaultButtonsMargin; + _navigationBar.AddChild(buttonSeparator); } nodes.Clear(); diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index f993b79f9..58aaf3e4c 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -39,11 +39,13 @@ namespace FlaxEditor.Windows private readonly ToolStripButton _navigateForwardButton; private readonly ToolStripButton _navigateUpButton; - private NavigationBar _navigationBar; + private ContentNavigationBar _navigationBar; private Tree _tree; private TextBox _foldersSearchBox; private TextBox _itemsSearchBox; private ViewDropdown _viewDropdown; + private ContentSettingsDropdown _ContentSettingDropdown; + private const float _ContentDropdownSizeX = 100; private SortType _sortType; private RootContentTreeNode _root; @@ -73,7 +75,7 @@ namespace FlaxEditor.Windows { Title = "Content"; Icon = editor.Icons.Folder32; - + // Content database events editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true; editor.ContentDatabase.ItemRemoved += OnContentDatabaseItemRemoved; @@ -107,11 +109,25 @@ namespace FlaxEditor.Windows _navigateBackwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Left64, NavigateBackward).LinkTooltip("Navigate backward"); _navigateForwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Right64, NavigateForward).LinkTooltip("Navigate forward"); _navigateUpButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Up64, NavigateUp).LinkTooltip("Navigate up"); - + _toolStrip.AddSeparator(); // Navigation bar - _navigationBar = new NavigationBar + + _navigationBar = new ContentNavigationBar(_toolStrip) { - Parent = this, + Parent = _toolStrip, + ofssetFromRightEdge = _ContentDropdownSizeX + }; + var DropdownSettingsPanel = new Panel(ScrollBars.None) + { + Parent = _toolStrip, + Size = new Float2(_ContentDropdownSizeX, _toolStrip.Height) + }; + //setings Dropdown + _ContentSettingDropdown = new ContentSettingsDropdown + { + AnchorPreset = AnchorPresets.StretchAll, + Parent = DropdownSettingsPanel, + Offsets = new Margin(2, 4, 4, 4) }; // Split panel @@ -215,7 +231,7 @@ namespace FlaxEditor.Windows private ContextMenu OnViewDropdownPopupCreate(ComboBox comboBox) { var menu = new ContextMenu(); - + var showFileExtensionsButton = menu.AddButton("Show file extensions", () => View.ShowFileExtensions = !View.ShowFileExtensions); showFileExtensionsButton.Checked = View.ShowFileExtensions; showFileExtensionsButton.AutoCheck = true; @@ -877,8 +893,9 @@ namespace FlaxEditor.Windows } else { + // Show folder contents - _view.ShowItems(target.Folder.Children, _sortType, false, true); + _view.ShowItems(ContentFilter.FilterFolder(target.Folder), _sortType, false, true); } } @@ -918,15 +935,17 @@ namespace FlaxEditor.Windows _root.Expand(true); // Add game project on top, plugins in the middle and engine at bottom - _root.AddChild(Editor.ContentDatabase.Game); + _root.AddChild(ContentFilter.Filter(Editor.ContentDatabase.Game)); foreach (var project in Editor.ContentDatabase.Projects) { project.SortChildrenRecursive(); if (project == Editor.ContentDatabase.Game || project == Editor.ContentDatabase.Engine) continue; _root.AddChild(project); + ContentFilter.plugins.Add(project); } _root.AddChild(Editor.ContentDatabase.Engine); + ContentFilter.engine = Editor.ContentDatabase.Engine; Editor.ContentDatabase.Game?.Expand(true); _tree.Margin = new Margin(0.0f, 0.0f, -16.0f, 2.0f); // Hide root node @@ -1035,15 +1054,6 @@ namespace FlaxEditor.Windows return base.OnMouseUp(location, button); } - - /// - protected override void PerformLayoutBeforeChildren() - { - base.PerformLayoutBeforeChildren(); - - _navigationBar?.UpdateBounds(_toolStrip); - } - /// public override bool UseLayoutData => true; @@ -1054,6 +1064,12 @@ namespace FlaxEditor.Windows writer.WriteAttributeString("Scale", _view.ViewScale.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("ShowFileExtensions", _view.ShowFileExtensions.ToString()); writer.WriteAttributeString("ViewType", _view.ViewType.ToString()); + for (int i = 0; i < _ContentSettingDropdown.Selection.Count; i++) + { + if(_ContentSettingDropdown.Selection.Count != 0) + writer.WriteAttributeString(_ContentSettingDropdown.Settings[_ContentSettingDropdown.Selection[i]].Replace(' ', '_'), _ContentSettingDropdown.Selection[i].ToString()); + } + } /// @@ -1066,6 +1082,15 @@ namespace FlaxEditor.Windows _view.ShowFileExtensions = value2; if (Enum.TryParse(node.GetAttribute("ViewType"), out ContentViewType viewType)) _view.ViewType = viewType; + + for (int i = 0; i < _ContentSettingDropdown.Settings.Count; i++) + { + int flag; + if (int.TryParse(node.GetAttribute(_ContentSettingDropdown.Settings[i].Replace(' ', '_')), out flag)) + _ContentSettingDropdown.Selection.Add(flag); + + } + ContentFilter.UpdateFilterVisability(_ContentSettingDropdown); } /// From 7b0e9b9e06a2fd03e7a85db61c91de24b4fd8b1e Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 24 Jul 2023 19:30:31 -0400 Subject: [PATCH 27/93] simplify code --- .../CustomEditors/Dedicated/SplineEditor.cs | 115 ++++++++++-------- 1 file changed, 61 insertions(+), 54 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 8d2644378..2f9eaf0d0 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -328,6 +328,10 @@ namespace FlaxEditor.CustomEditors.Dedicated _setSmoothAllTangentsButton.Button.Clicked += EndEditSpline; if (_selectedSpline) _selectedSpline.SplineUpdated += OnSplineEdited; + + SetSelectedTangentTypeAsCurrent(); + SetEditButtonsColor(); + SetEditButtonsEnabled(); } } @@ -340,7 +344,8 @@ namespace FlaxEditor.CustomEditors.Dedicated private void OnSplineEdited() { SetSelectedTangentTypeAsCurrent(); - UpdateButtonsColors(); + SetEditButtonsColor(); + SetEditButtonsEnabled(); } /// @@ -351,14 +356,6 @@ namespace FlaxEditor.CustomEditors.Dedicated UpdateSelectedPoint(); UpdateSelectedTangent(); - _linearTangentButton.Button.Enabled = CanEditTangent(); - _freeTangentButton.Button.Enabled = CanSetTangentFree(); - _alignedTangentButton.Button.Enabled = CanSetTangentAligned(); - _smoothInTangentButton.Button.Enabled = CanSetTangentSmoothIn(); - _smoothOutTangentButton.Button.Enabled = CanSetTangentSmoothOut(); - _setLinearAllTangentsButton.Button.Enabled = CanSetAllTangentsLinear(); - _setSmoothAllTangentsButton.Button.Enabled = CanSetAllTangentsSmooth(); - if (!CanEditTangent()) { return; @@ -393,6 +390,52 @@ namespace FlaxEditor.CustomEditors.Dedicated _tanOutChanged = false; } + private void SetSelectedTangentTypeAsCurrent() + { + 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(); + } + + private void SetEditButtonsColor() + { + if (!CanEditTangent()) + { + _linearTangentButton.Button.BackgroundColor = NormalButtonColor; + _freeTangentButton.Button.BackgroundColor = NormalButtonColor; + _alignedTangentButton.Button.BackgroundColor = NormalButtonColor; + _smoothInTangentButton.Button.BackgroundColor = NormalButtonColor; + _smoothOutTangentButton.Button.BackgroundColor = NormalButtonColor; + return; + } + + var isFree = _currentTangentMode is FreeTangentMode; + var isLinear = _currentTangentMode is LinearTangentMode; + var isAligned = _currentTangentMode is AlignedTangentMode; + var isSmoothIn = _currentTangentMode is SmoothInTangentMode; + var isSmoothOut = _currentTangentMode is SmoothOutTangentMode; + + _linearTangentButton.Button.BackgroundColor = isLinear ? SelectedButtonColor : NormalButtonColor; + _freeTangentButton.Button.BackgroundColor = isFree ? SelectedButtonColor : NormalButtonColor; + _alignedTangentButton.Button.BackgroundColor = isAligned ? SelectedButtonColor : NormalButtonColor; + _smoothInTangentButton.Button.BackgroundColor = isSmoothIn ? SelectedButtonColor : NormalButtonColor; + _smoothOutTangentButton.Button.BackgroundColor = isSmoothOut ? SelectedButtonColor : NormalButtonColor; + } + + private void SetEditButtonsEnabled() + { + _linearTangentButton.Button.Enabled = CanEditTangent(); + _freeTangentButton.Button.Enabled = CanSetTangentFree(); + _alignedTangentButton.Button.Enabled = CanSetTangentAligned(); + _smoothInTangentButton.Button.Enabled = CanSetTangentSmoothIn(); + _smoothOutTangentButton.Button.Enabled = CanSetTangentSmoothOut(); + _setLinearAllTangentsButton.Button.Enabled = CanSetAllTangentsLinear(); + _setSmoothAllTangentsButton.Button.Enabled = CanSetAllTangentsSmooth(); + } + private bool CanEditTangent() { return !HasDifferentTypes && !HasDifferentValues && (HasPointSelected || HasTangentsSelected); @@ -470,10 +513,12 @@ namespace FlaxEditor.CustomEditors.Dedicated private void UpdateSelectedPoint() { // works only if select one spline - if (Editor.Instance.SceneEditing.SelectionCount != 1) + if (!_selectedSpline) { _selectedPoint = null; - UpdateButtonsColors(); + SetSelectedTangentTypeAsCurrent(); + SetEditButtonsColor(); + SetEditButtonsEnabled(); return; } @@ -484,19 +529,16 @@ namespace FlaxEditor.CustomEditors.Dedicated { _selectedPoint = currentSelected as SplineNode.SplinePointNode; _lastPointSelected = _selectedPoint; - - var index = _lastPointSelected.Index; - - SetSelectedTangentTypeAsCurrent(); - UpdateButtonsColors(); - - _currentTangentMode.OnSelectKeyframe(_selectedSpline, index); + _currentTangentMode.OnSelectKeyframe(_selectedSpline, _lastPointSelected.Index); } else { _selectedPoint = null; - UpdateButtonsColors(); } + + SetSelectedTangentTypeAsCurrent(); + SetEditButtonsColor(); + SetEditButtonsEnabled(); } private void UpdateSelectedTangent() @@ -544,41 +586,6 @@ namespace FlaxEditor.CustomEditors.Dedicated _selectedTangentOut = null; } - private void UpdateButtonsColors() - { - if (!CanEditTangent()) - { - _linearTangentButton.Button.BackgroundColor = NormalButtonColor; - _freeTangentButton.Button.BackgroundColor = NormalButtonColor; - _alignedTangentButton.Button.BackgroundColor = NormalButtonColor; - _smoothInTangentButton.Button.BackgroundColor = NormalButtonColor; - _smoothOutTangentButton.Button.BackgroundColor = NormalButtonColor; - return; - } - - var isFree = _currentTangentMode is FreeTangentMode; - var isLinear = _currentTangentMode is LinearTangentMode; - var isAligned = _currentTangentMode is AlignedTangentMode; - var isSmoothIn = _currentTangentMode is SmoothInTangentMode; - var isSmoothOut = _currentTangentMode is SmoothOutTangentMode; - - _linearTangentButton.Button.BackgroundColor = isLinear ? SelectedButtonColor : NormalButtonColor; - _freeTangentButton.Button.BackgroundColor = isFree ? SelectedButtonColor : NormalButtonColor; - _alignedTangentButton.Button.BackgroundColor = isAligned ? SelectedButtonColor : NormalButtonColor; - _smoothInTangentButton.Button.BackgroundColor = isSmoothIn ? SelectedButtonColor : NormalButtonColor; - _smoothOutTangentButton.Button.BackgroundColor = isSmoothOut ? SelectedButtonColor : NormalButtonColor; - } - - private void SetSelectedTangentTypeAsCurrent() - { - 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(); - } - private void StartEditSpline() { var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled; From ea48a89e005e888fddaa3479a55719545bf93750 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 24 Jul 2023 22:34:48 -0400 Subject: [PATCH 28/93] improve auto smooth --- .../CustomEditors/Dedicated/SplineEditor.cs | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 2f9eaf0d0..a844cdf05 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -75,7 +75,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// public override void OnSetMode(Spline spline, int index) { - if (IsLinearTangentMode(spline, index)) + if (IsLinearTangentMode(spline, index) || IsSmoothInTangentMode(spline, index) || IsSmoothOutTangentMode(spline, index)) { SetPointSmooth(spline, index); } @@ -428,8 +428,8 @@ namespace FlaxEditor.CustomEditors.Dedicated private void SetEditButtonsEnabled() { _linearTangentButton.Button.Enabled = CanEditTangent(); - _freeTangentButton.Button.Enabled = CanSetTangentFree(); - _alignedTangentButton.Button.Enabled = CanSetTangentAligned(); + _freeTangentButton.Button.Enabled = CanEditTangent(); + _alignedTangentButton.Button.Enabled = CanEditTangent(); _smoothInTangentButton.Button.Enabled = CanSetTangentSmoothIn(); _smoothOutTangentButton.Button.Enabled = CanSetTangentSmoothOut(); _setLinearAllTangentsButton.Button.Enabled = CanSetAllTangentsLinear(); @@ -453,18 +453,6 @@ namespace FlaxEditor.CustomEditors.Dedicated return _lastPointSelected.Index < _selectedSpline.SplinePointsCount - 1; } - private bool CanSetTangentFree() - { - if (!CanEditTangent()) return false; - return _lastPointSelected.Index < _selectedSpline.SplinePointsCount - 1 && _lastPointSelected.Index != 0; - } - - private bool CanSetTangentAligned() - { - if (!CanEditTangent()) return false; - return _lastPointSelected.Index < _selectedSpline.SplinePointsCount - 1 && _lastPointSelected.Index != 0; - } - private bool CanSetAllTangentsSmooth() { return _selectedSpline != null; @@ -759,17 +747,23 @@ namespace FlaxEditor.CustomEditors.Dedicated var isFirstKeyframe = index <= 0; // force smooth it's linear point - if (tangentInSize == 0f && !isFirstKeyframe) tangentInSize = 100; - if (tangentOutSize == 0f && !isLastKeyframe) tangentOutSize = 100; + if (tangentInSize == 0f) tangentInSize = 100; + if (tangentOutSize == 0f) tangentOutSize = 100; + // try get next / last keyframe var nextKeyframe = !isLastKeyframe ? spline.GetSplineKeyframe(index + 1) : keyframe; var previousKeyframe = !isFirstKeyframe ? spline.GetSplineKeyframe(index - 1) : keyframe; // calc form from Spline.cpp -> SetTangentsSmooth - var slop = (keyframe.Value.Translation - previousKeyframe.Value.Translation + nextKeyframe.Value.Translation - keyframe.Value.Translation).Normalized; + // get tangent direction + var tangentDirection = (keyframe.Value.Translation - previousKeyframe.Value.Translation + nextKeyframe.Value.Translation - keyframe.Value.Translation).Normalized; + + keyframe.TangentIn.Translation = -tangentDirection; + keyframe.TangentOut.Translation = tangentDirection; + + keyframe.TangentIn.Translation *= tangentInSize; + keyframe.TangentOut.Translation *= tangentOutSize; - keyframe.TangentIn.Translation = -slop * tangentInSize; - keyframe.TangentOut.Translation = slop * tangentOutSize; spline.SetSplineKeyframe(index, keyframe); spline.UpdateSpline(); } From 714d0c2ca00f30b62256cd673f6ac7198db44d40 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 24 Jul 2023 22:41:45 -0400 Subject: [PATCH 29/93] small fix --- Source/Editor/CustomEditors/Dedicated/SplineEditor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index a844cdf05..8aa7b421a 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -343,7 +343,6 @@ namespace FlaxEditor.CustomEditors.Dedicated private void OnSplineEdited() { - SetSelectedTangentTypeAsCurrent(); SetEditButtonsColor(); SetEditButtonsEnabled(); } From 131060efa7c1030ae2010acfcd7b4eedb5fc3a90 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Fri, 28 Jul 2023 12:48:43 +0200 Subject: [PATCH 30/93] bug fix --- Source/Editor/Content/ContentFilter.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Content/ContentFilter.cs b/Source/Editor/Content/ContentFilter.cs index 2cbd78e65..12cca5c41 100644 --- a/Source/Editor/Content/ContentFilter.cs +++ b/Source/Editor/Content/ContentFilter.cs @@ -195,7 +195,7 @@ namespace FlaxEditor.Content { engine.Visible = false; engine.Folder.Visible = false; - + foreach (var item in plugins) { item.Visible = false; @@ -218,7 +218,6 @@ namespace FlaxEditor.Content { engine.Visible = true; engine.Folder.Visible = true; - Editor.Instance.Windows.ContentWin.RefreshView(engine); } if (dropdown.Selection[i] == 1)//Show Plugin Content @@ -227,7 +226,6 @@ namespace FlaxEditor.Content { item.Visible = true; item.Folder.Visible = true; - Editor.Instance.Windows.ContentWin.RefreshView(item); } } @@ -244,19 +242,16 @@ namespace FlaxEditor.Content gameSettings.Visible = true; settings.Visible = true; settings.Folder.Visible = true; - Editor.Instance.Windows.ContentWin.RefreshView((ContentTreeNode)settings.Parent); } if (dropdown.Selection[i] == 4)//"Show Shader Source" { shaders.Visible = true; shaders.Folder.Visible = true; - Editor.Instance.Windows.ContentWin.RefreshView(shaders); } } - - - + engine.ParentTree.PerformLayout(); + Editor.Instance.Windows.ContentWin.RefreshView(); } } } From b5eadbc5314699c651a3fdb5e66b9f4c6e93ef79 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 31 Jul 2023 09:06:39 -0400 Subject: [PATCH 31/93] test --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 172 ++++++++++++++---- 1 file changed, 138 insertions(+), 34 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 3ba1d14c3..940b51757 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -12,6 +12,8 @@ using FlaxEditor.Modules; using FlaxEngine; using FlaxEngine.Json; using Object = FlaxEngine.Object; +using System.Collections.Generic; +using System.Security.Cryptography; namespace FlaxEditor.SceneGraph.Actors { @@ -21,16 +23,27 @@ namespace FlaxEditor.SceneGraph.Actors [HideInEditor] public sealed class SplineNode : ActorNode { - public sealed class SplinePointNode : ActorChildNode + public unsafe sealed class SplinePointNode : ActorChildNode { + private Guid* splineNodeId; + + private Spline _spline; + private SplineNode _splineNode; + private SplinePointTangentNode _tangentInNode; + private SplinePointTangentNode _tangentOutNode; + public unsafe SplinePointNode(SplineNode node, Guid id, int index) : base(node, id, index) { - var g = (JsonSerializer.GuidInterop*)&id; - g->D++; - AddChild(new SplinePointTangentNode(node, id, index, true)); - g->D++; - AddChild(new SplinePointTangentNode(node, id, index, false)); + splineNodeId = &id; + _splineNode = node; + _spline = (Spline)_splineNode.Actor; + _spline.SplineUpdated += AddTangentIn; + _spline.SplineUpdated += AddTangentOut; + + + void AddTangentIn() => AddTangentNode(true, splineNodeId); + void AddTangentOut() => AddTangentNode(false, splineNodeId); } public override bool CanBeSelectedDirectly => true; @@ -89,6 +102,54 @@ namespace FlaxEditor.SceneGraph.Actors actor.RemoveSplinePoint(Index); } + public override void OnDispose() + { + base.OnDispose(); + } + + private unsafe void AddTangentNode(bool isIn, Guid* id) + { + if (_tangentInNode != null && _tangentOutNode != null) + { + return; + } + + var g = (JsonSerializer.GuidInterop*)&id; + + if (isIn && _tangentInNode == null) + { + g->D++; + _tangentInNode = new SplinePointTangentNode(_splineNode, *id, Index, true); + AddChild(_tangentInNode); + } + + if (!isIn && _tangentOutNode == null) + { + + g->D++; + _tangentOutNode = new SplinePointTangentNode(_splineNode, *id, Index, false); + AddChild(_tangentOutNode); + } + } + + //private unsafe void RemoveTangentNodes() + //{ + // var id = _splineNode.ID; + // var g = (JsonSerializer.GuidInterop*)&id; + + // if (_tangentInNode != null) + // { + // _tangentInNode.Delete(); + // g->D--; + // } + + // if (_tangentOutNode != null) + // { + // g->D--; + // _tangentOutNode.Delete(); + // } + //} + class DuplicateUndoAction : IUndoAction, ISceneEditAction { public Guid SplineId; @@ -225,12 +286,15 @@ namespace FlaxEditor.SceneGraph.Actors private int _index; private bool _isIn; + + public SplinePointTangentNode(SplineNode node, Guid id, int index, bool isIn) : base(id, isIn ? 0 : 1) { _node = node; _index = index; _isIn = isIn; + } public override Transform Transform @@ -279,44 +343,84 @@ namespace FlaxEditor.SceneGraph.Actors } } + private List _pointNodes; + /// public SplineNode(Actor actor) : base(actor) { - OnUpdate(); + _pointNodes = new List(); FlaxEngine.Scripting.Update += OnUpdate; + + + var spline = (Spline)Actor; + spline.SplineUpdated += UpdateNodesCount; + + UpdateNodesCount(); + } + + private unsafe void UpdateNodesCount() + { + var spline = (Spline)Actor; + var id = ID; + var g = (JsonSerializer.GuidInterop*)&id; + var splinePointsCount = spline.SplinePointsCount; + var splineNodePointsCount = _pointNodes.Count; + g->D += (uint)splineNodePointsCount; + while (splineNodePointsCount < splinePointsCount) + { + g->D++; + var newPoint = new SplinePointNode(this, id, splineNodePointsCount); + AddChildNode(newPoint); + _pointNodes.Add(newPoint); + splineNodePointsCount++; + } + + while (splineNodePointsCount > splinePointsCount) + { + splineNodePointsCount--; + var lastPoint = _pointNodes[splineNodePointsCount]; + _pointNodes.Remove(lastPoint); + lastPoint.Dispose(); + } } private unsafe void OnUpdate() { - // Sync spline points with gizmo handles - var actor = (Spline)Actor; - var dstCount = actor.SplinePointsCount; - if (dstCount > 1 && actor.IsLoop) - dstCount--; // The last point is the same as the first one for loop mode - var srcCount = ActorChildNodes?.Count ?? 0; - if (dstCount != srcCount) - { - // Remove unused points - while (srcCount > dstCount) - { - var node = ActorChildNodes[srcCount-- - 1]; - // TODO: support selection interface inside SceneGraph nodes (eg. on Root) so prefab editor can handle this too - if (Editor.Instance.SceneEditing.Selection.Contains(node)) - Editor.Instance.SceneEditing.Deselect(); - node.Dispose(); - } + //for (int i = 0; i < splineNodePointsCount; i++) + //{ + // _pointNodes[i].Transform = new Transform(spline.GetSplinePoint(i)); + //} + + // Sync spline points with gizmo handles + //var actor = (Spline)Actor; + //var dstCount = actor.SplinePointsCount; + //if (dstCount > 1 && actor.IsLoop) + // dstCount--; // The last point is the same as the first one for loop mode + //var srcCount = ActorChildNodes?.Count ?? 0; + //if (dstCount != srcCount) + //{ + // // Remove unused points + // while (srcCount > dstCount) + // { + // var node = ActorChildNodes[srcCount-- - 1]; + // // TODO: support selection interface inside SceneGraph nodes (eg. on Root) so prefab editor can handle this too + // if (Editor.Instance.SceneEditing.Selection.Contains(node)) + // Editor.Instance.SceneEditing.Deselect(); + // node.Dispose(); + // } + + // // Add new points + // var id = ID; + // var g = (JsonSerializer.GuidInterop*)&id; + // g->D += (uint)srcCount * 3; + // while (srcCount < dstCount) + // { + // g->D += 3; + // AddChildNode(new SplinePointNode(this, id, srcCount++)); + // } + //} - // Add new points - var id = ID; - var g = (JsonSerializer.GuidInterop*)&id; - g->D += (uint)srcCount * 3; - while (srcCount < dstCount) - { - g->D += 3; - AddChildNode(new SplinePointNode(this, id, srcCount++)); - } - } } /// From 84bed5b1c9a87137f3873eef9fc2153c362e7251 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 31 Jul 2023 14:02:03 -0400 Subject: [PATCH 32/93] Revert "test" This reverts commit b5eadbc5314699c651a3fdb5e66b9f4c6e93ef79. --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 170 ++++-------------- 1 file changed, 33 insertions(+), 137 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 940b51757..3ba1d14c3 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -12,8 +12,6 @@ using FlaxEditor.Modules; using FlaxEngine; using FlaxEngine.Json; using Object = FlaxEngine.Object; -using System.Collections.Generic; -using System.Security.Cryptography; namespace FlaxEditor.SceneGraph.Actors { @@ -23,27 +21,16 @@ namespace FlaxEditor.SceneGraph.Actors [HideInEditor] public sealed class SplineNode : ActorNode { - public unsafe sealed class SplinePointNode : ActorChildNode + public sealed class SplinePointNode : ActorChildNode { - private Guid* splineNodeId; - - private Spline _spline; - private SplineNode _splineNode; - private SplinePointTangentNode _tangentInNode; - private SplinePointTangentNode _tangentOutNode; - public unsafe SplinePointNode(SplineNode node, Guid id, int index) : base(node, id, index) { - splineNodeId = &id; - _splineNode = node; - _spline = (Spline)_splineNode.Actor; - _spline.SplineUpdated += AddTangentIn; - _spline.SplineUpdated += AddTangentOut; - - - void AddTangentIn() => AddTangentNode(true, splineNodeId); - void AddTangentOut() => AddTangentNode(false, splineNodeId); + var g = (JsonSerializer.GuidInterop*)&id; + g->D++; + AddChild(new SplinePointTangentNode(node, id, index, true)); + g->D++; + AddChild(new SplinePointTangentNode(node, id, index, false)); } public override bool CanBeSelectedDirectly => true; @@ -102,54 +89,6 @@ namespace FlaxEditor.SceneGraph.Actors actor.RemoveSplinePoint(Index); } - public override void OnDispose() - { - base.OnDispose(); - } - - private unsafe void AddTangentNode(bool isIn, Guid* id) - { - if (_tangentInNode != null && _tangentOutNode != null) - { - return; - } - - var g = (JsonSerializer.GuidInterop*)&id; - - if (isIn && _tangentInNode == null) - { - g->D++; - _tangentInNode = new SplinePointTangentNode(_splineNode, *id, Index, true); - AddChild(_tangentInNode); - } - - if (!isIn && _tangentOutNode == null) - { - - g->D++; - _tangentOutNode = new SplinePointTangentNode(_splineNode, *id, Index, false); - AddChild(_tangentOutNode); - } - } - - //private unsafe void RemoveTangentNodes() - //{ - // var id = _splineNode.ID; - // var g = (JsonSerializer.GuidInterop*)&id; - - // if (_tangentInNode != null) - // { - // _tangentInNode.Delete(); - // g->D--; - // } - - // if (_tangentOutNode != null) - // { - // g->D--; - // _tangentOutNode.Delete(); - // } - //} - class DuplicateUndoAction : IUndoAction, ISceneEditAction { public Guid SplineId; @@ -286,15 +225,12 @@ namespace FlaxEditor.SceneGraph.Actors private int _index; private bool _isIn; - - public SplinePointTangentNode(SplineNode node, Guid id, int index, bool isIn) : base(id, isIn ? 0 : 1) { _node = node; _index = index; _isIn = isIn; - } public override Transform Transform @@ -343,84 +279,44 @@ namespace FlaxEditor.SceneGraph.Actors } } - private List _pointNodes; - /// public SplineNode(Actor actor) : base(actor) { - _pointNodes = new List(); + OnUpdate(); FlaxEngine.Scripting.Update += OnUpdate; - - - var spline = (Spline)Actor; - spline.SplineUpdated += UpdateNodesCount; - - UpdateNodesCount(); - } - - private unsafe void UpdateNodesCount() - { - var spline = (Spline)Actor; - var id = ID; - var g = (JsonSerializer.GuidInterop*)&id; - var splinePointsCount = spline.SplinePointsCount; - var splineNodePointsCount = _pointNodes.Count; - g->D += (uint)splineNodePointsCount; - while (splineNodePointsCount < splinePointsCount) - { - g->D++; - var newPoint = new SplinePointNode(this, id, splineNodePointsCount); - AddChildNode(newPoint); - _pointNodes.Add(newPoint); - splineNodePointsCount++; - } - - while (splineNodePointsCount > splinePointsCount) - { - splineNodePointsCount--; - var lastPoint = _pointNodes[splineNodePointsCount]; - _pointNodes.Remove(lastPoint); - lastPoint.Dispose(); - } } private unsafe void OnUpdate() { - //for (int i = 0; i < splineNodePointsCount; i++) - //{ - // _pointNodes[i].Transform = new Transform(spline.GetSplinePoint(i)); - //} - // Sync spline points with gizmo handles - //var actor = (Spline)Actor; - //var dstCount = actor.SplinePointsCount; - //if (dstCount > 1 && actor.IsLoop) - // dstCount--; // The last point is the same as the first one for loop mode - //var srcCount = ActorChildNodes?.Count ?? 0; - //if (dstCount != srcCount) - //{ - // // Remove unused points - // while (srcCount > dstCount) - // { - // var node = ActorChildNodes[srcCount-- - 1]; - // // TODO: support selection interface inside SceneGraph nodes (eg. on Root) so prefab editor can handle this too - // if (Editor.Instance.SceneEditing.Selection.Contains(node)) - // Editor.Instance.SceneEditing.Deselect(); - // node.Dispose(); - // } - - // // Add new points - // var id = ID; - // var g = (JsonSerializer.GuidInterop*)&id; - // g->D += (uint)srcCount * 3; - // while (srcCount < dstCount) - // { - // g->D += 3; - // AddChildNode(new SplinePointNode(this, id, srcCount++)); - // } - //} + var actor = (Spline)Actor; + var dstCount = actor.SplinePointsCount; + if (dstCount > 1 && actor.IsLoop) + dstCount--; // The last point is the same as the first one for loop mode + var srcCount = ActorChildNodes?.Count ?? 0; + if (dstCount != srcCount) + { + // Remove unused points + while (srcCount > dstCount) + { + var node = ActorChildNodes[srcCount-- - 1]; + // TODO: support selection interface inside SceneGraph nodes (eg. on Root) so prefab editor can handle this too + if (Editor.Instance.SceneEditing.Selection.Contains(node)) + Editor.Instance.SceneEditing.Deselect(); + node.Dispose(); + } + // Add new points + var id = ID; + var g = (JsonSerializer.GuidInterop*)&id; + g->D += (uint)srcCount * 3; + while (srcCount < dstCount) + { + g->D += 3; + AddChildNode(new SplinePointNode(this, id, srcCount++)); + } + } } /// From b80682c97e42f94a4f9080f63f80bd7df8a5fa3d Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 31 Jul 2023 19:21:26 -0400 Subject: [PATCH 33/93] set spline points / tangents size by camera distance --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 3ba1d14c3..75b86950c 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -12,6 +12,7 @@ using FlaxEditor.Modules; using FlaxEngine; using FlaxEngine.Json; using Object = FlaxEngine.Object; +using FlaxEditor.Viewport.Cameras; namespace FlaxEditor.SceneGraph.Actors { @@ -167,10 +168,11 @@ namespace FlaxEditor.SceneGraph.Actors public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal) { + normal = -ray.Ray.Direction; var actor = (Spline)_node.Actor; var pos = actor.GetSplinePoint(Index); - normal = -ray.Ray.Direction; - return new BoundingSphere(pos, 7.0f).Intersects(ref ray.Ray, out distance); + var nodeSize = NodeSizeByDistance(Transform.Translation, PointNodeSize); + return new BoundingSphere(pos, nodeSize).Intersects(ref ray.Ray, out distance); } public override void OnDebugDraw(ViewportDebugDrawData data) @@ -179,23 +181,26 @@ namespace FlaxEditor.SceneGraph.Actors var pos = actor.GetSplinePoint(Index); var tangentIn = actor.GetSplineTangent(Index, true).Translation; var tangentOut = actor.GetSplineTangent(Index, false).Translation; + var pointSize = NodeSizeByDistance(pos, PointNodeSize); + var tangentInSize = NodeSizeByDistance(tangentIn, TangentNodeSize); + var tangentOutSize = NodeSizeByDistance(tangentOut, TangentNodeSize); // Draw spline path ParentNode.OnDebugDraw(data); // Draw selected point highlight - DebugDraw.DrawSphere(new BoundingSphere(pos, 5.0f), Color.Yellow, 0, false); + DebugDraw.DrawSphere(new BoundingSphere(pos, pointSize), Color.Yellow, 0, false); // Draw tangent points if (tangentIn != pos) { DebugDraw.DrawLine(pos, tangentIn, Color.Blue.AlphaMultiplied(0.6f), 0, false); - DebugDraw.DrawWireSphere(new BoundingSphere(tangentIn, 4.0f), Color.Blue, 0, false); + DebugDraw.DrawWireSphere(new BoundingSphere(tangentIn, tangentInSize), Color.Blue, 0, false); } if (tangentOut != pos) { DebugDraw.DrawLine(pos, tangentOut, Color.Red.AlphaMultiplied(0.6f), 0, false); - DebugDraw.DrawWireSphere(new BoundingSphere(tangentOut, 4.0f), Color.Red, 0, false); + DebugDraw.DrawWireSphere(new BoundingSphere(tangentOut, tangentOutSize), Color.Red, 0, false); } } @@ -249,10 +254,11 @@ namespace FlaxEditor.SceneGraph.Actors public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal) { + normal = -ray.Ray.Direction; var actor = (Spline)_node.Actor; var pos = actor.GetSplineTangent(_index, _isIn).Translation; - normal = -ray.Ray.Direction; - return new BoundingSphere(pos, 7.0f).Intersects(ref ray.Ray, out distance); + var tangentSize = NodeSizeByDistance(Transform.Translation, TangentNodeSize); + return new BoundingSphere(pos, tangentSize).Intersects(ref ray.Ray, out distance); } public override void OnDebugDraw(ViewportDebugDrawData data) @@ -263,7 +269,8 @@ namespace FlaxEditor.SceneGraph.Actors // Draw selected tangent highlight var actor = (Spline)_node.Actor; var pos = actor.GetSplineTangent(_index, _isIn).Translation; - DebugDraw.DrawSphere(new BoundingSphere(pos, 5.0f), Color.YellowGreen, 0, false); + var tangentSize = NodeSizeByDistance(Transform.Translation, TangentNodeSize); + DebugDraw.DrawSphere(new BoundingSphere(pos, tangentSize), Color.YellowGreen, 0, false); } public override void OnContextMenu(ContextMenu contextMenu) @@ -279,6 +286,9 @@ namespace FlaxEditor.SceneGraph.Actors } } + private const float PointNodeSize = 2; + private const float TangentNodeSize = 1.6f; + /// public SplineNode(Actor actor) : base(actor) @@ -398,6 +408,14 @@ namespace FlaxEditor.SceneGraph.Actors } } + private static float NodeSizeByDistance(Vector3 nodePosition, float nodeSize) + { + var cameraTransform = Editor.Instance.Windows.EditWin.Viewport.ViewportCamera.Viewport.ViewTransform; + var distance = Vector3.Distance(cameraTransform.Translation, nodePosition) / 100; + return distance * nodeSize; + } + + /// public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal) { From 2b7b80ad38906ac277dd6dc32ae9a06720e3b9d0 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:05:13 -0400 Subject: [PATCH 34/93] makes more easy select a spline point or spline tangent --- Source/Editor/CustomEditors/Dedicated/SplineEditor.cs | 11 +++++++---- Source/Editor/SceneGraph/Actors/SplineNode.cs | 7 +++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 8aa7b421a..d4eb2f1e0 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -706,9 +706,10 @@ namespace FlaxEditor.CustomEditors.Dedicated // auto smooth tangent if's linear if (keyframe.TangentIn.Translation.Length == 0) { + var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplineTangent(index, false).Translation, 10f); var previousKeyframe = spline.GetSplineKeyframe(index - 1); var tangentDirection = keyframe.Value.WorldToLocalVector(previousKeyframe.Value.Translation - keyframe.Value.Translation); - tangentDirection = tangentDirection.Normalized * 100f; + tangentDirection = tangentDirection.Normalized * smoothRange; keyframe.TangentIn.Translation = tangentDirection; } @@ -724,9 +725,10 @@ namespace FlaxEditor.CustomEditors.Dedicated // auto smooth tangent if's linear if (keyframe.TangentOut.Translation.Length == 0) { + var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplineTangent(index, false).Translation, 10f); var nextKeyframe = spline.GetSplineKeyframe(index + 1); var tangentDirection = keyframe.Value.WorldToLocalVector(nextKeyframe.Value.Translation - keyframe.Value.Translation); - tangentDirection = tangentDirection.Normalized * 100f; + tangentDirection = tangentDirection.Normalized * smoothRange; keyframe.TangentOut.Translation = tangentDirection; } @@ -744,10 +746,11 @@ namespace FlaxEditor.CustomEditors.Dedicated var isLastKeyframe = index >= spline.SplinePointsCount - 1; var isFirstKeyframe = index <= 0; + var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplinePoint(index), 10f); // force smooth it's linear point - if (tangentInSize == 0f) tangentInSize = 100; - if (tangentOutSize == 0f) tangentOutSize = 100; + if (tangentInSize == 0f) tangentInSize = smoothRange; + if (tangentOutSize == 0f) tangentOutSize = smoothRange; // try get next / last keyframe var nextKeyframe = !isLastKeyframe ? spline.GetSplineKeyframe(index + 1) : keyframe; diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 75b86950c..fe96ad4e8 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -286,8 +286,8 @@ namespace FlaxEditor.SceneGraph.Actors } } - private const float PointNodeSize = 2; - private const float TangentNodeSize = 1.6f; + private const float PointNodeSize = 1.5f; + private const float TangentNodeSize = 1.0f; /// public SplineNode(Actor actor) @@ -408,14 +408,13 @@ namespace FlaxEditor.SceneGraph.Actors } } - private static float NodeSizeByDistance(Vector3 nodePosition, float nodeSize) + internal static float NodeSizeByDistance(Vector3 nodePosition, float nodeSize) { var cameraTransform = Editor.Instance.Windows.EditWin.Viewport.ViewportCamera.Viewport.ViewTransform; var distance = Vector3.Distance(cameraTransform.Translation, nodePosition) / 100; return distance * nodeSize; } - /// public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal) { From 600659e4536e3cc6932f52358f93ce0e9ec8b16f Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:34:50 -0400 Subject: [PATCH 35/93] Move DrawSpline from Spline.cpp to SplineNode.cs --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 29 ++++++++++++ Source/Engine/Level/Actors/Spline.cpp | 47 ------------------- Source/Engine/Level/Actors/Spline.h | 4 -- 3 files changed, 29 insertions(+), 51 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index fe96ad4e8..24ea60f21 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -415,6 +415,35 @@ namespace FlaxEditor.SceneGraph.Actors return distance * nodeSize; } + public override void OnDebugDraw(ViewportDebugDrawData data) + { + DrawSpline((Spline)Actor, Color.White, Actor.Transform, false); + } + + private void DrawSpline(Spline spline, Color color, Transform transform, bool depthTest) + { + var count = spline.SplineKeyframes.Length; + if (count == 0) + return; + var keyframes = spline.SplineKeyframes; + var pointIndex = 0; + var prev = spline.GetSplineKeyframe(0); + + Vector3 prevPos = transform.LocalToWorld(prev.Value.Translation); + DebugDraw.DrawWireSphere(new BoundingSphere(prevPos, 5.0f), color, 0.0f, depthTest); + for (int i = 1; i < count; i++) + { + var next = keyframes[pointIndex]; + Vector3 nextPos = transform.LocalToWorld(next.Value.Translation); + DebugDraw.DrawWireSphere(new BoundingSphere(nextPos, 5.0f), color, 0.0f, depthTest); + var d = (next.Time - prev.Time) / 3.0f; + DebugDraw.DrawBezier(prevPos, prevPos + prev.TangentOut.Translation * d, nextPos + next.TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); + prev = next; + prevPos = nextPos; + pointIndex++; + } + } + /// public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal) { diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index dbd7ef045..2923463b0 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -455,53 +455,6 @@ void Spline::SetKeyframes(MArray* data) #endif -#if USE_EDITOR - -#include "Engine/Debug/DebugDraw.h" - -namespace -{ - void DrawSpline(Spline* spline, const Color& color, const Transform& transform, bool depthTest) - { - const int32 count = spline->Curve.GetKeyframes().Count(); - if (count == 0) - return; - Spline::Keyframe* prev = spline->Curve.GetKeyframes().Get(); - Vector3 prevPos = transform.LocalToWorld(prev->Value.Translation); - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(prevPos, 5.0f), color, 0.0f, depthTest); - for (int32 i = 1; i < count; i++) - { - Spline::Keyframe* next = prev + 1; - Vector3 nextPos = transform.LocalToWorld(next->Value.Translation); - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(nextPos, 5.0f), color, 0.0f, depthTest); - const float d = (next->Time - prev->Time) / 3.0f; - DEBUG_DRAW_BEZIER(prevPos, prevPos + prev->TangentOut.Translation * d, nextPos + next->TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); - prev = next; - prevPos = nextPos; - } - } -} - -void Spline::OnDebugDraw() -{ - const Color color = GetSplineColor(); - DrawSpline(this, color.AlphaMultiplied(0.7f), _transform, true); - - // Base - Actor::OnDebugDraw(); -} - -void Spline::OnDebugDrawSelected() -{ - const Color color = GetSplineColor(); - DrawSpline(this, color.AlphaMultiplied(0.3f), _transform, false); - - // Base - Actor::OnDebugDrawSelected(); -} - -#endif - void Spline::OnTransformChanged() { // Base diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index ea3cf8569..426304fb9 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -369,10 +369,6 @@ private: public: // [Actor] -#if USE_EDITOR - void OnDebugDraw() override; - void OnDebugDrawSelected() override; -#endif void OnTransformChanged() override; void Initialize() override; void Serialize(SerializeStream& stream, const void* otherObj) override; From f511259a33be31ee45c9bac00823bacb971c54df Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:43:33 -0400 Subject: [PATCH 36/93] draw spline point size by distance from camera --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 24ea60f21..cbc8911a8 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -417,7 +417,7 @@ namespace FlaxEditor.SceneGraph.Actors public override void OnDebugDraw(ViewportDebugDrawData data) { - DrawSpline((Spline)Actor, Color.White, Actor.Transform, false); + DrawSpline((Spline)Actor, Color.White, Actor.Transform, true); } private void DrawSpline(Spline spline, Color color, Transform transform, bool depthTest) @@ -428,15 +428,16 @@ namespace FlaxEditor.SceneGraph.Actors var keyframes = spline.SplineKeyframes; var pointIndex = 0; var prev = spline.GetSplineKeyframe(0); - - Vector3 prevPos = transform.LocalToWorld(prev.Value.Translation); - DebugDraw.DrawWireSphere(new BoundingSphere(prevPos, 5.0f), color, 0.0f, depthTest); - for (int i = 1; i < count; i++) + var prevPos = transform.LocalToWorld(prev.Value.Translation); + var pointSize = NodeSizeByDistance(spline.GetSplinePoint(0), PointNodeSize); + DebugDraw.DrawWireSphere(new BoundingSphere(prevPos, pointSize), color, 0.0f, depthTest); + for (int i = 0; i < count; i++) { var next = keyframes[pointIndex]; - Vector3 nextPos = transform.LocalToWorld(next.Value.Translation); - DebugDraw.DrawWireSphere(new BoundingSphere(nextPos, 5.0f), color, 0.0f, depthTest); + var nextPos = transform.LocalToWorld(next.Value.Translation); var d = (next.Time - prev.Time) / 3.0f; + pointSize = NodeSizeByDistance(spline.GetSplinePoint(i), PointNodeSize); + DebugDraw.DrawWireSphere(new BoundingSphere(nextPos, pointSize), color, 0.0f, depthTest); DebugDraw.DrawBezier(prevPos, prevPos + prev.TangentOut.Translation * d, nextPos + next.TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); prev = next; prevPos = nextPos; From 38492ffe70102d93341ef9f41ed7a35b131c3e88 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Sat, 5 Aug 2023 11:01:15 -0400 Subject: [PATCH 37/93] small adjust --- .../CustomEditors/Dedicated/SplineEditor.cs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index d4eb2f1e0..2fe601108 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -474,6 +474,7 @@ namespace FlaxEditor.CustomEditors.Dedicated if (_currentTangentMode is FreeTangentMode) return; _currentTangentMode = new FreeTangentMode(); _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); + SetEditButtonsColor(); } private void SetModeAligned() @@ -500,23 +501,21 @@ namespace FlaxEditor.CustomEditors.Dedicated private void UpdateSelectedPoint() { // works only if select one spline - if (!_selectedSpline) + if (_selectedSpline) { - _selectedPoint = null; - SetSelectedTangentTypeAsCurrent(); - SetEditButtonsColor(); - SetEditButtonsEnabled(); - return; - } + var currentSelected = Editor.Instance.SceneEditing.Selection[0]; - var currentSelected = Editor.Instance.SceneEditing.Selection[0]; - - if (currentSelected == _selectedPoint) return; - if (currentSelected is SplineNode.SplinePointNode) - { - _selectedPoint = currentSelected as SplineNode.SplinePointNode; - _lastPointSelected = _selectedPoint; - _currentTangentMode.OnSelectKeyframe(_selectedSpline, _lastPointSelected.Index); + if (currentSelected == _selectedPoint) return; + if (currentSelected is SplineNode.SplinePointNode) + { + _selectedPoint = currentSelected as SplineNode.SplinePointNode; + _lastPointSelected = _selectedPoint; + _currentTangentMode.OnSelectKeyframe(_selectedSpline, _lastPointSelected.Index); + } + else + { + _selectedPoint = null; + } } else { From 6b1cc5bf2fec3829c031be2dd64550205259c8ef Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Sat, 5 Aug 2023 12:07:40 -0400 Subject: [PATCH 38/93] adjust again... --- Source/Engine/Level/Actors/Spline.cpp | 47 +++++++++++++++++++++++++++ Source/Engine/Level/Actors/Spline.h | 4 +++ 2 files changed, 51 insertions(+) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 2923463b0..dbd7ef045 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -455,6 +455,53 @@ void Spline::SetKeyframes(MArray* data) #endif +#if USE_EDITOR + +#include "Engine/Debug/DebugDraw.h" + +namespace +{ + void DrawSpline(Spline* spline, const Color& color, const Transform& transform, bool depthTest) + { + const int32 count = spline->Curve.GetKeyframes().Count(); + if (count == 0) + return; + Spline::Keyframe* prev = spline->Curve.GetKeyframes().Get(); + Vector3 prevPos = transform.LocalToWorld(prev->Value.Translation); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(prevPos, 5.0f), color, 0.0f, depthTest); + for (int32 i = 1; i < count; i++) + { + Spline::Keyframe* next = prev + 1; + Vector3 nextPos = transform.LocalToWorld(next->Value.Translation); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(nextPos, 5.0f), color, 0.0f, depthTest); + const float d = (next->Time - prev->Time) / 3.0f; + DEBUG_DRAW_BEZIER(prevPos, prevPos + prev->TangentOut.Translation * d, nextPos + next->TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); + prev = next; + prevPos = nextPos; + } + } +} + +void Spline::OnDebugDraw() +{ + const Color color = GetSplineColor(); + DrawSpline(this, color.AlphaMultiplied(0.7f), _transform, true); + + // Base + Actor::OnDebugDraw(); +} + +void Spline::OnDebugDrawSelected() +{ + const Color color = GetSplineColor(); + DrawSpline(this, color.AlphaMultiplied(0.3f), _transform, false); + + // Base + Actor::OnDebugDrawSelected(); +} + +#endif + void Spline::OnTransformChanged() { // Base diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index 426304fb9..ea3cf8569 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -369,6 +369,10 @@ private: public: // [Actor] +#if USE_EDITOR + void OnDebugDraw() override; + void OnDebugDrawSelected() override; +#endif void OnTransformChanged() override; void Initialize() override; void Serialize(SerializeStream& stream, const void* otherObj) override; From dc3e71a262e3cc832b45d3f766b4512d6fa9c193 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Sat, 5 Aug 2023 21:26:27 -0400 Subject: [PATCH 39/93] fix for large worlds --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index cbc8911a8..9af736fac 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -286,8 +286,8 @@ namespace FlaxEditor.SceneGraph.Actors } } - private const float PointNodeSize = 1.5f; - private const float TangentNodeSize = 1.0f; + private const Real PointNodeSize = 1.5f; + private const Real TangentNodeSize = 1.0f; /// public SplineNode(Actor actor) @@ -408,7 +408,7 @@ namespace FlaxEditor.SceneGraph.Actors } } - internal static float NodeSizeByDistance(Vector3 nodePosition, float nodeSize) + internal static Real NodeSizeByDistance(Vector3 nodePosition, Real nodeSize) { var cameraTransform = Editor.Instance.Windows.EditWin.Viewport.ViewportCamera.Viewport.ViewTransform; var distance = Vector3.Distance(cameraTransform.Translation, nodePosition) / 100; From 8e1cac7d2fcd798d9f32f4fa90e3e709fe0d63f5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 15 Aug 2023 21:34:07 -0500 Subject: [PATCH 40/93] Add to be able to choose sloppy mesh simplification or not in model import settings for LODs. --- Source/Engine/Tools/ModelTool/ModelTool.cpp | 10 +++++++++- Source/Engine/Tools/ModelTool/ModelTool.h | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index f2dfd6927..371a64476 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -383,6 +383,8 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj SERIALIZE(BaseLOD); SERIALIZE(LODCount); SERIALIZE(TriangleReduction); + SERIALIZE(Sloppy); + SERIALIZE(LODTargetError); SERIALIZE(ImportMaterials); SERIALIZE(ImportTextures); SERIALIZE(RestoreMaterialsOnReimport); @@ -424,6 +426,8 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi DESERIALIZE(BaseLOD); DESERIALIZE(LODCount); DESERIALIZE(TriangleReduction); + DESERIALIZE(Sloppy); + DESERIALIZE(LODTargetError); DESERIALIZE(ImportMaterials); DESERIALIZE(ImportTextures); DESERIALIZE(RestoreMaterialsOnReimport); @@ -1528,7 +1532,11 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op int32 dstMeshIndexCountTarget = int32(srcMeshIndexCount * triangleReduction) / 3 * 3; Array indices; indices.Resize(dstMeshIndexCountTarget); - int32 dstMeshIndexCount = (int32)meshopt_simplifySloppy(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget); + int32 dstMeshIndexCount = {}; + if (options.Sloppy) + dstMeshIndexCount = (int32)meshopt_simplifySloppy(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget); + else + dstMeshIndexCount = (int32)meshopt_simplify(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget, options.LODTargetError); indices.Resize(dstMeshIndexCount); if (dstMeshIndexCount == 0) continue; diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index 985bd3ccc..19a8d9031 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -322,6 +322,12 @@ public: // The target amount of triangles for the generated LOD (based on the higher LOD). Normalized to range 0-1. For instance 0.4 cuts the triangle count to 40%. API_FIELD(Attributes="EditorOrder(1130), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGeometry)), Limit(0, 1, 0.001f)") float TriangleReduction = 0.5f; + // Whether to do a sloppy mesh optimization. This is faster and does not follow the topology of the original mesh. + API_FIELD(Attributes="EditorOrder(1140), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGeometry))") + bool Sloppy = true; + // Only used if Sloppy is false. Target error is an approximate measure of the deviation from the original mesh using distance normalized to [0..1] range (e.g. 1e-2f means that simplifier will try to maintain the error to be below 1% of the mesh extents). + API_FIELD(Attributes="EditorOrder(1150), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGeometry)), Limit(0.01f, 1, 0.001f)") + float LODTargetError = 0.1f; public: // Materials From fb69bc64f8dc0c85c244f1742e87558b579bb2ac Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 24 Aug 2023 11:38:14 -0500 Subject: [PATCH 41/93] Add automated plugin project creation and Git cloning. --- Source/Editor/Windows/PluginsWindow.cs | 556 ++++++++++++++++++++++++- 1 file changed, 554 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index 694551345..451e58afa 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -2,11 +2,19 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; using FlaxEditor.GUI; +using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Tabs; using FlaxEngine; using FlaxEngine.GUI; +using FlaxEngine.Json; +using Debug = FlaxEngine.Debug; namespace FlaxEditor.Windows { @@ -17,6 +25,8 @@ namespace FlaxEditor.Windows public sealed class PluginsWindow : EditorWindow { private Tabs _tabs; + private Button _addPluginProjectButton; + private Button _cloneProjectButton; private readonly List _categories = new List(); private readonly Dictionary _entries = new Dictionary(); @@ -174,19 +184,557 @@ namespace FlaxEditor.Windows { Title = "Plugins"; + var vp = new Panel + { + AnchorPreset = AnchorPresets.StretchAll, + Parent = this, + }; + _addPluginProjectButton = new Button + { + Text = "Create Plugin Project", + TooltipText = "Add new plugin project.", + AnchorPreset = AnchorPresets.TopLeft, + LocalLocation = new Float2(70,18), + Size = new Float2(150, 25), + Parent = vp, + }; + _addPluginProjectButton.Clicked += OnAddButtonClicked; + + _cloneProjectButton = new Button + { + Text = "Clone Plugin Project", + TooltipText = "Git Clone a plugin project.", + AnchorPreset = AnchorPresets.TopLeft, + LocalLocation = new Float2(70 + _addPluginProjectButton.Size.X + 8, 18), + Size = new Float2(150, 25), + Parent = vp, + }; + _cloneProjectButton.Clicked += OnCloneProjectButtonClicked; + _tabs = new Tabs { Orientation = Orientation.Vertical, AnchorPreset = AnchorPresets.StretchAll, - Offsets = Margin.Zero, + Offsets = new Margin(0, 0, _addPluginProjectButton.Bottom + 8, 0), TabsSize = new Float2(120, 32), - Parent = this + Parent = vp }; OnPluginsChanged(); PluginManager.PluginsChanged += OnPluginsChanged; } + private void OnCloneProjectButtonClicked() + { + var popup = new ContextMenuBase + { + Size = new Float2(300, 125), + ClipChildren = false, + CullChildren = false, + }; + popup.Show(_cloneProjectButton, new Float2(_cloneProjectButton.Width, 0)); + + var nameLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + AutoWidth = true, + Text = "Name", + HorizontalAlignment = TextAlignment.Near, + }; + nameLabel.LocalY += 10; + + var nameTextBox = new TextBox + { + Parent = popup, + WatermarkText = "Plugin Name", + TooltipText = "If left blank, this will take the git name.", + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + nameTextBox.LocalX += (300 - (10)) * 0.5f; + nameTextBox.LocalY += 10; + nameLabel.LocalX += (300 - (nameLabel.Width + nameTextBox.Width)) * 0.5f + 10; + + var defaultTextBoxBorderColor = nameTextBox.BorderColor; + var defaultTextBoxBorderSelectedColor = nameTextBox.BorderSelectedColor; + nameTextBox.TextChanged += () => + { + if (string.IsNullOrEmpty(nameTextBox.Text)) + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + return; + } + + var pluginPath = Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text); + if (Directory.Exists(pluginPath)) + { + nameTextBox.BorderColor = Color.Red; + nameTextBox.BorderSelectedColor = Color.Red; + } + else + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + } + }; + + var gitPathLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + AutoWidth = true, + Text = "Git Path", + HorizontalAlignment = TextAlignment.Near, + }; + gitPathLabel.LocalX += (300 - gitPathLabel.Width) * 0.5f; + gitPathLabel.LocalY += 35; + + var gitPathTextBox = new TextBox + { + Parent = popup, + WatermarkText = "https://github.com/FlaxEngine/ExamplePlugin.git", + AnchorPreset = AnchorPresets.TopLeft, + Size = new Float2(280, TextBox.DefaultHeight), + IsMultiline = false, + }; + gitPathTextBox.LocalY += 60; + gitPathTextBox.LocalX += 10; + + var submitButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Clone", + Width = 70, + }; + submitButton.LocalX += 300 * 0.5f - submitButton.Width - 10; + submitButton.LocalY += 90; + + submitButton.Clicked += () => + { + if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text)) && !string.IsNullOrEmpty(nameTextBox.Text)) + { + Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Cannot create plugin due to name conflict."); + return; + } + OnCloneButtonClicked(nameTextBox.Text, gitPathTextBox.Text); + nameTextBox.Clear(); + gitPathTextBox.Clear(); + popup.Hide(); + }; + + var cancelButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Cancel", + Width = 70, + }; + cancelButton.LocalX += 300 * 0.5f + 10; + cancelButton.LocalY += 90; + + cancelButton.Clicked += () => + { + nameTextBox.Clear(); + gitPathTextBox.Clear(); + popup.Hide(); + }; + } + + private async void OnCloneButtonClicked(string pluginName, string gitPath) + { + if (string.IsNullOrEmpty(gitPath)) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to create plugin project due to no GIT path."); + return; + } + if (string.IsNullOrEmpty(pluginName)) + { + var split = gitPath.Split('/'); + if (string.IsNullOrEmpty(split[^1])) + { + var name = split[^2].Replace(".git", ""); + pluginName = name; + } + else + { + var name = split[^1].Replace(".git", ""); + pluginName = name; + } + } + + var clonePath = Path.Combine(Globals.ProjectFolder, "Plugins", pluginName); + if (!Directory.Exists(clonePath)) + Directory.CreateDirectory(clonePath); + else + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Plugin Name is already used. Pick a different Name."); + return; + } + try + { + // Start git clone + var gitProcess = new Process(); + gitProcess.StartInfo.FileName = "git"; + gitProcess.StartInfo.Arguments = $"clone {gitPath} \"{clonePath}\""; + gitProcess.StartInfo.UseShellExecute = false; + gitProcess.StartInfo.RedirectStandardOutput = true; + gitProcess.StartInfo.RedirectStandardError = true; + + gitProcess.Start(); + await gitProcess.WaitForExitAsync(); + + string output = await gitProcess.StandardOutput.ReadToEndAsync(); + string error = await gitProcess.StandardError.ReadToEndAsync(); + Debug.Logger.LogHandler.LogWrite(LogType.Info, $"{output}"); + Debug.Logger.LogHandler.LogWrite(LogType.Info, $"{error}"); + } + catch (Exception e) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed Gir process. {e}"); + return; + } + + Debug.Logger.LogHandler.LogWrite(LogType.Info, $"Plugin project has been cloned."); + + // Find project config file. Could be different then what the user named the folder. + var files = Directory.GetFiles(clonePath); + string pluginProjectName = ""; + foreach (var file in files) + { + if (file.Contains(".flaxproj", StringComparison.OrdinalIgnoreCase)) + { + pluginProjectName = Path.GetFileNameWithoutExtension(file); + Debug.Log(pluginProjectName); + } + } + + if (string.IsNullOrEmpty(pluginProjectName)) + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to find plugin project file to add to Project config. Please add manually."); + else + { + await AddReferenceToProject(pluginName, pluginProjectName); + MessageBox.Show($"{pluginName} has been successfully cloned. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); + } + } + + private void OnAddButtonClicked() + { + var popup = new ContextMenuBase + { + Size = new Float2(230, 125), + ClipChildren = false, + CullChildren = false, + }; + popup.Show(_addPluginProjectButton, new Float2(_addPluginProjectButton.Width, 0)); + + var nameLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Name", + HorizontalAlignment = TextAlignment.Near, + }; + nameLabel.LocalX += 10; + nameLabel.LocalY += 10; + + var nameTextBox = new TextBox + { + Parent = popup, + WatermarkText = "Plugin Name", + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + nameTextBox.LocalX += 100; + nameTextBox.LocalY += 10; + var defaultTextBoxBorderColor = nameTextBox.BorderColor; + var defaultTextBoxBorderSelectedColor = nameTextBox.BorderSelectedColor; + nameTextBox.TextChanged += () => + { + if (string.IsNullOrEmpty(nameTextBox.Text)) + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + return; + } + + var pluginPath = Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text); + if (Directory.Exists(pluginPath)) + { + nameTextBox.BorderColor = Color.Red; + nameTextBox.BorderSelectedColor = Color.Red; + } + else + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + } + }; + + var versionLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Version", + HorizontalAlignment = TextAlignment.Near, + }; + versionLabel.LocalX += 10; + versionLabel.LocalY += 35; + + var versionTextBox = new TextBox + { + Parent = popup, + WatermarkText = "1.0.0", + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + versionTextBox.LocalY += 35; + versionTextBox.LocalX += 100; + + var companyLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Company", + HorizontalAlignment = TextAlignment.Near, + }; + companyLabel.LocalX += 10; + companyLabel.LocalY += 60; + + var companyTextBox = new TextBox + { + Parent = popup, + WatermarkText = "Company Name", + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + companyTextBox.LocalY += 60; + companyTextBox.LocalX += 100; + + var submitButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Create", + Width = 70, + }; + submitButton.LocalX += 40; + submitButton.LocalY += 90; + + submitButton.Clicked += () => + { + if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text))) + { + Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Cannot create plugin due to name conflict."); + return; + } + OnCreateButtonClicked(nameTextBox.Text, versionTextBox.Text, companyTextBox.Text); + nameTextBox.Clear(); + versionTextBox.Clear(); + companyTextBox.Clear(); + popup.Hide(); + }; + + var cancelButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Cancel", + Width = 70, + }; + cancelButton.LocalX += 120; + cancelButton.LocalY += 90; + + cancelButton.Clicked += () => + { + nameTextBox.Clear(); + versionTextBox.Clear(); + companyTextBox.Clear(); + popup.Hide(); + }; + } + + private async void OnCreateButtonClicked(string pluginName, string pluginVersion, string companyName) + { + if (string.IsNullOrEmpty(pluginName)) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to create plugin project due to no plugin name."); + return; + } + + var templateUrl = "https://github.com/FlaxEngine/ExamplePlugin/archive/refs/heads/master.zip"; + var localTemplateFolderLocation = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache"); + if (!Directory.Exists(localTemplateFolderLocation)) + Directory.CreateDirectory(localTemplateFolderLocation); + var localTemplatePath = localTemplateFolderLocation + @"\TemplatePlugin.zip"; + + try + { + // Download example plugin + using (HttpClient client = new HttpClient()) + { + byte[] zipBytes = await client.GetByteArrayAsync(templateUrl); + await File.WriteAllBytesAsync(!File.Exists(localTemplatePath) ? Path.Combine(localTemplatePath) : Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip", zipBytes); + + Debug.Logger.LogHandler.LogWrite(LogType.Info, "Template for plugin project has downloaded"); + } + } + catch (Exception e) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to download template project. Trying to use local file. {e}"); + if (!File.Exists(localTemplatePath)) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to use local file. Does not exist."); + return; + } + } + + // Check if any changes in new downloaded file + if (File.Exists(Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip")) + { + var localTemplatePath2 = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip"; + bool areDifferent = false; + using (var zip1 = ZipFile.OpenRead(localTemplatePath)) + { + using (var zip2 = ZipFile.OpenRead(localTemplatePath2)) + { + if (zip1.Entries.Count != zip2.Entries.Count) + { + areDifferent = true; + } + + foreach (ZipArchiveEntry entry1 in zip1.Entries) + { + ZipArchiveEntry entry2 = zip2.GetEntry(entry1.FullName); + if (entry2 == null) + { + areDifferent = true; + break; + } + if (entry1.Length != entry2.Length || entry1.CompressedLength != entry2.CompressedLength || entry1.Crc32 != entry2.Crc32) + { + areDifferent = true; + break; + } + } + } + } + if (areDifferent) + { + File.Delete(localTemplatePath); + File.Move(localTemplatePath2, localTemplatePath); + } + else + { + File.Delete(localTemplatePath2); + } + } + + var extractPath = Path.Combine(Globals.ProjectFolder, "Plugins"); + if (!Directory.Exists(extractPath)) + Directory.CreateDirectory(extractPath); + + try + { + await Task.Run(() => ZipFile.ExtractToDirectory(localTemplatePath, extractPath)); + Debug.Logger.LogHandler.LogWrite(LogType.Info, "Template for plugin project successfully moved to project."); + } + catch (IOException e) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to add plugin to project. {e}"); + } + + var oldpluginPath = Path.Combine(extractPath, "ExamplePlugin-master"); + var newPluginPath = Path.Combine(extractPath , pluginName); + Directory.Move(oldpluginPath, newPluginPath); + + var oldFlaxProjFile = Path.Combine(newPluginPath, "ExamplePlugin.flaxproj"); + var newFlaxProjFile = Path.Combine(newPluginPath, $"{pluginName}.flaxproj"); + File.Move(oldFlaxProjFile, newFlaxProjFile); + + var readme = Path.Combine(newPluginPath, "README.md"); + if (File.Exists(readme)) + File.Delete(readme); + var license = Path.Combine(newPluginPath, "LICENSE"); + if (File.Exists(license)) + File.Delete(license); + + // Flax plugin project file + var flaxPluginProjContents = JsonSerializer.Deserialize(await File.ReadAllTextAsync(newFlaxProjFile)); + flaxPluginProjContents.Name = pluginName; + if (!string.IsNullOrEmpty(pluginVersion)) + flaxPluginProjContents.Version = new Version(pluginVersion); + if (!string.IsNullOrEmpty(companyName)) + flaxPluginProjContents.Company = companyName; + flaxPluginProjContents.GameTarget = $"{pluginName}Target"; + flaxPluginProjContents.EditorTarget = $"{pluginName}EditorTarget"; + await File.WriteAllTextAsync(newFlaxProjFile, JsonSerializer.Serialize(flaxPluginProjContents, typeof(ProjectInfo))); + + // Rename source directories + var sourcePath = Path.Combine(newPluginPath, "Source"); + var sourceDirectories = Directory.GetDirectories(sourcePath); + foreach (var directory in sourceDirectories) + { + var files = Directory.GetFiles(directory); + foreach (var file in files) + { + if (file.Contains("MyPlugin.cs") || file.Contains("MyPluginEditor.cs")) + { + File.Delete(file); + continue; + } + + var fileText = await File.ReadAllTextAsync(file); + await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginName)); + var fileName = Path.GetFileName(file).Replace("ExamplePlugin", pluginName); + File.Move(file, Path.Combine(directory, fileName)); + } + + var newName = directory.Replace("ExamplePlugin", pluginName); + Directory.Move(directory, newName); + } + + // Rename targets + var targetFiles = Directory.GetFiles(sourcePath); + foreach (var file in targetFiles) + { + var fileText = await File.ReadAllTextAsync(file); + await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginName)); + var newName = file.Replace("ExamplePlugin", pluginName); + File.Move(file, newName); + } + Debug.Logger.LogHandler.LogWrite(LogType.Info, $"Plugin project {pluginName} has successfully been created."); + + await AddReferenceToProject(pluginName, pluginName); + MessageBox.Show($"{pluginName} has been successfully created. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); + } + + private async Task AddReferenceToProject(string pluginFolderName, string pluginName) + { + // Project flax config file + var flaxProjPath = Editor.GameProject.ProjectPath; + if (File.Exists(flaxProjPath)) + { + var flaxProjContents = JsonSerializer.Deserialize(await File.ReadAllTextAsync(flaxProjPath)); + var oldReferences = flaxProjContents.References; + var references = new List(oldReferences); + var newPath = $"$(ProjectPath)/Plugins/{pluginFolderName}/{pluginName}.flaxproj"; + if (!references.Exists(x => string.Equals(x.Name, newPath))) + { + var newReference = new ProjectInfo.Reference + { + Name = newPath, + }; + references.Add(newReference); + } + flaxProjContents.References = references.ToArray(); + await File.WriteAllTextAsync(flaxProjPath, JsonSerializer.Serialize(flaxProjContents, typeof(ProjectInfo))); + } + } + + private void OnPluginsChanged() { List toRemove = null; @@ -312,6 +860,10 @@ namespace FlaxEditor.Windows /// public override void OnDestroy() { + if (_addPluginProjectButton != null) + _addPluginProjectButton.Clicked -= OnAddButtonClicked; + if (_cloneProjectButton != null) + _cloneProjectButton.Clicked -= OnCloneProjectButtonClicked; PluginManager.PluginsChanged -= OnPluginsChanged; base.OnDestroy(); From 51664fc236dfc748185dab7ab0ac6323defb1ede Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 24 Aug 2023 13:51:08 -0500 Subject: [PATCH 42/93] Use pltform process. --- Source/Editor/Windows/PluginsWindow.cs | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index 451e58afa..deac0d9ce 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; @@ -376,20 +375,12 @@ namespace FlaxEditor.Windows try { // Start git clone - var gitProcess = new Process(); - gitProcess.StartInfo.FileName = "git"; - gitProcess.StartInfo.Arguments = $"clone {gitPath} \"{clonePath}\""; - gitProcess.StartInfo.UseShellExecute = false; - gitProcess.StartInfo.RedirectStandardOutput = true; - gitProcess.StartInfo.RedirectStandardError = true; - - gitProcess.Start(); - await gitProcess.WaitForExitAsync(); - - string output = await gitProcess.StandardOutput.ReadToEndAsync(); - string error = await gitProcess.StandardError.ReadToEndAsync(); - Debug.Logger.LogHandler.LogWrite(LogType.Info, $"{output}"); - Debug.Logger.LogHandler.LogWrite(LogType.Info, $"{error}"); + var settings = new CreateProcessSettings(); + settings.FileName = "git"; + settings.Arguments = $"clone {gitPath} \"{clonePath}\""; + settings.ShellExecute = false; + settings.LogOutput = true; + Platform.CreateProcess(ref settings); } catch (Exception e) { From 0bbd619e329a355fff606afa4fff6c1bb1f06832 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 24 Aug 2023 13:54:55 -0500 Subject: [PATCH 43/93] Code cleanup --- Source/Editor/Windows/PluginsWindow.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index deac0d9ce..cf68c6ad8 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -384,7 +384,7 @@ namespace FlaxEditor.Windows } catch (Exception e) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed Gir process. {e}"); + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed Git process. {e}"); return; } @@ -559,7 +559,7 @@ namespace FlaxEditor.Windows var localTemplateFolderLocation = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache"); if (!Directory.Exists(localTemplateFolderLocation)) Directory.CreateDirectory(localTemplateFolderLocation); - var localTemplatePath = localTemplateFolderLocation + @"\TemplatePlugin.zip"; + var localTemplatePath = Path.Combine(localTemplateFolderLocation, "TemplatePlugin.zip"); try { @@ -567,7 +567,7 @@ namespace FlaxEditor.Windows using (HttpClient client = new HttpClient()) { byte[] zipBytes = await client.GetByteArrayAsync(templateUrl); - await File.WriteAllBytesAsync(!File.Exists(localTemplatePath) ? Path.Combine(localTemplatePath) : Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip", zipBytes); + await File.WriteAllBytesAsync(!File.Exists(localTemplatePath) ? Path.Combine(localTemplatePath) : Path.Combine(Editor.LocalCachePath, "TemplatePluginCache" , "TemplatePlugin1.zip"), zipBytes); Debug.Logger.LogHandler.LogWrite(LogType.Info, "Template for plugin project has downloaded"); } @@ -583,9 +583,9 @@ namespace FlaxEditor.Windows } // Check if any changes in new downloaded file - if (File.Exists(Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip")) + if (File.Exists(Path.Combine(Editor.LocalCachePath, "TemplatePluginCache", "TemplatePlugin1.zip"))) { - var localTemplatePath2 = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip"; + var localTemplatePath2 = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache", "TemplatePlugin1.zip"); bool areDifferent = false; using (var zip1 = ZipFile.OpenRead(localTemplatePath)) { From 7b44075ac89f85e447a088180878b5d7414a5c82 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Fri, 4 Aug 2023 09:59:45 +0300 Subject: [PATCH 44/93] Add recursive methods for layers --- Source/Engine/Level/Actor.cpp | 24 ++++++++++++++++++++++++ Source/Engine/Level/Actor.h | 11 +++++++++++ 2 files changed, 35 insertions(+) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 8185f3b9d..a0aa01cba 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -456,6 +456,19 @@ void Actor::SetLayerName(const StringView& value) LOG(Warning, "Unknown layer name '{0}'", value); } +void Actor::SetLayerNameRecursive(const StringView& value) +{ + for (int32 i = 0; i < 32; i++) + { + if (Level::Layers[i] == value) + { + SetLayerRecursive(i); + return; + } + } + LOG(Warning, "Unknown layer name '{0}'", value); +} + bool Actor::HasTag() const { return Tags.Count() != 0; @@ -500,6 +513,17 @@ void Actor::SetLayer(int32 layerIndex) OnLayerChanged(); } +void Actor::SetLayerRecursive(int32 layerIndex) +{ + layerIndex = Math::Clamp(layerIndex, 0, 31); + for (const auto& child : Children) + child->SetLayerRecursive(layerIndex); + if (layerIndex == _layer) + return; + _layer = layerIndex; + OnLayerChanged(); +} + void Actor::SetName(const StringView& value) { if (_name == value) diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 510bc9eb8..7a75b12f7 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -102,6 +102,12 @@ public: /// The index of the layer. API_PROPERTY() void SetLayer(int32 layerIndex); + /// + /// Sets the layer recursively for all underlying children. + /// + /// The index of the layer. + API_FUNCTION() void SetLayerRecursive(int32 layerIndex); + /// /// Gets the name of the layer. /// @@ -113,6 +119,11 @@ public: /// API_PROPERTY() void SetLayerName(const StringView& value); + /// + /// Sets the name of the layer recursively for all underlying children. + /// + API_FUNCTION() void SetLayerNameRecursive(const StringView& value); + /// /// Determines whether this actor has any tag assigned. /// From ff2f239858a6bc1690d670d9985e44c3ec655890 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 25 Aug 2023 08:17:09 -0500 Subject: [PATCH 45/93] Add system reference. --- Source/Editor/Editor.Build.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Editor.Build.cs b/Source/Editor/Editor.Build.cs index a971dbefa..f497225bb 100644 --- a/Source/Editor/Editor.Build.cs +++ b/Source/Editor/Editor.Build.cs @@ -41,6 +41,7 @@ public class Editor : EditorModule options.ScriptingAPI.SystemReferences.Add("System.Xml.ReaderWriter"); options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions"); options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter"); + options.ScriptingAPI.SystemReferences.Add("System.IO.Compression.ZipFile"); // Enable optimizations for Editor, disable this for debugging the editor if (options.Configuration == TargetConfiguration.Development) From d969a7f8b07636df66d58f9ad5bad6791041310c Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 26 Aug 2023 15:17:45 -0500 Subject: [PATCH 46/93] Add more physics settings. --- .../Physics/PhysX/PhysicsBackendPhysX.cpp | 33 ++++++++++- Source/Engine/Physics/PhysicsSettings.h | 56 +++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index 69dfeaa27..de020a35d 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -889,17 +889,46 @@ void* PhysicsBackend::CreateScene(const PhysicsSettings& settings) sceneDesc.gravity = C2P(settings.DefaultGravity); sceneDesc.flags |= PxSceneFlag::eENABLE_ACTIVE_ACTORS; if (!settings.DisableCCD) - sceneDesc.flags |= PxSceneFlag::eENABLE_CCD; + sceneDesc.flags |= PxSceneFlag::eENABLE_CCD | PxSceneFlag::eENABLE_PCM; sceneDesc.simulationEventCallback = &scenePhysX->EventsCallback; sceneDesc.filterShader = FilterShader; sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity; - sceneDesc.solverType = PxSolverType::ePGS; + + switch (settings.SolverType) + { + case PhysicsSolverType::ProjectedGaussSeidelIterativeSolver: + sceneDesc.solverType = PxSolverType::ePGS; + break; + case PhysicsSolverType::DefaultTemporalGaussSeidelSolver: + sceneDesc.solverType = PxSolverType::eTGS; + break; + default: ; + } + + sceneDesc.solverType = PxSolverType::eTGS; if (sceneDesc.cpuDispatcher == nullptr) { scenePhysX->CpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp(Platform::GetCPUInfo().ProcessorCoreCount - 1, 1, 4)); CHECK_INIT(scenePhysX->CpuDispatcher, "PxDefaultCpuDispatcherCreate failed!"); sceneDesc.cpuDispatcher = scenePhysX->CpuDispatcher; } + + switch (settings.BroadPhaseType) + { + case PhysicsBroadPhaseType::SweepAndPrune: + sceneDesc.broadPhaseType = PxBroadPhaseType::eSAP; + break; + case PhysicsBroadPhaseType::MultiboxPruning: + sceneDesc.broadPhaseType = PxBroadPhaseType::eMBP; + break; + case PhysicsBroadPhaseType::AutomaticBoxPruning: + sceneDesc.broadPhaseType = PxBroadPhaseType::eABP; + break; + case PhysicsBroadPhaseType::ParallelAutomaticBoxPruning: + sceneDesc.broadPhaseType = PxBroadPhaseType::ePABP; + break; + default: ; + } // Create scene scenePhysX->Scene = PhysX->createScene(sceneDesc); diff --git a/Source/Engine/Physics/PhysicsSettings.h b/Source/Engine/Physics/PhysicsSettings.h index 1533665e8..8fa2a7295 100644 --- a/Source/Engine/Physics/PhysicsSettings.h +++ b/Source/Engine/Physics/PhysicsSettings.h @@ -6,6 +6,50 @@ #include "Engine/Core/Math/Vector3.h" #include "Types.h" +/// +/// Broad phase algorithm used in the simulation. +/// +/// +API_ENUM() enum class PhysicsBroadPhaseType +{ + /// + /// Sweep and prune. + /// + SweepAndPrune = 0, + + /// + /// Multi box pruning. + /// + MultiboxPruning = 1, + + /// + /// Automatic box pruning. + /// + AutomaticBoxPruning = 2, + + /// + /// Parallel automatic box pruning. + /// + ParallelAutomaticBoxPruning = 3, +}; + +/// +/// The type of solver used in the simulation. +/// +/// +API_ENUM() enum class PhysicsSolverType +{ + /// + /// Projected Gauss-Seidel iterative solver. + /// + ProjectedGaussSeidelIterativeSolver = 0, + + /// + /// Default Temporal Gauss-Seidel solver. + /// + DefaultTemporalGaussSeidelSolver = 1, +}; + /// /// Physics simulation settings container. /// @@ -43,6 +87,18 @@ public: API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Simulation\")") bool DisableCCD = false; + /// + /// Broad phase algorithm to use in the simulation. + /// + API_FIELD(Attributes="EditorOrder(71), EditorDisplay(\"Simulation\")") + PhysicsBroadPhaseType BroadPhaseType = PhysicsBroadPhaseType::SweepAndPrune; + + /// + /// The solver type to use in the simulation. + /// + API_FIELD(Attributes="EditorOrder(72), EditorDisplay(\"Simulation\")") + PhysicsSolverType SolverType = PhysicsSolverType::ProjectedGaussSeidelIterativeSolver; + /// /// The maximum allowed delta time (in seconds) for the physics simulation step. /// From 44598d474173c5345911bb818de0eec50ea1a1f4 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 26 Aug 2023 17:07:04 -0500 Subject: [PATCH 47/93] Small change in showing target error --- Source/Engine/Tools/ModelTool/ModelTool.cpp | 6 +++--- Source/Engine/Tools/ModelTool/ModelTool.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 371a64476..19f203667 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -383,7 +383,7 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj SERIALIZE(BaseLOD); SERIALIZE(LODCount); SERIALIZE(TriangleReduction); - SERIALIZE(Sloppy); + SERIALIZE(SloppyOptimization); SERIALIZE(LODTargetError); SERIALIZE(ImportMaterials); SERIALIZE(ImportTextures); @@ -426,7 +426,7 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi DESERIALIZE(BaseLOD); DESERIALIZE(LODCount); DESERIALIZE(TriangleReduction); - DESERIALIZE(Sloppy); + DESERIALIZE(SloppyOptimization); DESERIALIZE(LODTargetError); DESERIALIZE(ImportMaterials); DESERIALIZE(ImportTextures); @@ -1533,7 +1533,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op Array indices; indices.Resize(dstMeshIndexCountTarget); int32 dstMeshIndexCount = {}; - if (options.Sloppy) + if (options.SloppyOptimization) dstMeshIndexCount = (int32)meshopt_simplifySloppy(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget); else dstMeshIndexCount = (int32)meshopt_simplify(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget, options.LODTargetError); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index 19a8d9031..9dc5bf067 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -322,11 +322,11 @@ public: // The target amount of triangles for the generated LOD (based on the higher LOD). Normalized to range 0-1. For instance 0.4 cuts the triangle count to 40%. API_FIELD(Attributes="EditorOrder(1130), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGeometry)), Limit(0, 1, 0.001f)") float TriangleReduction = 0.5f; - // Whether to do a sloppy mesh optimization. This is faster and does not follow the topology of the original mesh. + // Whether to do a sloppy mesh optimization. This is faster but does not follow the topology of the original mesh. API_FIELD(Attributes="EditorOrder(1140), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGeometry))") - bool Sloppy = true; + bool SloppyOptimization = true; // Only used if Sloppy is false. Target error is an approximate measure of the deviation from the original mesh using distance normalized to [0..1] range (e.g. 1e-2f means that simplifier will try to maintain the error to be below 1% of the mesh extents). - API_FIELD(Attributes="EditorOrder(1150), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGeometry)), Limit(0.01f, 1, 0.001f)") + API_FIELD(Attributes="EditorOrder(1150), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(SloppyOptimization), true), Limit(0.01f, 1, 0.001f)") float LODTargetError = 0.1f; public: // Materials From 5b5c298a960a7d096af06ef512ec56d406dfa4ce Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 1 Sep 2023 06:35:52 -0500 Subject: [PATCH 48/93] Add remove tag method to actor. --- Source/Engine/Level/Actor.cpp | 5 +++++ Source/Engine/Level/Actor.h | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 8185f3b9d..a9d136b30 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -476,6 +476,11 @@ void Actor::AddTag(const Tag& tag) Tags.AddUnique(tag); } +void Actor::RemoveTag(const Tag& tag) +{ + Tags.Remove(tag); +} + PRAGMA_DISABLE_DEPRECATION_WARNINGS const String& Actor::GetTag() const diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 510bc9eb8..8643dba05 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -136,6 +136,12 @@ public: /// The tag to add. API_FUNCTION() void AddTag(const Tag& tag); + /// + /// Removes a tag to the actor + /// + /// The tag to remove. + API_FUNCTION() void RemoveTag(const Tag& tag); + /// /// Gets the name of the tag. /// [Deprecated in v1.5] From 53861c479580ba36aa956fb282578be3b5acd924 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 1 Sep 2023 14:28:26 -0500 Subject: [PATCH 49/93] Improve content item in tile view. Decrease margin between list view items. --- Source/Editor/Content/GUI/ContentView.cs | 15 ++--- Source/Editor/Content/Items/ContentItem.cs | 71 ++++++++++++++++++---- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 6065ca9f8..40a06c1b6 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -711,7 +711,7 @@ namespace FlaxEditor.Content.GUI protected override void PerformLayoutBeforeChildren() { float width = GetClientArea().Width; - float x = 0, y = 0; + float x = 0, y = 1; float viewScale = _viewScale * 0.97f; switch (ViewType) @@ -722,21 +722,22 @@ namespace FlaxEditor.Content.GUI int itemsToFit = Mathf.FloorToInt(width / defaultItemsWidth) - 1; if (itemsToFit < 1) itemsToFit = 1; - float itemsWidth = width / Mathf.Max(itemsToFit, 1); + int xSpace = 4; + float itemsWidth = width / Mathf.Max(itemsToFit, 1) - xSpace; float itemsHeight = itemsWidth / defaultItemsWidth * (ContentItem.DefaultHeight * viewScale); var flooredItemsWidth = Mathf.Floor(itemsWidth); var flooredItemsHeight = Mathf.Floor(itemsHeight); - x = itemsToFit == 1 ? 0 : itemsWidth / itemsToFit; + x = itemsToFit == 1 ? 1 : itemsWidth / itemsToFit + xSpace; for (int i = 0; i < _children.Count; i++) { var c = _children[i]; c.Bounds = new Rectangle(Mathf.Floor(x), Mathf.Floor(y), flooredItemsWidth, flooredItemsHeight); - x += itemsWidth + itemsWidth / itemsToFit; + x += (itemsWidth + xSpace) + (itemsWidth + xSpace) / itemsToFit; if (x + itemsWidth > width) { - x = itemsToFit == 1 ? 0 : itemsWidth / itemsToFit; - y += itemsHeight + 5; + x = itemsToFit == 1 ? 1 : itemsWidth / itemsToFit + xSpace; + y += itemsHeight + 7; } } if (x > 0) @@ -751,7 +752,7 @@ namespace FlaxEditor.Content.GUI { var c = _children[i]; c.Bounds = new Rectangle(x, y, width, itemsHeight); - y += itemsHeight + 5; + y += itemsHeight + 1; } y += 40.0f; diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 66825fb42..41f35b4c7 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -483,6 +483,30 @@ namespace FlaxEditor.Content else Render2D.FillRectangle(rectangle, Color.Black); } + + /// + /// Draws the item thumbnail. + /// + /// The thumbnail rectangle. + /// /// Whether or not to draw the shadow. Overrides DrawShadow. + public void DrawThumbnail(ref Rectangle rectangle, bool shadow) + { + // Draw shadow + if (shadow) + { + const float thumbnailInShadowSize = 50.0f; + var shadowRect = rectangle.MakeExpanded((DefaultThumbnailSize - thumbnailInShadowSize) * rectangle.Width / DefaultThumbnailSize * 1.3f); + if (!_shadowIcon.IsValid) + _shadowIcon = Editor.Instance.Icons.AssetShadow128; + Render2D.DrawSprite(_shadowIcon, shadowRect); + } + + // Draw thumbnail + if (_thumbnail.IsValid) + Render2D.DrawSprite(_thumbnail, rectangle); + else + Render2D.FillRectangle(rectangle, Color.Black); + } /// /// Gets the amount of references to that item. @@ -655,9 +679,32 @@ namespace FlaxEditor.Content { case ContentViewType.Tiles: { - var thumbnailSize = size.X - 2 * DefaultMarginSize; - thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize); + var thumbnailSize = size.X; + thumbnailRect = new Rectangle(0, 0, thumbnailSize, thumbnailSize); nameAlignment = TextAlignment.Center; + + // Small shadow + var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1); + var color = Color.Black.AlphaMultiplied(0.2f); + Render2D.FillRectangle(shadowRect, color); + + Render2D.FillRectangle(clientRect, style.LightBackground); + Render2D.FillRectangle(TextRectangle, style.TextBoxBackground); + var accentHeight = 2 * view.ViewScale; + var barRect = new Rectangle(0, thumbnailRect.Height - accentHeight, clientRect.Width, accentHeight); + Render2D.FillRectangle(barRect, Color.DimGray); + + DrawThumbnail(ref thumbnailRect, false); + if (isSelected) + { + Render2D.FillRectangle(textRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + Render2D.DrawRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + } + else if (IsMouseOver) + { + Render2D.FillRectangle(textRect, style.BackgroundHighlighted); + Render2D.DrawRectangle(clientRect, style.BackgroundHighlighted); + } break; } case ContentViewType.List: @@ -665,23 +712,21 @@ namespace FlaxEditor.Content var thumbnailSize = size.Y - 2 * DefaultMarginSize; thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize); nameAlignment = TextAlignment.Near; + + if (isSelected) + Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + else if (IsMouseOver) + Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); + + DrawThumbnail(ref thumbnailRect); break; } default: throw new ArgumentOutOfRangeException(); } - - // Draw background - if (isSelected) - Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); - else if (IsMouseOver) - Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); - - // Draw preview - DrawThumbnail(ref thumbnailRect); - + // Draw short name Render2D.PushClip(ref textRect); - Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 0.75f, 0.95f); + Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); Render2D.PopClip(); } From 67909e33c824068df4f4dd3a19aecb908dfefff2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 1 Sep 2023 16:37:14 -0500 Subject: [PATCH 50/93] Add difference for folders. --- Source/Editor/Content/Items/ContentItem.cs | 55 +++++++++++++++------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 41f35b4c7..b27159aa0 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -683,27 +683,46 @@ namespace FlaxEditor.Content thumbnailRect = new Rectangle(0, 0, thumbnailSize, thumbnailSize); nameAlignment = TextAlignment.Center; - // Small shadow - var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1); - var color = Color.Black.AlphaMultiplied(0.2f); - Render2D.FillRectangle(shadowRect, color); - - Render2D.FillRectangle(clientRect, style.LightBackground); - Render2D.FillRectangle(TextRectangle, style.TextBoxBackground); - var accentHeight = 2 * view.ViewScale; - var barRect = new Rectangle(0, thumbnailRect.Height - accentHeight, clientRect.Width, accentHeight); - Render2D.FillRectangle(barRect, Color.DimGray); - - DrawThumbnail(ref thumbnailRect, false); - if (isSelected) + if (this is ContentFolder) { - Render2D.FillRectangle(textRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); - Render2D.DrawRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + // Small shadow + var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1); + var color = Color.Black.AlphaMultiplied(0.2f); + Render2D.FillRectangle(shadowRect, color); + Render2D.FillRectangle(clientRect, style.Background.RGBMultiplied(1.25f)); + + if (isSelected) + Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + else if (IsMouseOver) + Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); + + DrawThumbnail(ref thumbnailRect, false); } - else if (IsMouseOver) + else { - Render2D.FillRectangle(textRect, style.BackgroundHighlighted); - Render2D.DrawRectangle(clientRect, style.BackgroundHighlighted); + // Small shadow + var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1); + var color = Color.Black.AlphaMultiplied(0.2f); + Render2D.FillRectangle(shadowRect, color); + + Render2D.FillRectangle(clientRect, style.Background.RGBMultiplied(1.25f)); + Render2D.FillRectangle(TextRectangle, style.LightBackground); + + var accentHeight = 2 * view.ViewScale; + var barRect = new Rectangle(0, thumbnailRect.Height - accentHeight, clientRect.Width, accentHeight); + Render2D.FillRectangle(barRect, Color.DimGray); + + DrawThumbnail(ref thumbnailRect, false); + if (isSelected) + { + Render2D.FillRectangle(textRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + Render2D.DrawRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + } + else if (IsMouseOver) + { + Render2D.FillRectangle(textRect, style.BackgroundHighlighted); + Render2D.DrawRectangle(clientRect, style.BackgroundHighlighted); + } } break; } From 92a5432d224f105e5f526a47656dbddd82fc3e60 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 2 Sep 2023 13:13:34 -0500 Subject: [PATCH 51/93] Sort plugin projects in content tree. --- Source/Editor/Windows/ContentWindow.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index b1f04c8f5..9d782b13b 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -931,6 +931,7 @@ namespace FlaxEditor.Windows // Add game project on top, plugins in the middle and engine at bottom _root.AddChild(Editor.ContentDatabase.Game); + Editor.ContentDatabase.Projects.Sort(); foreach (var project in Editor.ContentDatabase.Projects) { project.SortChildrenRecursive(); From afec6afe4c9e9d57c903fd1e363db5258f6bc06d Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 3 Sep 2023 14:04:46 +0300 Subject: [PATCH 52/93] Fix managed UTF-8 string allocation not using correct encoding --- Source/Engine/Engine/NativeInterop.Unmanaged.cs | 4 ++-- Source/Engine/Scripting/Runtime/DotNet.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 879f804d2..2c1bab744 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -581,9 +581,9 @@ namespace FlaxEngine.Interop } [UnmanagedCallersOnly] - internal static IntPtr NewStringLength(sbyte* text, int length) + internal static IntPtr NewStringUTF8(sbyte* text, int length) { - return ManagedString.ToNativeWeak(new string(text, 0, length)); + return ManagedString.ToNativeWeak(new string(text, 0, length, System.Text.Encoding.UTF8)); } [UnmanagedCallersOnly] diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index f4ae5b1ec..1bcef50b8 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -365,8 +365,8 @@ MString* MCore::String::GetEmpty(MDomain* domain) MString* MCore::String::New(const char* str, int32 length, MDomain* domain) { - static void* NewStringLengthPtr = GetStaticMethodPointer(TEXT("NewStringLength")); - return (MString*)CallStaticMethod(NewStringLengthPtr, str, length); + static void* NewStringUTF8Ptr = GetStaticMethodPointer(TEXT("NewStringUTF8")); + return (MString*)CallStaticMethod(NewStringUTF8Ptr, str, length); } MString* MCore::String::New(const Char* str, int32 length, MDomain* domain) From 924e1c6d81efca81b3aaf3af2b1f863e349c4ec8 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 3 Sep 2023 06:36:29 -0500 Subject: [PATCH 53/93] Fix right click deselecting scene tree nodes. --- Source/Editor/GUI/Tree/TreeNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index ced70a281..1a1ebe5a1 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -762,7 +762,7 @@ namespace FlaxEditor.GUI.Tree // Add/Remove tree.AddOrRemoveSelection(this); } - else + else if (button != MouseButton.Right) { // Select tree.Select(this); From 39ecfe8c242940126c394ac8bd2c2e042beff4a2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 3 Sep 2023 12:11:33 -0500 Subject: [PATCH 54/93] Small bug fix --- Source/Editor/GUI/Tree/TreeNode.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 1a1ebe5a1..7e3af05bf 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -762,7 +762,11 @@ namespace FlaxEditor.GUI.Tree // Add/Remove tree.AddOrRemoveSelection(this); } - else if (button != MouseButton.Right) + else if (button == MouseButton.Right && tree.Selection.Contains(this)) + { + // Do nothing + } + else { // Select tree.Select(this); From 20f35e332f309a13512b2982627fd7736da9ab02 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 4 Sep 2023 20:15:59 -0500 Subject: [PATCH 55/93] Add slider control --- Source/Engine/UI/GUI/Common/Slider.cs | 347 ++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 Source/Engine/UI/GUI/Common/Slider.cs diff --git a/Source/Engine/UI/GUI/Common/Slider.cs b/Source/Engine/UI/GUI/Common/Slider.cs new file mode 100644 index 000000000..06c73f15e --- /dev/null +++ b/Source/Engine/UI/GUI/Common/Slider.cs @@ -0,0 +1,347 @@ +using System; + +namespace FlaxEngine.GUI; + +/// +/// The slider control. +/// +public class Slider : ContainerControl +{ + /// + /// The minimum value. + /// + protected float _minimum; + + /// + /// The maximum value. + /// + protected float _maximum = 100f; + + /// + /// Gets or sets the minimum value. + /// + [EditorOrder(20), Tooltip("The minimum value.")] + public float Minimum + { + get => _minimum; + set + { + if (value > _maximum) + throw new ArgumentOutOfRangeException(); + if (WholeNumbers) + value = Mathf.RoundToInt(value); + _minimum = value; + if (Value < _minimum) + Value = _minimum; + } + } + + /// + /// Gets or sets the maximum value. + /// + [EditorOrder(30), Tooltip("The maximum value.")] + public float Maximum + { + get => _maximum; + set + { + if (value < _minimum || Mathf.IsZero(value)) + throw new ArgumentOutOfRangeException(); + if (WholeNumbers) + value = Mathf.RoundToInt(value); + _maximum = value; + if (Value > _maximum) + Value = _maximum; + } + } + + private float _value = 100f; + private Rectangle _thumbRect; + private float _thumbCenter; + private Float2 _thumbSize = new Float2(16, 16); + private bool _isSliding; + + /// + /// Gets or sets the value (normalized to range 0-100). + /// + [EditorOrder(10), Tooltip("The current value.")] + public float Value + { + get => _value; + set + { + value = Mathf.Clamp(value, Minimum, Maximum); + if (WholeNumbers) + value = Mathf.RoundToInt(value); + if (!Mathf.NearEqual(value, _value)) + { + _value = value; + + // Update + UpdateThumb(); + ValueChanged?.Invoke(); + } + } + } + + /// + /// The height of the track. + /// + [EditorOrder(40), Tooltip("The track height.")] + public int TrackHeight { get; set; } = 2; + + /// + /// The thumb size. + /// + [EditorOrder(41), Tooltip("The size of the thumb.")] + public Float2 ThumbSize { + get => _thumbSize; + set + { + _thumbSize = value; + UpdateThumb(); + } + } + + /// + /// Whether to fill the track. + /// + [EditorOrder(42), Tooltip("Fill the track.")] + public bool FillTrack = true; + + /// + /// Whether to use whole numbers. + /// + [EditorOrder(43), Tooltip("Use whole numbers.")] + public bool WholeNumbers = false; + + /// + /// The color of the slider track line + /// + [EditorDisplay("Track Style"), EditorOrder(2010), Tooltip("The color of the slider track line."), ExpandGroups] + public Color TrackLineColor { get; set; } + + /// + /// The color of the slider fill track line + /// + [EditorDisplay("Track Style"), EditorOrder(2011), VisibleIf(nameof(FillTrack)), Tooltip("The color of the slider fill track line.")] + public Color TrackFillLineColor { get; set; } + + /// + /// Gets the size of the track. + /// + private float TrackWidth => Width; + + /// + /// Gets or sets the brush used for slider track drawing. + /// + [EditorDisplay("Track Style"), EditorOrder(2012), Tooltip("The brush used for slider track drawing.")] + public IBrush TrackBrush { get; set; } + + /// + /// Gets or sets the brush used for slider fill track drawing. + /// + [EditorDisplay("Track Style"), EditorOrder(2013), VisibleIf(nameof(FillTrack)), Tooltip("The brush used for slider fill track drawing.")] + public IBrush FillTrackBrush { get; set; } + + /// + /// The color of the slider thumb when it's not selected + /// + [EditorDisplay("Thumb Style"), EditorOrder(2030), Tooltip("The color of the slider thumb when it's not selected."), ExpandGroups] + public Color ThumbColor { get; set; } + + /// + /// The color of the slider thumb when it's selected + /// + [EditorDisplay("Thumb Style"), EditorOrder(2031), Tooltip("The color of the slider thumb when it's selected.")] + public Color ThumbColorSelected { get; set; } + + /// + /// Gets or sets the brush used for slider thumb drawing. + /// + [EditorDisplay("Thumb Style"), EditorOrder(2032), Tooltip("The brush of the slider thumb.")] + public IBrush ThumbBrush { get; set; } + + /// + /// Gets a value indicating whether user is using a slider. + /// + [HideInEditor] + public bool IsSliding => _isSliding; + + /// + /// Occurs when sliding starts. + /// + public event Action SlidingStart; + + /// + /// Occurs when sliding ends. + /// + public event Action SlidingEnd; + + /// + /// Occurs when value gets changed. + /// + public event Action ValueChanged; + + /// + /// Initializes a new instance of the class. + /// + public Slider() + : this(120, 30) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The width. + /// The height. + public Slider(float width, float height) + : base(0, 0, width, height) + { + var style = Style.Current; + TrackLineColor = style.BackgroundHighlighted; + TrackFillLineColor = style.LightBackground; + ThumbColor = style.BackgroundNormal; + ThumbColorSelected = style.BackgroundSelected; + UpdateThumb(); + } + + private void UpdateThumb() + { + // Cache data + float trackSize = TrackWidth; + float range = Maximum - Minimum; + float pixelRange = trackSize - _thumbSize.X; + float perc = (_value - Minimum) / range; + float thumbPosition = (int)(perc * pixelRange); + _thumbCenter = thumbPosition + _thumbSize.X / 2; + _thumbRect = new Rectangle(thumbPosition, (Height - _thumbSize.Y) / 2, _thumbSize.X, _thumbSize.Y); + } + + private void EndSliding() + { + _isSliding = false; + EndMouseCapture(); + SlidingEnd?.Invoke(); + } + + /// + public override void Draw() + { + base.Draw(); + + // Draw track line + //var lineRect = new Rectangle(4, (Height - TrackHeight) / 2, Width - 8, TrackHeight); + var lineRect = new Rectangle(_thumbSize.X / 2, (Height - TrackHeight) / 2, Width - _thumbSize.X, TrackHeight); + if (TrackBrush != null) + TrackBrush.Draw(lineRect, TrackLineColor); + else + Render2D.FillRectangle(lineRect, TrackLineColor); + + // Draw track fill + if (FillTrack) + { + var fillLineRect = new Rectangle(_thumbSize.X / 2, (Height - TrackHeight - 2) / 2, Width - (Width - _thumbCenter) - _thumbSize.X / 2, TrackHeight + 2); + Render2D.PushClip(ref fillLineRect); + if (FillTrackBrush != null) + FillTrackBrush.Draw(lineRect, TrackFillLineColor); + else + Render2D.FillRectangle(lineRect, TrackFillLineColor); + Render2D.PopClip(); + } + + // Draw thumb + var thumbColor = _isSliding ? ThumbColorSelected : ThumbColor; + if (ThumbBrush != null) + ThumbBrush.Draw(_thumbRect, thumbColor); + else + Render2D.FillRectangle(_thumbRect, thumbColor); + } + + /// + public override void OnLostFocus() + { + if (_isSliding) + { + EndSliding(); + } + + base.OnLostFocus(); + } + + /// + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (button == MouseButton.Left) + { + Focus(); + float mousePosition = location.X; + + if (_thumbRect.Contains(ref location)) + { + // Start sliding + _isSliding = true; + StartMouseCapture(); + SlidingStart?.Invoke(); + return true; + } + else + { + // Click change + Value += (mousePosition < _thumbCenter ? -1 : 1) * 10; + } + } + + return base.OnMouseDown(location, button); + } + + /// + public override void OnMouseMove(Float2 location) + { + if (_isSliding) + { + // Update sliding + var slidePosition = location + Root.TrackingMouseOffset; + Value = Mathf.Remap(slidePosition.X, 4, TrackWidth - 4, Minimum, Maximum); + } + else + { + base.OnMouseMove(location); + } + } + + /// + public override bool OnMouseUp(Float2 location, MouseButton button) + { + if (button == MouseButton.Left && _isSliding) + { + EndSliding(); + return true; + } + + return base.OnMouseUp(location, button); + } + + /// + public override void OnEndMouseCapture() + { + // Check if was sliding + if (_isSliding) + { + EndSliding(); + } + else + { + base.OnEndMouseCapture(); + } + } + + /// + protected override void OnSizeChanged() + { + base.OnSizeChanged(); + + UpdateThumb(); + } +} From 65a02f032fd686bbeebeddc32752a25c95ed5b37 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 5 Sep 2023 08:25:25 -0500 Subject: [PATCH 56/93] Expose helpful slider data. --- Source/Engine/UI/GUI/Common/Slider.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Source/Engine/UI/GUI/Common/Slider.cs b/Source/Engine/UI/GUI/Common/Slider.cs index 06c73f15e..b0344f055 100644 --- a/Source/Engine/UI/GUI/Common/Slider.cs +++ b/Source/Engine/UI/GUI/Common/Slider.cs @@ -83,6 +83,24 @@ public class Slider : ContainerControl } } } + + /// + /// The local position of the thumb center + /// + [HideInEditor] + public float ThumbCenter => _thumbCenter; + + /// + /// The local position of the beginning of the track. + /// + [HideInEditor] + public float TrackBeginning => _thumbSize.X / 2; + + /// + /// The local position of the end of the track. + /// + [HideInEditor] + public float TrackEnd => Width - _thumbSize.X / 2; /// /// The height of the track. From d96f19e5c66bc8f54bb806c515ce564d6b75260a Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 5 Sep 2023 08:44:59 -0500 Subject: [PATCH 57/93] Change exposed positionns to float 2 --- Source/Engine/UI/GUI/Common/Slider.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Slider.cs b/Source/Engine/UI/GUI/Common/Slider.cs index b0344f055..d7324ae0e 100644 --- a/Source/Engine/UI/GUI/Common/Slider.cs +++ b/Source/Engine/UI/GUI/Common/Slider.cs @@ -88,19 +88,19 @@ public class Slider : ContainerControl /// The local position of the thumb center /// [HideInEditor] - public float ThumbCenter => _thumbCenter; + public Float2 ThumbCenter => new(_thumbCenter, Height / 2); /// /// The local position of the beginning of the track. /// [HideInEditor] - public float TrackBeginning => _thumbSize.X / 2; + public Float2 TrackBeginning => new(_thumbSize.X / 2, Height / 2); /// /// The local position of the end of the track. /// [HideInEditor] - public float TrackEnd => Width - _thumbSize.X / 2; + public Float2 TrackEnd => new(Width - _thumbSize.X / 2, Height / 2); /// /// The height of the track. From d9a1eb349df0a0c5ae8ac7c9b38e0d24bdfdeb53 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Wed, 6 Sep 2023 05:43:27 +0300 Subject: [PATCH 58/93] Implement tags recursive addition --- Source/Engine/Level/Actor.cpp | 7 +++++++ Source/Engine/Level/Actor.h | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index a0aa01cba..388379237 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -489,6 +489,13 @@ void Actor::AddTag(const Tag& tag) Tags.AddUnique(tag); } +void Actor::AddTagRecursive(const Tag& tag) +{ + for (const auto& child : Children) + child->AddTagRecursive(tag); + Tags.AddUnique(tag); +} + PRAGMA_DISABLE_DEPRECATION_WARNINGS const String& Actor::GetTag() const diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 7a75b12f7..31cfe2473 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -120,7 +120,7 @@ public: API_PROPERTY() void SetLayerName(const StringView& value); /// - /// Sets the name of the layer recursively for all underlying children. + /// Sets the name of the layer recursively for actor and for all underlying child actors. /// API_FUNCTION() void SetLayerNameRecursive(const StringView& value); @@ -147,6 +147,12 @@ public: /// The tag to add. API_FUNCTION() void AddTag(const Tag& tag); + /// + /// Adds a tag to the actor and for all underlying child actors. + /// + /// The tag to add. + API_FUNCTION() void AddTagRecursive(const Tag& tag); + /// /// Gets the name of the tag. /// [Deprecated in v1.5] From 5ceafaad77cb70d3ac3ffd3975d2d7a483c46f34 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Wed, 6 Sep 2023 05:23:06 +0300 Subject: [PATCH 59/93] WindowBase::Resized delegate --- Source/Engine/Platform/Base/WindowBase.cpp | 1 + Source/Engine/Platform/Base/WindowBase.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Source/Engine/Platform/Base/WindowBase.cpp b/Source/Engine/Platform/Base/WindowBase.cpp index 5cff76361..49b4cc631 100644 --- a/Source/Engine/Platform/Base/WindowBase.cpp +++ b/Source/Engine/Platform/Base/WindowBase.cpp @@ -405,6 +405,7 @@ void WindowBase::OnResize(int32 width, int32 height) _swapChain->Resize(width, height); if (RenderTask) RenderTask->Resize(width, height); + Resized({ static_cast(width), static_cast(height) }); INVOKE_EVENT_PARAMS_2(OnResize, &width, &height); } diff --git a/Source/Engine/Platform/Base/WindowBase.h b/Source/Engine/Platform/Base/WindowBase.h index e4e7d2080..812359fb4 100644 --- a/Source/Engine/Platform/Base/WindowBase.h +++ b/Source/Engine/Platform/Base/WindowBase.h @@ -315,6 +315,11 @@ public: /// Action Closed; + /// + /// Event fired when window gets resized. + /// + Delegate Resized; + /// /// Event fired when window gets focused. /// From fc7c24f7714bca2781119b3cfdb2bc645f62c5e8 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Thu, 7 Sep 2023 11:33:16 -0400 Subject: [PATCH 60/93] Fix for #1365 --- .../Engine/Physics/Colliders/CharacterController.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 488acd6e4..0d5819422 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -245,12 +245,10 @@ void CharacterController::CreateShape() void CharacterController::UpdateBounds() { - void* actor = _shape ? PhysicsBackend::GetShapeActor(_shape) : nullptr; - if (actor) - PhysicsBackend::GetActorBounds(actor, _box); - else - _box = BoundingBox(_transform.Translation); - BoundingSphere::FromBox(_box, _sphere); + const float radius = Math::Max(_height, _radius); + const Vector3 position = _transform.LocalToWorld(_center); + _sphere = BoundingSphere(position, radius); + _box = BoundingBox::FromSphere(_sphere); } void CharacterController::AddMovement(const Vector3& translation, const Quaternion& rotation) From 317384ed28287473b16b95e6dfb3c35f937992a9 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Fri, 8 Sep 2023 22:19:09 -0400 Subject: [PATCH 61/93] add debugdraw::drawray --- Source/Engine/Debug/DebugDraw.cpp | 5 +++++ Source/Engine/Debug/DebugDraw.h | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 97422b9e2..e8489f5b5 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -920,6 +920,11 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bo } } +void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest) +{ + DrawLine(origin, origin + direction, color, duration, depthTest); +} + void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration, bool depthTest) { const Float3 startF = start - Context->Origin, endF = end - Context->Origin; diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index 8cc5452ed..a4f69678a 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -69,6 +69,16 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// True if draw all debug shapes from scenes too or false if draw just from specified actor list. API_FUNCTION() static void DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes); + /// + /// Draws the line in a direction. + /// + /// The origin of the line. + /// The direction of the line. + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + API_FUNCTION() static void DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration = 0.0f, bool depthTest = true); + /// /// Draws the line. /// @@ -604,6 +614,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color, int32 size = 32, float duration = 0.0f); }; +#define DEBUG_DRAW_RAY(origin, direction, color, duration, depthTest) DebugDraw::DrawRay(origin, direction, color, duration, depthTest) #define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) DebugDraw::DrawLine(start, end, color, duration, depthTest) #define DEBUG_DRAW_LINES(lines, transform, color, duration, depthTest) DebugDraw::DrawLines(lines, transform, color, duration, depthTest) #define DEBUG_DRAW_BEZIER(p1, p2, p3, p4, color, duration, depthTest) DebugDraw::DrawBezier(p1, p2, p3, p4, color, duration, depthTest) From 420e02e3c19f4d109c2a6dbb4e89f333c2c79e99 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Sat, 9 Sep 2023 09:28:06 -0400 Subject: [PATCH 62/93] add empty drawRay --- Source/Engine/Debug/DebugDraw.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Source/Engine/Debug/DebugDraw.cs b/Source/Engine/Debug/DebugDraw.cs index af39ebeea..0fe0d51d2 100644 --- a/Source/Engine/Debug/DebugDraw.cs +++ b/Source/Engine/Debug/DebugDraw.cs @@ -31,6 +31,18 @@ namespace FlaxEngine { } + /// + /// Draws the line in a direction. + /// + /// The start point. + /// The direction of the line. + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + public static void DrawRay(Vector3 origin, Vector3 direction, Color color, float duration = 0.0f, bool depthTest = true) + { + } + /// /// Draws the line. /// From c6ad4d0e19836995a31716563601e954b2717982 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Sat, 9 Sep 2023 13:07:39 -0400 Subject: [PATCH 63/93] small doc fix --- Source/Engine/Debug/DebugDraw.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Debug/DebugDraw.cs b/Source/Engine/Debug/DebugDraw.cs index 0fe0d51d2..bc000e03d 100644 --- a/Source/Engine/Debug/DebugDraw.cs +++ b/Source/Engine/Debug/DebugDraw.cs @@ -34,7 +34,7 @@ namespace FlaxEngine /// /// Draws the line in a direction. /// - /// The start point. + /// The origin of the line. /// The direction of the line. /// The color. /// The duration (in seconds). Use 0 to draw it only once. From 9291295a4d9997e3257037db7135d3a5e52d3962 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 11:25:36 +0200 Subject: [PATCH 64/93] Fix `Dictionary` and `HashSet` iterators to prevent unwanted data copies #1361 --- Source/Engine/Content/Assets/VisualScript.cpp | 6 +-- Source/Engine/Core/Collections/Array.h | 8 ++-- Source/Engine/Core/Collections/ChunkedArray.h | 1 - Source/Engine/Core/Collections/Dictionary.h | 48 +++++++++++-------- Source/Engine/Core/Collections/HashSet.h | 46 +++++++++--------- 5 files changed, 57 insertions(+), 52 deletions(-) diff --git a/Source/Engine/Content/Assets/VisualScript.cpp b/Source/Engine/Content/Assets/VisualScript.cpp index e292a0133..698f1ca8c 100644 --- a/Source/Engine/Content/Assets/VisualScript.cpp +++ b/Source/Engine/Content/Assets/VisualScript.cpp @@ -1251,7 +1251,7 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val boxBase = node->GetBox(3); if (boxBase->HasConnection()) eatBox(node, boxBase->FirstConnection()); - Dictionary::Iterator it(dictionary, iteratorValue.Value.AsInt); + Dictionary::Iterator it(&dictionary, iteratorValue.Value.AsInt); ++it; iteratorValue.Value.AsInt = it.Index(); } @@ -1269,12 +1269,12 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val // Key case 1: if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count()) - value = Dictionary::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Key; + value = Dictionary::Iterator(scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Key; break; // Value case 2: if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count()) - value = Dictionary::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Value; + value = Dictionary::Iterator(scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Value; break; // Break case 5: diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h index 3feae4e73..58117cf0a 100644 --- a/Source/Engine/Core/Collections/Array.h +++ b/Source/Engine/Core/Collections/Array.h @@ -938,12 +938,12 @@ public: FORCE_INLINE bool IsEnd() const { - return _index == _array->Count(); + return _index == _array->_count; } FORCE_INLINE bool IsNotEnd() const { - return _index != _array->Count(); + return _index != _array->_count; } FORCE_INLINE T& operator*() const @@ -975,7 +975,7 @@ public: Iterator& operator++() { - if (_index != _array->Count()) + if (_index != _array->_count) _index++; return *this; } @@ -983,7 +983,7 @@ public: Iterator operator++(int) { Iterator temp = *this; - if (_index != _array->Count()) + if (_index != _array->_count) _index++; return temp; } diff --git a/Source/Engine/Core/Collections/ChunkedArray.h b/Source/Engine/Core/Collections/ChunkedArray.h index 86473763b..d01711e38 100644 --- a/Source/Engine/Core/Collections/ChunkedArray.h +++ b/Source/Engine/Core/Collections/ChunkedArray.h @@ -95,7 +95,6 @@ public: struct Iterator { friend ChunkedArray; - private: ChunkedArray* _collection; int32 _chunkIndex; diff --git a/Source/Engine/Core/Collections/Dictionary.h b/Source/Engine/Core/Collections/Dictionary.h index 2327f3c24..01c25a842 100644 --- a/Source/Engine/Core/Collections/Dictionary.h +++ b/Source/Engine/Core/Collections/Dictionary.h @@ -237,22 +237,28 @@ public: { friend Dictionary; private: - Dictionary& _collection; + Dictionary* _collection; int32 _index; public: - Iterator(Dictionary& collection, const int32 index) + Iterator(Dictionary* collection, const int32 index) : _collection(collection) , _index(index) { } - Iterator(Dictionary const& collection, const int32 index) - : _collection((Dictionary&)collection) + Iterator(Dictionary const* collection, const int32 index) + : _collection(const_cast(collection)) , _index(index) { } + Iterator() + : _collection(nullptr) + , _index(-1) + { + } + Iterator(const Iterator& i) : _collection(i._collection) , _index(i._index) @@ -273,27 +279,27 @@ public: FORCE_INLINE bool IsEnd() const { - return _index == _collection._size; + return _index == _collection->_size; } FORCE_INLINE bool IsNotEnd() const { - return _index != _collection._size; + return _index != _collection->_size; } FORCE_INLINE Bucket& operator*() const { - return _collection._allocation.Get()[_index]; + return _collection->_allocation.Get()[_index]; } FORCE_INLINE Bucket* operator->() const { - return &_collection._allocation.Get()[_index]; + return &_collection->_allocation.Get()[_index]; } FORCE_INLINE explicit operator bool() const { - return _index >= 0 && _index < _collection._size; + return _index >= 0 && _index < _collection->_size; } FORCE_INLINE bool operator!() const @@ -320,10 +326,10 @@ public: Iterator& operator++() { - const int32 capacity = _collection.Capacity(); + const int32 capacity = _collection->_size; if (_index != capacity) { - const Bucket* data = _collection._allocation.Get(); + const Bucket* data = _collection->_allocation.Get(); do { _index++; @@ -343,7 +349,7 @@ public: { if (_index > 0) { - const Bucket* data = _collection._allocation.Get(); + const Bucket* data = _collection->_allocation.Get(); do { _index--; @@ -633,7 +639,7 @@ public: /// Iterator with key and value. void Add(const Iterator& i) { - ASSERT(&i._collection != this && i); + ASSERT(i._collection != this && i); const Bucket& bucket = *i; Add(bucket.Key, bucket.Value); } @@ -667,7 +673,7 @@ public: /// True if cannot remove item from the collection because cannot find it, otherwise false. bool Remove(const Iterator& i) { - ASSERT(&i._collection == this); + ASSERT(i._collection == this); if (i) { ASSERT(_allocation.Get()[i._index].IsOccupied()); @@ -711,7 +717,7 @@ public: return End(); FindPositionResult pos; FindPosition(key, pos); - return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End(); + return pos.ObjectIndex != -1 ? Iterator(this, pos.ObjectIndex) : End(); } /// @@ -812,38 +818,38 @@ public: public: Iterator Begin() const { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } Iterator End() const { - return Iterator(*this, _size); + return Iterator(this, _size); } Iterator begin() { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } FORCE_INLINE Iterator end() { - return Iterator(*this, _size); + return Iterator(this, _size); } const Iterator begin() const { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } FORCE_INLINE const Iterator end() const { - return Iterator(*this, _size); + return Iterator(this, _size); } protected: diff --git a/Source/Engine/Core/Collections/HashSet.h b/Source/Engine/Core/Collections/HashSet.h index ad6f8ffc6..107e42e65 100644 --- a/Source/Engine/Core/Collections/HashSet.h +++ b/Source/Engine/Core/Collections/HashSet.h @@ -213,17 +213,17 @@ public: { friend HashSet; private: - HashSet& _collection; + HashSet* _collection; int32 _index; - Iterator(HashSet& collection, const int32 index) + Iterator(HashSet* collection, const int32 index) : _collection(collection) , _index(index) { } - Iterator(HashSet const& collection, const int32 index) - : _collection((HashSet&)collection) + Iterator(HashSet const* collection, const int32 index) + : _collection(const_cast(collection)) , _index(index) { } @@ -244,27 +244,27 @@ public: public: FORCE_INLINE bool IsEnd() const { - return _index == _collection.Capacity(); + return _index == _collection->_size; } FORCE_INLINE bool IsNotEnd() const { - return _index != _collection.Capacity(); + return _index != _collection->_size; } FORCE_INLINE Bucket& operator*() const { - return _collection._allocation.Get()[_index]; + return _collection->_allocation.Get()[_index]; } FORCE_INLINE Bucket* operator->() const { - return &_collection._allocation.Get()[_index]; + return &_collection->_allocation.Get()[_index]; } FORCE_INLINE explicit operator bool() const { - return _index >= 0 && _index < _collection._size; + return _index >= 0 && _index < _collection->_size; } FORCE_INLINE bool operator !() const @@ -274,12 +274,12 @@ public: FORCE_INLINE bool operator==(const Iterator& v) const { - return _index == v._index && &_collection == &v._collection; + return _index == v._index && _collection == v._collection; } FORCE_INLINE bool operator!=(const Iterator& v) const { - return _index != v._index || &_collection != &v._collection; + return _index != v._index || _collection != v._collection; } Iterator& operator=(const Iterator& v) @@ -291,10 +291,10 @@ public: Iterator& operator++() { - const int32 capacity = _collection.Capacity(); + const int32 capacity = _collection->_size; if (_index != capacity) { - const Bucket* data = _collection._allocation.Get(); + const Bucket* data = _collection->_allocation.Get(); do { _index++; @@ -314,7 +314,7 @@ public: { if (_index > 0) { - const Bucket* data = _collection._allocation.Get(); + const Bucket* data = _collection->_allocation.Get(); do { _index--; @@ -464,7 +464,7 @@ public: /// Iterator with item to add void Add(const Iterator& i) { - ASSERT(&i._collection != this && i); + ASSERT(i._collection != this && i); const Bucket& bucket = *i; Add(bucket.Item); } @@ -498,7 +498,7 @@ public: /// True if cannot remove item from the collection because cannot find it, otherwise false. bool Remove(const Iterator& i) { - ASSERT(&i._collection == this); + ASSERT(i._collection == this); if (i) { ASSERT(_allocation.Get()[i._index].IsOccupied()); @@ -523,7 +523,7 @@ public: return End(); FindPositionResult pos; FindPosition(item, pos); - return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End(); + return pos.ObjectIndex != -1 ? Iterator(this, pos.ObjectIndex) : End(); } /// @@ -559,38 +559,38 @@ public: public: Iterator Begin() const { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } Iterator End() const { - return Iterator(*this, _size); + return Iterator(this, _size); } Iterator begin() { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } FORCE_INLINE Iterator end() { - return Iterator(*this, _size); + return Iterator(this, _size); } const Iterator begin() const { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } FORCE_INLINE const Iterator end() const { - return Iterator(*this, _size); + return Iterator(this, _size); } protected: From 53deeff11601b326b4506a5d6747f98ef1426023 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 11:33:08 +0200 Subject: [PATCH 65/93] Missing change from 9291295a4d9997e3257037db7135d3a5e52d3962 --- Source/Engine/Core/Collections/Dictionary.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Collections/Dictionary.h b/Source/Engine/Core/Collections/Dictionary.h index 01c25a842..f5ffb1bfa 100644 --- a/Source/Engine/Core/Collections/Dictionary.h +++ b/Source/Engine/Core/Collections/Dictionary.h @@ -314,7 +314,7 @@ public: FORCE_INLINE bool operator!=(const Iterator& v) const { - return _index != v._index || &_collection != &v._collection; + return _index != v._index || _collection != v._collection; } Iterator& operator=(const Iterator& v) From 9da686cd525c3ff76fc5b8722b697d26c64688a1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 11:58:54 +0200 Subject: [PATCH 66/93] Improve #1369 for #1365 --- .../Physics/Colliders/CharacterController.cpp | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 0d5819422..42455daa4 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -8,6 +8,8 @@ #include "Engine/Serialization/Serialization.h" #include "Engine/Engine/Time.h" +#define CC_MIN_SIZE 0.001f + CharacterController::CharacterController(const SpawnParams& params) : Collider(params) , _controller(nullptr) @@ -100,10 +102,9 @@ void CharacterController::SetStepOffset(float value) { const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float contactOffset = Math::Max(_contactOffset, ZeroTolerance); - const float minSize = 0.001f; - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); - const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, minSize); - PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - minSize)); + const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); + const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, CC_MIN_SIZE); + PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - CC_MIN_SIZE)); } } @@ -175,9 +176,8 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis void CharacterController::DrawPhysicsDebug(RenderView& view) { const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); + const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE); + const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); const Vector3 position = _transform.LocalToWorld(_center); if (view.Mode == ViewMode::PhysicsColliders) DEBUG_DRAW_TUBE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::LightYellow, 0, true); @@ -188,9 +188,8 @@ void CharacterController::DrawPhysicsDebug(RenderView& view) void CharacterController::OnDebugDrawSelected() { const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); + const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE); + const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); const Vector3 position = _transform.LocalToWorld(_center); DEBUG_DRAW_WIRE_TUBE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow, 0, false); @@ -231,9 +230,8 @@ void CharacterController::UpdateSize() const if (_controller) { const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), minSize); - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); + const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), CC_MIN_SIZE); + const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); PhysicsBackend::SetControllerSize(_controller, radius, height); } } @@ -245,10 +243,14 @@ void CharacterController::CreateShape() void CharacterController::UpdateBounds() { - const float radius = Math::Max(_height, _radius); + const float scaling = GetScale().GetAbsolute().MaxValue(); + const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE); + const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); const Vector3 position = _transform.LocalToWorld(_center); - _sphere = BoundingSphere(position, radius); - _box = BoundingBox::FromSphere(_sphere); + const Vector3 extent(radius, height * 0.5f + radius, radius); + _box = BoundingBox(position - extent, position + extent); + BoundingSphere::FromBox(_box, _sphere); + DEBUG_DRAW_BOX(_box, Color::Red.AlphaMultiplied(0.4f), 2.0f, true); } void CharacterController::AddMovement(const Vector3& translation, const Quaternion& rotation) From a4312590e794b7d1566620ced859dc75cdc90cd0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 13:34:46 +0200 Subject: [PATCH 67/93] Fix debug draw leftover from 9da686cd525c3ff76fc5b8722b697d26c64688a1 #1369 #1365 --- Source/Engine/Physics/Colliders/CharacterController.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 42455daa4..59521e51c 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -250,7 +250,6 @@ void CharacterController::UpdateBounds() const Vector3 extent(radius, height * 0.5f + radius, radius); _box = BoundingBox(position - extent, position + extent); BoundingSphere::FromBox(_box, _sphere); - DEBUG_DRAW_BOX(_box, Color::Red.AlphaMultiplied(0.4f), 2.0f, true); } void CharacterController::AddMovement(const Vector3& translation, const Quaternion& rotation) From ae27d51dc68381eac5162f58990813d68f38c350 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 14:53:58 +0200 Subject: [PATCH 68/93] Fix building for Android with the latest Java version (update to Gradle `8.3`) --- Source/Platforms/Android/Binaries/Project/app/build.gradle | 1 + Source/Platforms/Android/Binaries/Project/build.gradle | 2 +- Source/Platforms/Android/Binaries/Project/gradle.properties | 3 +-- .../Binaries/Project/gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Platforms/Android/Binaries/Project/app/build.gradle b/Source/Platforms/Android/Binaries/Project/app/build.gradle index 0ad5776b5..2f5da0a56 100644 --- a/Source/Platforms/Android/Binaries/Project/app/build.gradle +++ b/Source/Platforms/Android/Binaries/Project/app/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 24 + namespace "${PackageName}" defaultConfig { applicationId "${PackageName}" minSdkVersion 24 diff --git a/Source/Platforms/Android/Binaries/Project/build.gradle b/Source/Platforms/Android/Binaries/Project/build.gradle index 31d9237bc..ff4610757 100644 --- a/Source/Platforms/Android/Binaries/Project/build.gradle +++ b/Source/Platforms/Android/Binaries/Project/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:8.1.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/Source/Platforms/Android/Binaries/Project/gradle.properties b/Source/Platforms/Android/Binaries/Project/gradle.properties index 7be81848a..f38a936bd 100644 --- a/Source/Platforms/Android/Binaries/Project/gradle.properties +++ b/Source/Platforms/Android/Binaries/Project/gradle.properties @@ -9,5 +9,4 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m -org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 diff --git a/Source/Platforms/Android/Binaries/Project/gradle/wrapper/gradle-wrapper.properties b/Source/Platforms/Android/Binaries/Project/gradle/wrapper/gradle-wrapper.properties index 483089527..ee1048a44 100644 --- a/Source/Platforms/Android/Binaries/Project/gradle/wrapper/gradle-wrapper.properties +++ b/Source/Platforms/Android/Binaries/Project/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip From 716e643f2a609cbc829dc8c117bd3ed4d2cf14e0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 21:07:41 +0200 Subject: [PATCH 69/93] Fix applying prefab changes to nesting prefab instances #1256 --- Source/Editor/Modules/SceneModule.cs | 2 +- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 5 ----- Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs | 2 +- Source/Engine/Level/Prefabs/Prefab.Apply.cpp | 7 +++---- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Modules/SceneModule.cs b/Source/Editor/Modules/SceneModule.cs index 0a51038ac..257262585 100644 --- a/Source/Editor/Modules/SceneModule.cs +++ b/Source/Editor/Modules/SceneModule.cs @@ -591,7 +591,7 @@ namespace FlaxEditor.Modules private void OnActorNameChanged(Actor actor) { ActorNode node = GetActorNode(actor); - node?.TreeNode.OnNameChanged(); + node?.TreeNode.UpdateText(); } private void OnActorActiveChanged(Actor actor) diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 83ea666d9..c4edda65e 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -97,11 +97,6 @@ namespace FlaxEditor.SceneGraph.GUI } } - internal void OnNameChanged() - { - UpdateText(); - } - /// /// Updates the tree node text. /// diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 1b1cfb4ff..5a532e362 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -430,7 +430,7 @@ namespace FlaxEditor.Windows.Assets { if (actorNode.Actor) { - actorNode.TreeNode.OnNameChanged(); + actorNode.TreeNode.UpdateText(); actorNode.TreeNode.OnOrderInParentChanged(); } diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp index 5ea8ad0a4..e0df48fae 100644 --- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp @@ -1255,20 +1255,19 @@ void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, ArrayWaitForLoaded()) { LOG(Warning, "Waiting for prefab asset load failed."); continue; } + // Sync only if prefab is used by this prefab (directly) and it has been captured before const int32 nestedPrefabIndex = nestedPrefab->NestedPrefabs.Find(GetID()); if (nestedPrefabIndex != -1) { - if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[nestedPrefabIndex])) + if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[i])) continue; - nestedPrefab->SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData); - ObjectsRemovalService::Flush(); } } From fb27606541f05333d6fb186458cd2946e120a52d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 13:54:44 +0200 Subject: [PATCH 70/93] Add missing `Particle Scale` getter node to Particle Emitter graph #1343 --- Source/Editor/Surface/Archetypes/Particles.cs | 12 ++++++++++++ .../Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp | 6 ++++++ .../Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp | 4 ++++ Source/Engine/Particles/Graph/ParticleEmitterGraph.h | 9 ++++++++- 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Particles.cs b/Source/Editor/Surface/Archetypes/Particles.cs index dd861d4af..1389a6904 100644 --- a/Source/Editor/Surface/Archetypes/Particles.cs +++ b/Source/Editor/Surface/Archetypes/Particles.cs @@ -553,6 +553,18 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0), } }, + new NodeArchetype + { + TypeID = 112, + Title = "Particle Scale", + Description = "Particle scale.", + Flags = NodeFlags.ParticleEmitterGraph, + Size = new Float2(200, 30), + Elements = new[] + { + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float3), 0), + } + }, // Simulation data access nodes new NodeArchetype diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp index 9ef996869..9d91d0013 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp @@ -331,6 +331,12 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node value = GET_PARTICLE_ATTRIBUTE(0, float); break; } + // Particle Scale + case 112: + { + value = GET_PARTICLE_ATTRIBUTE(0, Float3); + break; + } // Effect Position case 200: { diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp index 905d1a11c..de142827c 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp @@ -331,6 +331,10 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va case 111: value = AccessParticleAttribute(node, TEXT("Radius"), ParticleAttribute::ValueTypes::Float, AccessMode::Read); break; + // Particle Scale + case 112: + value = AccessParticleAttribute(node, TEXT("Scale"), ParticleAttribute::ValueTypes::Float3, AccessMode::Read); + break; // Effect Position case 200: value = Value(VariantType::Float3, TEXT("EffectPosition")); diff --git a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h index cfe259a72..f5946c226 100644 --- a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h +++ b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h @@ -269,13 +269,20 @@ public: USE_ATTRIBUTE(Lifetime, Float, 1); break; } - // Particle Mass + // Particle Radius case GRAPH_NODE_MAKE_TYPE(14, 111): { node->UsesParticleData = true; USE_ATTRIBUTE(Radius, Float, 0); break; } + // Particle Scale + case GRAPH_NODE_MAKE_TYPE(14, 112): + { + node->UsesParticleData = true; + USE_ATTRIBUTE(Scale, Float3, 0); + break; + } // Random case GRAPH_NODE_MAKE_TYPE(14, 208): case GRAPH_NODE_MAKE_TYPE(14, 209): From 9afa98e9059dd54525c00b93ee231e57c512e0c3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 13:59:10 +0200 Subject: [PATCH 71/93] Fix `Random Range` nodes in CPU particles/script graphs when using `int` #1341 --- Source/Engine/Visject/VisjectGraph.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index ca4495f67..1cee5d46e 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -1279,16 +1279,16 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value) // Random Float Range case 213: { - auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat; - auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat; + auto a = (float)tryGetValue(node->TryGetBox(1), node->Values[0]); + auto b = (float)tryGetValue(node->TryGetBox(2), node->Values[1]); value = Math::Lerp(a, b, RAND); break; } // Random Vector2 Range case 214: { - auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat2(); - auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat2(); + auto a = (Float2)tryGetValue(node->TryGetBox(1), node->Values[0]); + auto b = (Float2)tryGetValue(node->TryGetBox(2), node->Values[1]); value = Float2( Math::Lerp(a.X, b.X, RAND), Math::Lerp(a.Y, b.Y, RAND) @@ -1298,8 +1298,8 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value) // Random Vector3 Range case 215: { - auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat3(); - auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat3(); + auto a = (Float3)tryGetValue(node->TryGetBox(1), node->Values[0]); + auto b = (Float3)tryGetValue(node->TryGetBox(2), node->Values[1]); value = Float3( Math::Lerp(a.X, b.X, RAND), Math::Lerp(a.Y, b.Y, RAND), @@ -1310,8 +1310,8 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value) // Random Vector4 Range case 216: { - auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat4(); - auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat4(); + auto a = (Float4)tryGetValue(node->TryGetBox(1), node->Values[0]); + auto b = (Float4)tryGetValue(node->TryGetBox(2), node->Values[1]); value = Float4( Math::Lerp(a.X, b.X, RAND), Math::Lerp(a.Y, b.Y, RAND), From 1134cfa6a6964837d6391247a899a95573ce6f91 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 15:05:46 +0200 Subject: [PATCH 72/93] Fix using `ManagedDictionary` when item value type is array #1305 --- Source/Engine/Scripting/Scripting.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index 2b49d97d4..68da64bf7 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -205,6 +205,13 @@ namespace FlaxEngine internal static void AddDictionaryItem(IDictionary dictionary, object key, object value) { + // TODO: more generic approach to properly add value that is of custom boxed type? (eg. via NativeInterop.MarshalToManaged) + if (value is ManagedArray managedArray) + { + var managedArrayHandle = ManagedHandle.Alloc(managedArray, GCHandleType.Normal); + value = NativeInterop.MarshalToManaged((IntPtr)managedArrayHandle, managedArray.ArrayType); + managedArrayHandle.Free(); + } dictionary.Add(key, value); } From 48d4a51c1b589ec2b7560812cecc3af5154422fd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 15:06:26 +0200 Subject: [PATCH 73/93] Fix crash when boxing native array to managed value --- Source/Engine/Scripting/ManagedCLR/MUtils.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.h b/Source/Engine/Scripting/ManagedCLR/MUtils.h index 90feecab3..7b340e5c6 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.h +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.h @@ -3,6 +3,7 @@ #pragma once #include "MTypes.h" +#include "MClass.h" #include "MCore.h" #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/DataContainer.h" @@ -354,7 +355,7 @@ struct MConverter> { if (!klass) return nullptr; - MArray* result = MCore::Array::New(klass, data.Count()); + MArray* result = MCore::Array::New(klass->GetElementClass(), data.Count()); MConverter converter; converter.ToManagedArray(result, Span(data.Get(), data.Count())); return (MObject*)result; From 829d3b3642c1ade36c2e74ff37178a5a29df6702 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 15:07:06 +0200 Subject: [PATCH 74/93] Add editor shortcuts usability when toolstrip or main menu controls are focused --- Source/Editor/GUI/MainMenu.cs | 11 ++++++++++ Source/Editor/GUI/ToolStrip.cs | 21 +++++++++++++++++++ Source/Editor/Modules/ContentFindingModule.cs | 2 ++ 3 files changed, 34 insertions(+) diff --git a/Source/Editor/GUI/MainMenu.cs b/Source/Editor/GUI/MainMenu.cs index 66b10f0aa..370ad056b 100644 --- a/Source/Editor/GUI/MainMenu.cs +++ b/Source/Editor/GUI/MainMenu.cs @@ -304,6 +304,17 @@ namespace FlaxEditor.GUI return true; } + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (base.OnKeyDown(key)) + return true; + + // Fallback to the edit window for shortcuts + var editor = Editor.Instance; + return editor.Windows.EditWin.InputActions.Process(editor, this, key); + } + /// protected override void PerformLayoutAfterChildren() { diff --git a/Source/Editor/GUI/ToolStrip.cs b/Source/Editor/GUI/ToolStrip.cs index 0dac241ed..360d63ffe 100644 --- a/Source/Editor/GUI/ToolStrip.cs +++ b/Source/Editor/GUI/ToolStrip.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using FlaxEditor.Windows; using FlaxEngine; using FlaxEngine.GUI; @@ -181,5 +182,25 @@ namespace FlaxEditor.GUI PerformLayout(); } + + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (base.OnKeyDown(key)) + return true; + + // Fallback to the owning window for shortcuts + EditorWindow editorWindow = null; + ContainerControl c = Parent; + while (c != null && editorWindow == null) + { + editorWindow = c as EditorWindow; + c = c.Parent; + } + var editor = Editor.Instance; + if (editorWindow == null) + editorWindow = editor.Windows.EditWin; // Fallback to main editor window + return editorWindow.InputActions.Process(editor, this, key); + } } } diff --git a/Source/Editor/Modules/ContentFindingModule.cs b/Source/Editor/Modules/ContentFindingModule.cs index ccf1cb3e4..9e8002c8a 100644 --- a/Source/Editor/Modules/ContentFindingModule.cs +++ b/Source/Editor/Modules/ContentFindingModule.cs @@ -166,6 +166,8 @@ namespace FlaxEditor.Modules public void ShowFinder(Control control) { var finder = _finder ?? (_finder = new ContentFinder()); + if (control == null) + control = Editor.Instance.Windows.MainWindow.GUI; var position = (control.Size - new Float2(finder.Width, 300.0f)) * 0.5f; finder.Show(control, position); } From 748ff3b8a5a09aa89d2912d33f9a7a108b2121b0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 15:07:37 +0200 Subject: [PATCH 75/93] Optimize `CultureInfoEditor` and `TagEditor` performance when searching entries by text --- Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs | 4 ++-- Source/Editor/CustomEditors/Editors/TagEditor.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs b/Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs index d04b5b6da..e1a2b0195 100644 --- a/Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs @@ -123,9 +123,9 @@ namespace FlaxEditor.CustomEditors.Editors { if (tree.IsLayoutLocked) return; - root.LockChildrenRecursive(); + tree.LockChildrenRecursive(); Utilities.Utils.UpdateSearchPopupFilter(root, searchBox.Text); - root.UnlockChildrenRecursive(); + tree.UnlockChildrenRecursive(); menu.PerformLayout(); }; root.ExpandAll(true); diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index ab5fd5d1a..3d2dd86aa 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -593,9 +593,9 @@ namespace FlaxEditor.CustomEditors.Editors { if (tree.IsLayoutLocked) return; - root.LockChildrenRecursive(); + tree.LockChildrenRecursive(); Utilities.Utils.UpdateSearchPopupFilter(root, searchBox.Text); - root.UnlockChildrenRecursive(); + tree.UnlockChildrenRecursive(); menu.PerformLayout(); }; From 59c460ccbccc4944462b8f978d7fc5393e02efa9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 15:08:13 +0200 Subject: [PATCH 76/93] Format code --- Source/Editor/Managed/ManagedEditor.Internal.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index bed9cfb5c..afcbb740a 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -321,7 +321,7 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_SaveJsonAsset(MString* outputPathObj, const StringView dataObjChars = MCore::String::GetChars(dataObj); const StringAsANSI<> data(dataObjChars.Get(), dataObjChars.Length()); const StringAnsiView dataAnsi(data.Get(), data.Length()); - + const StringView dataTypeNameObjChars = MCore::String::GetChars(dataTypeNameObj); const StringAsANSI<> dataTypeName(dataTypeNameObjChars.Get(), dataTypeNameObjChars.Length()); const StringAnsiView dataTypeNameAnsi(dataTypeName.Get(), dataTypeName.Length()); @@ -427,7 +427,7 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_GetCollisionWires(CollisionData* colli const int32 linesCount = debugLines.Count() / 2; MCore::GC::WriteRef(triangles, (MObject*)MCore::Array::New(Float3::TypeInitializer.GetClass(), debugLines.Count())); - MCore::GC::WriteRef(indices, (MObject*)MCore::Array::New( MCore::TypeCache::Int32, linesCount * 3)); + MCore::GC::WriteRef(indices, (MObject*)MCore::Array::New(MCore::TypeCache::Int32, linesCount * 3)); // Use one triangle per debug line Platform::MemoryCopy(MCore::Array::GetAddress(*triangles), debugLines.Get(), debugLines.Count() * sizeof(Float3)); @@ -585,7 +585,7 @@ DEFINE_INTERNAL_CALL(MArray*) EditorInternal_GetVisualScriptLocals(int* localsCo const int32 count = stack->Scope->Parameters.Length() + stack->Scope->ReturnedValues.Count(); const MClass* mclass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEditor.Editor+VisualScriptLocal"); ASSERT(mclass); - result = MCore::Array::New( mclass, count); + result = MCore::Array::New(mclass, count); VisualScriptLocalManaged local; local.NodeId = MAX_uint32; if (stack->Scope->Parameters.Length() != 0) @@ -642,7 +642,7 @@ DEFINE_INTERNAL_CALL(MArray*) EditorInternal_GetVisualScriptStackFrames(int* sta } const MClass* mclass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEditor.Editor+VisualScriptStackFrame"); ASSERT(mclass); - result = MCore::Array::New( mclass, count); + result = MCore::Array::New(mclass, count); VisualScriptStackFrameManaged* resultPtr = MCore::Array::GetAddress(result); s = stack; count = 0; From 16fed8927c9657bea85d990a75551ba77ef4a4be Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 20:02:53 +0200 Subject: [PATCH 77/93] Refactor #1266 (code quality and simplicity) --- Source/Editor/Content/ContentFilter.cs | 257 ------------------ ...vigationButton.cs => ContentNavigation.cs} | 68 ++++- .../Content/GUI/ContentNavigationBar.cs | 27 -- .../GUI/ContentNavigationButtonSeparator.cs | 85 ------ .../Content/GUI/ContentSettingsDropdown.cs | 95 ------- Source/Editor/Content/Items/ContentItem.cs | 8 +- .../Content/Tree/RootContentTreeNode.cs | 2 +- Source/Editor/GUI/ComboBox.cs | 27 +- .../GUI/ContextMenu/ContextMenuButton.cs | 2 +- Source/Editor/GUI/NavigationBar.cs | 7 +- .../Editor/Modules/ContentDatabaseModule.cs | 17 ++ .../Windows/ContentWindow.Navigation.cs | 27 +- Source/Editor/Windows/ContentWindow.Search.cs | 33 +-- Source/Editor/Windows/ContentWindow.cs | 170 ++++++++---- 14 files changed, 241 insertions(+), 584 deletions(-) delete mode 100644 Source/Editor/Content/ContentFilter.cs rename Source/Editor/Content/GUI/{ContentNavigationButton.cs => ContentNavigation.cs} (59%) delete mode 100644 Source/Editor/Content/GUI/ContentNavigationBar.cs delete mode 100644 Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs delete mode 100644 Source/Editor/Content/GUI/ContentSettingsDropdown.cs diff --git a/Source/Editor/Content/ContentFilter.cs b/Source/Editor/Content/ContentFilter.cs deleted file mode 100644 index 12cca5c41..000000000 --- a/Source/Editor/Content/ContentFilter.cs +++ /dev/null @@ -1,257 +0,0 @@ -using FlaxEditor.Content; -using FlaxEditor.Content.GUI; -using FlaxEngine; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace FlaxEditor.Content -{ - /// - /// class with is controling visability of items in content window - /// - internal class ContentFilter - { - #region Filters Config - /// - /// all suported files by engine Content Folder - /// - public static readonly List SuportedFileExtencionsInContentFolder = new List() - { - ".flax", - ".json", - ".scene", - ".prefab", - }; - /// - /// all suported files by engine Source Folder - /// - public static readonly List SuportedFileExtencionsInSourceFolder = new List() - { - ".shader", - ".cs", - ".h", - ".cpp", - }; - /// - /// ignores folders in source folder (top layer), - /// for example obj folder is default c# project folder - /// - internal static readonly List HideFoldersInSourceFolder = new List() - { - "obj", // default c# project folder - "Properties", // c# project stuff ? - }; - /// - /// ignores files in source folder (top layer), - /// - internal static readonly List HideFilesInSourceFolder = new List() //dont edit - { - "Game.csproj", //solucion file - "Game.Gen.cs", //auto-generated not be edited - "GameEditorTarget.Build.cs", - "GameTarget.Build.cs", - }; - #endregion - internal static ContentItem gameSettings; - internal static List buildFiles; - - internal static ContentTreeNode settings; - internal static ContentTreeNode shaders; - internal static ProjectTreeNode engine; - internal static List plugins = new(); - internal static List FilterFolder(ContentFolder folder) - { - return FilterFolder(folder, SuportedFileExtencionsInContentFolder.ToArray(), SuportedFileExtencionsInSourceFolder.ToArray()); - } - internal static List FilterFolder(ContentFolder folder, string[] FileExtencionsInContentFolder, string[] ExtencionsInSourceFolder) - { - if (folder.CanHaveAssets) - { - for (int i = 0; i < folder.Children.Count; i++) - { - bool Visible = false; - for (int j = 0; j < FileExtencionsInContentFolder.Length; j++) - { - if ((folder.Children[i].Path.EndsWith(FileExtencionsInContentFolder[j]) || folder.Children[i].IsFolder)) - { - if (folder.Children[i].Visible) - { - Visible = true; - } - break; - } - } - folder.Children[i].Visible = Visible; - } - } - else if (folder.CanHaveScripts) - { - for (int i = 0; i < folder.Children.Count; i++) - { - bool Visible = false; - for (int j = 0; j < ExtencionsInSourceFolder.Length; j++) - { - if ((folder.Children[i].Path.EndsWith(ExtencionsInSourceFolder[j]) || folder.Children[i].IsFolder)) - { - if (folder.Children[i].Visible) - { - Visible = true; - } - break; - } - } - folder.Children[i].Visible = Visible; - } - } - return folder.Children; - } - internal static ProjectTreeNode Filter(ProjectTreeNode tree) - { - var content = tree.Children[0] as ContentTreeNode; - var source = tree.Children[1] as ContentTreeNode; - //filter content folder (top layer) - buildFiles = new(); - for (int i = 0; i < content.Folder.Children.Count; i++) - { - if (content.Folder.Children[i].FileName == "GameSettings.json") - { - gameSettings = content.Folder.Children[i]; - content.Folder.Children[i].Visible = false; - break; - } - } - - int hindenCount = 0; - - for (int i = content.Children.Count - 1; i >= 0; i--)//we are starting from back it faster - { - var node = content.Children[i] as ContentTreeNode; - if (node.Folder.FileName == "Settings") - { - settings = node; - hindenCount++; - node.Visible = false; - node.Folder.Visible = false; - } - if (node.Folder.FileName == "Shaders") - { - shaders = node; - hindenCount++; - node.Visible = false; - node.Folder.Visible = false; - - } - if (hindenCount == 2) - break; - } - - - //----------------------------------------------------------------------------------------------------- - - //filter source folder (top layer) - hindenCount = 0; - for (int i = 0; i < source.Folder.Children.Count; i++) - { - for (int j = 0; j < HideFilesInSourceFolder.Count; j++) - { - if (source.Folder.Children[i].FileName == HideFilesInSourceFolder[j]) - { - source.Folder.Children[i].Visible = false; - hindenCount++; - if(i > 1) - { - buildFiles.Add(source.Folder.Children[i]); - } - if (HideFilesInSourceFolder.Count == hindenCount) goto HideFilesInSourceFolderComplited; - break; - } - } - } - HideFilesInSourceFolderComplited: - hindenCount = 0; - for (int i = source.Children.Count - 1; i >= 0; i--) - { - var node = source.Children[i] as ContentTreeNode; - for (int j = 0; j < HideFoldersInSourceFolder.Count; j++) - { - if (node.Folder.FileName == HideFoldersInSourceFolder[j]) - { - node.Visible = false; - node.Folder.Visible = false; - hindenCount++; - if (HideFoldersInSourceFolder.Count == hindenCount) goto HideFoldersInSourceFolderComplited; - break; - } - } - } - HideFoldersInSourceFolderComplited: - //content - return tree; - } - internal static void UpdateFilterVisability(ContentSettingsDropdown dropdown) - { - engine.Visible = false; - engine.Folder.Visible = false; - - foreach (var item in plugins) - { - item.Visible = false; - item.Folder.Visible = false; - } - foreach (var item in buildFiles) - { - item.Visible = false; - } - gameSettings.Visible = false; - settings.Visible = false; - settings.Folder.Visible = false; - - shaders.Visible = false; - shaders.Folder.Visible = false; - - for (int i = 0; i < dropdown.Selection.Count; i++) - { - if (dropdown.Selection[i] == 0) //Show Engine Content - { - engine.Visible = true; - engine.Folder.Visible = true; - } - - if (dropdown.Selection[i] == 1)//Show Plugin Content - { - foreach (var item in plugins) - { - item.Visible = true; - item.Folder.Visible = true; - } - } - - if (dropdown.Selection[i] == 2)//Show Build Files - { - foreach (var item in buildFiles) - { - item.Visible = true; - } - } - - if (dropdown.Selection[i] == 3)//Show Game Settings - { - gameSettings.Visible = true; - settings.Visible = true; - settings.Folder.Visible = true; - } - - if (dropdown.Selection[i] == 4)//"Show Shader Source" - { - shaders.Visible = true; - shaders.Folder.Visible = true; - } - } - engine.ParentTree.PerformLayout(); - Editor.Instance.Windows.ContentWin.RefreshView(); - } - } -} diff --git a/Source/Editor/Content/GUI/ContentNavigationButton.cs b/Source/Editor/Content/GUI/ContentNavigation.cs similarity index 59% rename from Source/Editor/Content/GUI/ContentNavigationButton.cs rename to Source/Editor/Content/GUI/ContentNavigation.cs index beec247da..7adf6aa62 100644 --- a/Source/Editor/Content/GUI/ContentNavigationButton.cs +++ b/Source/Editor/Content/GUI/ContentNavigation.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEditor.GUI; +using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Drag; using FlaxEngine; using FlaxEngine.GUI; @@ -72,7 +73,6 @@ namespace FlaxEditor.Content.GUI if (_dragOverItems == null) _dragOverItems = new DragItems(ValidateDragItem); - _dragOverItems.OnDragEnter(data); var result = GetDragEffect(data); _validDragOver = result != DragDropEffect.None; @@ -122,4 +122,70 @@ namespace FlaxEditor.Content.GUI return result; } } + + sealed class ContentNavigationSeparator : ComboBox + { + public ContentNavigationButton Target; + + public ContentNavigationSeparator(ContentNavigationButton target, float x, float y, float height) + { + Target = target; + Bounds = new Rectangle(x, y, 16, height); + Offsets = new Margin(Bounds.X, Bounds.Width, Bounds.Y, Bounds.Height); + UpdateTransform(); + + MaximumItemsInViewCount = 20; + var style = Style.Current; + BackgroundColor = style.BackgroundNormal; + BackgroundColorHighlighted = BackgroundColor; + BackgroundColorSelected = BackgroundColor; + } + + protected override ContextMenu OnCreatePopup() + { + // Update items + ClearItems(); + foreach (var child in Target.TargetNode.Children) + { + if (child is ContentTreeNode node) + { + if (node.Folder.VisibleInHierarchy) // Respect the filter set by ContentFilterConfig.Filter(...) + AddItem(node.Folder.ShortName); + } + } + + return base.OnCreatePopup(); + } + + public override void Draw() + { + var style = Style.Current; + var rect = new Rectangle(Float2.Zero, Size); + var color = IsDragOver ? style.BackgroundSelected * 0.6f : (_mouseDown ? style.BackgroundSelected : (IsMouseOver ? style.BackgroundHighlighted : Color.Transparent)); + Render2D.FillRectangle(rect, color); + Render2D.DrawSprite(Editor.Instance.Icons.ArrowRight12, new Rectangle(rect.Location.X, rect.Y + rect.Size.Y * 0.25f, rect.Size.X, rect.Size.X), EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled); + } + + protected override void OnLayoutMenuButton(ContextMenuButton button, int index, bool construct = false) + { + button.Icon = Editor.Instance.Icons.FolderClosed32; + if (_tooltips != null && _tooltips.Length > index) + button.TooltipText = _tooltips[index]; + } + + protected override void OnItemClicked(int index) + { + base.OnItemClicked(index); + + var item = _items[index]; + foreach (var child in Target.TargetNode.Children) + { + if (child is ContentTreeNode node && node.Folder.ShortName == item) + { + Editor.Instance.Windows.ContentWin.Navigate(node); + return; + } + } + } + } } diff --git a/Source/Editor/Content/GUI/ContentNavigationBar.cs b/Source/Editor/Content/GUI/ContentNavigationBar.cs deleted file mode 100644 index 673ac806e..000000000 --- a/Source/Editor/Content/GUI/ContentNavigationBar.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FlaxEditor.GUI; -using FlaxEngine; - -namespace FlaxEditor.Content.GUI -{ - internal class ContentNavigationBar : NavigationBar - { - ToolStrip _toolstrip; - internal float ofssetFromRightEdge = 80; - internal ContentNavigationBar(ToolStrip toolstrip) : base() - { - _toolstrip = toolstrip; - } - /// - protected override void Arrange() - { - base.Arrange(); - var lastToolstripButton = _toolstrip.LastButton; - var parentSize = Parent.Size; - Bounds = new Rectangle - ( - new Float2(lastToolstripButton.Right, 0), - new Float2(parentSize.X - X - ofssetFromRightEdge, _toolstrip.Height) - ); - } - } -} diff --git a/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs b/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs deleted file mode 100644 index cad63066d..000000000 --- a/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs +++ /dev/null @@ -1,85 +0,0 @@ -using FlaxEditor; -using FlaxEditor.GUI; -using FlaxEditor.GUI.ContextMenu; -using FlaxEngine; -using FlaxEngine.GUI; -using System.Collections.Generic; - -namespace FlaxEditor.Content.GUI -{ - internal class ContentNavigationButtonSeparator : ComboBox - { - public ContentNavigationButton Target { get; } - public ContentNavigationButtonSeparator(ContentNavigationButton target, float x, float y,float height) - { - Target = target; - Bounds = new Rectangle(x, y, 16, height); - Offsets = new Margin(Bounds.X, Bounds.Width, Bounds.Y, Bounds.Height); - UpdateTransform(); - - MaximumItemsInViewCount = 20; - var style = Style.Current; - BackgroundColor = style.BackgroundNormal; - BackgroundColorHighlighted = BackgroundColor; - BackgroundColorSelected = BackgroundColor; - } - protected override ContextMenu OnCreatePopup() - { - UpdateDropDownItems(); - return base.OnCreatePopup(); - } - internal void UpdateDropDownItems() - { - ClearItems(); - _items = new(); - for (int i = 0; i < Target.TargetNode.Children.Count; i++) - { - if (Target.TargetNode.Children[i] is ContentTreeNode node) - { - if (node.Folder.VisibleInHierarchy) // respect the filter set by ContentFilterConfig.Filter(...) - AddItem(node.Folder.ShortName); - } - } - } - /// - public override void Draw() - { - // Cache data - var style = Style.Current; - var clientRect = new Rectangle(Float2.Zero, Size); - // Draw background - if (IsDragOver) - { - Render2D.FillRectangle(clientRect, Style.Current.BackgroundSelected * 0.6f); - } - else if (_mouseDown) - { - Render2D.FillRectangle(clientRect, style.BackgroundSelected); - } - else if (IsMouseOver) - { - Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); - } - - Render2D.DrawSprite(Editor.Instance.Icons.ArrowRight12, new Rectangle(clientRect.Location.X, clientRect.Y + ((Size.Y / 2f)/2f), Size.X, Size.X), EnabledInHierarchy ? Style.Current.Foreground : Style.Current.ForegroundDisabled); - } - protected override void OnLayoutMenuButton(ref ContextMenuButton button, int index, bool construct = false) - { - var style = Style.Current; - button.Icon = Editor.Instance.Icons.FolderClosed32; - if (_tooltips != null && _tooltips.Length > index) - { - button.TooltipText = _tooltips[index]; - } - } - protected override void OnItemClicked(int index) - { - base.OnItemClicked(index); - if (Target.TargetNode.Children[index] is ContentTreeNode node) - { - Editor.Instance.Windows.ContentWin.Navigate(node); - } - // Navigate calls the OnDestroy at some point dont place code below or editor will crash - } - } -} diff --git a/Source/Editor/Content/GUI/ContentSettingsDropdown.cs b/Source/Editor/Content/GUI/ContentSettingsDropdown.cs deleted file mode 100644 index 04722a674..000000000 --- a/Source/Editor/Content/GUI/ContentSettingsDropdown.cs +++ /dev/null @@ -1,95 +0,0 @@ -using FlaxEditor.Content; -using FlaxEditor.CustomEditors.Editors; -using FlaxEditor.GUI; -using FlaxEditor.GUI.ContextMenu; -using FlaxEditor.States; -using FlaxEditor.Utilities; -using FlaxEngine; -using FlaxEngine.GUI; -using FlaxEngine.Json; -using System.Collections.Generic; -using System.Linq; - -namespace FlaxEditor.Content.GUI -{ - class ContentSettingsDropdown : ComboBox - { - internal readonly List Settings = new() - { - "Show Engine Content", - "Show Plugin Content", - "Show Build Files", - "Show Game Settings", - "Show Shader Source" - }; - public ContentSettingsDropdown() - { - SupportMultiSelect = true; - MaximumItemsInViewCount = 20; - var style = Style.Current; - BackgroundColor = style.BackgroundNormal; - BackgroundColorHighlighted = BackgroundColor; - BackgroundColorSelected = BackgroundColor; - - } - protected override ContextMenu OnCreatePopup() - { - UpdateDropDownItems(); - return base.OnCreatePopup(); - } - internal void UpdateDropDownItems() - { - ClearItems(); - - for (int i = 0; i < Settings.Count; i++) - { - AddItem(Settings[i]); - } - } - /// - public override void Draw() - { - // Cache data - var style = Style.Current; - var clientRect = new Rectangle(Float2.Zero, Size); - // Draw background - if (IsDragOver) - { - Render2D.FillRectangle(clientRect, Style.Current.BackgroundSelected * 0.6f); - } - else if (_mouseDown) - { - Render2D.FillRectangle(clientRect, style.BackgroundSelected); - } - else if (IsMouseOver) - { - Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); - } - float size = (Size.Y / 1.5f); //Icon size - // Draw text - Render2D.DrawText(Font.GetFont(), "Settings", new Rectangle(size, 0, Size.X - Size.Y, Size.Y), TextColor, TextAlignment.Center, TextAlignment.Center); - Render2D.DrawSprite(Editor.Instance.Icons.Settings12, new Rectangle((Size.Y - size) / 2.0f, (Size.Y - size) / 2.0f, size, size)); - } - protected override void OnLayoutMenuButton(ref ContextMenuButton button, int index, bool construct = false) - { - var style = Style.Current; - if (_selectedIndices.Contains(index)) - { - button.Icon = style.CheckBoxTick; - } - else - { - button.Icon = SpriteHandle.Default; - } - if (_tooltips != null && _tooltips.Length > index) - { - button.TooltipText = _tooltips[index]; - } - } - protected override void OnSelectedIndexChanged() - { - ContentFilter.UpdateFilterVisability(this); - base.OnSelectedIndexChanged(); - } - } -} diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index e6a8c309c..94db3a5b9 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -441,11 +441,9 @@ namespace FlaxEditor.Content { get { + // Skip when hidden if (!Visible) - { return Rectangle.Empty; - } - var view = Parent as ContentView; var size = Size; switch (view?.ViewType ?? ContentViewType.Tiles) @@ -453,7 +451,7 @@ namespace FlaxEditor.Content case ContentViewType.Tiles: { var textHeight = DefaultTextHeight * size.X / DefaultWidth; - return new Rectangle(0, size.Y - textHeight, size.X - 2, textHeight); + return new Rectangle(0, size.Y - textHeight, size.X, textHeight); } case ContentViewType.List: { @@ -671,8 +669,6 @@ namespace FlaxEditor.Content /// public override void Draw() { - - // Cache data var size = Size; var style = Style.Current; var view = Parent as ContentView; diff --git a/Source/Editor/Content/Tree/RootContentTreeNode.cs b/Source/Editor/Content/Tree/RootContentTreeNode.cs index 6dde4cbb2..328c51874 100644 --- a/Source/Editor/Content/Tree/RootContentTreeNode.cs +++ b/Source/Editor/Content/Tree/RootContentTreeNode.cs @@ -17,6 +17,6 @@ namespace FlaxEditor.Content } /// - public override string NavButtonLabel => string.Empty; + public override string NavButtonLabel => " /"; } } diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index da38268b2..da2106450 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using FlaxEngine.GUI; -using FlaxEditor.GUI.ContextMenu; namespace FlaxEditor.GUI { @@ -399,13 +399,14 @@ namespace FlaxEditor.GUI OnItemClicked((int)btn.Tag); if (SupportMultiSelect) { + // Don't hide in multi-select, so user can edit multiple elements instead of just one UpdateButtons(); _popupMenu?.PerformLayout(); } else { _popupMenu?.Hide(); - }//[nori_sc] don't hide it Support MultiSelect is on actions per min is important for UX, if some one wont to set more then 5 elements in multi select menu let them do it + } }; } @@ -423,13 +424,15 @@ namespace FlaxEditor.GUI if (_items.Count > 0) { UpdateButtons(); + // Show dropdown list _popupMenu.MinimumWidth = Width; _popupMenu.Show(this, new Float2(1, Height)); } } + /// - /// update buttons layout and repains + /// Updates buttons layout. /// private void UpdateButtons() { @@ -443,7 +446,7 @@ namespace FlaxEditor.GUI for (int i = 0; i < _items.Count; i++) { var btn = _popupMenu.AddButton(_items[i]); - OnLayoutMenuButton(ref btn, i, true); + OnLayoutMenuButton(btn, i, true); btn.Tag = i; } } @@ -457,25 +460,23 @@ namespace FlaxEditor.GUI if (itemControls[i] is ContextMenuButton btn) { btn.Text = _items[i]; - OnLayoutMenuButton(ref btn, i, true); + OnLayoutMenuButton(btn, i, true); } } } } + /// - /// caled when button is created or repainted u can overite it to give the button custom look + /// Called when button is created or updated. Can be used to customize the visuals. /// - /// button refrance - /// curent item index + /// The button. + /// The item index. /// true if button is created else it is repainting the button - protected virtual void OnLayoutMenuButton(ref FlaxEditor.GUI.ContextMenu.ContextMenuButton button,int index, bool construct = false) + protected virtual void OnLayoutMenuButton(ContextMenuButton button, int index, bool construct = false) { - var style = Style.Current; button.Checked = _selectedIndices.Contains(index); if (_tooltips != null && _tooltips.Length > index) - { button.TooltipText = _tooltips[index]; - } } /// @@ -498,6 +499,8 @@ namespace FlaxEditor.GUI _popupMenu = null; } + if (IsDisposing) + return; _selectedIndices.Clear(); _selectedIndices = null; _items.Clear(); diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index c2b130e4e..e371f7c4b 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -138,7 +138,7 @@ namespace FlaxEditor.GUI.ContextMenu // Draw icon const float iconSize = 14; - var icon = Checked ? Style.Current.CheckBoxTick : Icon; + var icon = Checked ? style.CheckBoxTick : Icon; if (icon.IsValid) Render2D.DrawSprite(icon, new Rectangle(-iconSize - 1, (Height - iconSize) / 2, iconSize, iconSize), textColor); } diff --git a/Source/Editor/GUI/NavigationBar.cs b/Source/Editor/GUI/NavigationBar.cs index 3c717423b..b3811b4a9 100644 --- a/Source/Editor/GUI/NavigationBar.cs +++ b/Source/Editor/GUI/NavigationBar.cs @@ -50,14 +50,9 @@ namespace FlaxEditor.GUI { if (toolstrip == null) return; - var lastToolstripButton = toolstrip.LastButton; var parentSize = Parent.Size; - Bounds = new Rectangle - ( - new Float2(lastToolstripButton.Right + 8.0f, 0), - new Float2(parentSize.X - X - 8.0f, toolstrip.Height) - ); + Bounds = new Rectangle(lastToolstripButton.Right + 8.0f, 0, parentSize.X - X - 8.0f, toolstrip.Height); } } } diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index ecafbf5d4..29d6b2c00 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -902,6 +902,23 @@ namespace FlaxEditor.Modules } if (sortChildren) node.SortChildren(); + + // Ignore some special folders + if (node is MainContentTreeNode mainNode && mainNode.Folder.ShortName == "Source") + { + var mainNodeChild = mainNode.Folder.Find(StringUtils.CombinePaths(mainNode.Path, "obj")) as ContentFolder; + if (mainNodeChild != null) + { + mainNodeChild.Visible = false; + mainNodeChild.Node.Visible = false; + } + mainNodeChild = mainNode.Folder.Find(StringUtils.CombinePaths(mainNode.Path, "Properties")) as ContentFolder; + if (mainNodeChild != null) + { + mainNodeChild.Visible = false; + mainNodeChild.Node.Visible = false; + } + } } private void LoadScripts(ContentTreeNode parent, string[] files) diff --git a/Source/Editor/Windows/ContentWindow.Navigation.cs b/Source/Editor/Windows/ContentWindow.Navigation.cs index ca3de992d..b1def7ae8 100644 --- a/Source/Editor/Windows/ContentWindow.Navigation.cs +++ b/Source/Editor/Windows/ContentWindow.Navigation.cs @@ -196,18 +196,19 @@ namespace FlaxEditor.Windows } float x = NavigationBar.DefaultButtonsMargin; float h = _toolStrip.ItemsHeight - 2 * ToolStrip.DefaultMarginV; - for (int i = nodes.Count - 2; i >= 0; i--) + for (int i = nodes.Count - 1; i >= 0; i--) { - var button = new ContentNavigationButton(nodes[i], x - 100, ToolStrip.DefaultMarginV, h); + var button = new ContentNavigationButton(nodes[i], x, ToolStrip.DefaultMarginV, h); button.PerformLayout(); x += button.Width + NavigationBar.DefaultButtonsMargin; _navigationBar.AddChild(button); - if (i == 0) - continue; - var buttonSeparator = new ContentNavigationButtonSeparator(button, x, ToolStrip.DefaultMarginV, h); - buttonSeparator.PerformLayout(); - x += buttonSeparator.Width + NavigationBar.DefaultButtonsMargin; - _navigationBar.AddChild(buttonSeparator); + if (i > 0) + { + var separator = new ContentNavigationSeparator(button, x, ToolStrip.DefaultMarginV, h); + separator.PerformLayout(); + x += separator.Width + NavigationBar.DefaultButtonsMargin; + _navigationBar.AddChild(separator); + } } nodes.Clear(); @@ -224,21 +225,13 @@ namespace FlaxEditor.Windows /// /// Gets the current view folder. /// - public ContentFolder CurrentViewFolder - { - get - { - var node = SelectedNode; - return node?.Folder; - } - } + public ContentFolder CurrentViewFolder => SelectedNode?.Folder; /// /// Shows the root folder. /// public void ShowRoot() { - // Show root folder _tree.Select(_root); } } diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index a6fd2c81e..a1072d158 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -191,6 +191,7 @@ namespace FlaxEditor.Windows } // Search by filter only + bool showAllFiles = _showAllFiles; if (string.IsNullOrWhiteSpace(query)) { if (SelectedNode == _root) @@ -199,12 +200,12 @@ namespace FlaxEditor.Windows for (int i = 0; i < _root.ChildrenCount; i++) { if (_root.GetChild(i) is ContentTreeNode node) - UpdateItemsSearchFilter(node.Folder, items, filters); + UpdateItemsSearchFilter(node.Folder, items, filters, showAllFiles); } } else { - UpdateItemsSearchFilter(CurrentViewFolder, items, filters); + UpdateItemsSearchFilter(CurrentViewFolder, items, filters, showAllFiles); } } // Search by asset ID @@ -221,12 +222,12 @@ namespace FlaxEditor.Windows for (int i = 0; i < _root.ChildrenCount; i++) { if (_root.GetChild(i) is ContentTreeNode node) - UpdateItemsSearchFilter(node.Folder, items, filters, query); + UpdateItemsSearchFilter(node.Folder, items, filters, showAllFiles, query); } } else { - UpdateItemsSearchFilter(CurrentViewFolder, items, filters, query); + UpdateItemsSearchFilter(CurrentViewFolder, items, filters, showAllFiles, query); } } @@ -234,42 +235,34 @@ namespace FlaxEditor.Windows _view.ShowItems(items, _sortType); } - private void UpdateItemsSearchFilter(ContentFolder folder, List items, bool[] filters) + private void UpdateItemsSearchFilter(ContentFolder folder, List items, bool[] filters, bool showAllFiles) { for (int i = 0; i < folder.Children.Count; i++) { var child = folder.Children[i]; - if (child is ContentFolder childFolder) { - UpdateItemsSearchFilter(childFolder, items, filters); + UpdateItemsSearchFilter(childFolder, items, filters, showAllFiles); } - else + else if (filters[(int)child.SearchFilter] && (showAllFiles || !(child is FileItem))) { - if (filters[(int)child.SearchFilter]) - { - items.Add(child); - } + items.Add(child); } } } - private void UpdateItemsSearchFilter(ContentFolder folder, List items, bool[] filters, string filterText) + private void UpdateItemsSearchFilter(ContentFolder folder, List items, bool[] filters, bool showAllFiles, string filterText) { for (int i = 0; i < folder.Children.Count; i++) { var child = folder.Children[i]; - if (child is ContentFolder childFolder) { - UpdateItemsSearchFilter(childFolder, items, filters, filterText); + UpdateItemsSearchFilter(childFolder, items, filters, showAllFiles, filterText); } - else if (filters[(int)child.SearchFilter]) + else if (filters[(int)child.SearchFilter] && (showAllFiles || !(child is FileItem)) && QueryFilterHelper.Match(filterText, child.ShortName)) { - if (filters[(int)child.SearchFilter] && QueryFilterHelper.Match(filterText, child.ShortName)) - { - items.Add(child); - } + items.Add(child); } } } diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 2a779ec01..f4fb4afdc 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Xml; using FlaxEditor.Content; using FlaxEditor.Content.GUI; @@ -39,14 +40,13 @@ namespace FlaxEditor.Windows private readonly ToolStripButton _navigateForwardButton; private readonly ToolStripButton _navigateUpButton; - private ContentNavigationBar _navigationBar; + private NavigationBar _navigationBar; private Tree _tree; private TextBox _foldersSearchBox; private TextBox _itemsSearchBox; private ViewDropdown _viewDropdown; - private ContentSettingsDropdown _ContentSettingDropdown; - private const float _ContentDropdownSizeX = 100; private SortType _sortType; + private bool _showEngineFiles = true, _showPluginsFiles = true, _showAllFiles = true; private RootContentTreeNode _root; @@ -66,6 +66,57 @@ namespace FlaxEditor.Windows /// public ContentView View => _view; + internal bool ShowEngineFiles + { + get => _showEngineFiles; + set + { + if (_showEngineFiles != value) + { + _showEngineFiles = value; + if (Editor.ContentDatabase.Engine != null) + { + Editor.ContentDatabase.Engine.Visible = value; + Editor.ContentDatabase.Engine.Folder.Visible = value; + RefreshView(); + } + } + } + } + + internal bool ShowPluginsFiles + { + get => _showPluginsFiles; + set + { + if (_showPluginsFiles != value) + { + _showPluginsFiles = value; + foreach (var project in Editor.ContentDatabase.Projects) + { + if (project == Editor.ContentDatabase.Game || project == Editor.ContentDatabase.Engine) + continue; + project.Visible = value; + project.Folder.Visible = value; + RefreshView(); + } + } + } + } + + internal bool ShowAllFiles + { + get => _showAllFiles; + set + { + if (_showAllFiles != value) + { + _showAllFiles = value; + RefreshView(); + } + } + } + /// /// Initializes a new instance of the class. /// @@ -75,7 +126,7 @@ namespace FlaxEditor.Windows { Title = "Content"; Icon = editor.Icons.Folder32; - + // Content database events editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true; editor.ContentDatabase.ItemRemoved += OnContentDatabaseItemRemoved; @@ -110,24 +161,11 @@ namespace FlaxEditor.Windows _navigateForwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Right64, NavigateForward).LinkTooltip("Navigate forward"); _navigateUpButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Up64, NavigateUp).LinkTooltip("Navigate up"); _toolStrip.AddSeparator(); + // Navigation bar - - _navigationBar = new ContentNavigationBar(_toolStrip) + _navigationBar = new NavigationBar { Parent = _toolStrip, - ofssetFromRightEdge = _ContentDropdownSizeX - }; - var DropdownSettingsPanel = new Panel(ScrollBars.None) - { - Parent = _toolStrip, - Size = new Float2(_ContentDropdownSizeX, _toolStrip.Height) - }; - //setings Dropdown - _ContentSettingDropdown = new ContentSettingsDropdown - { - AnchorPreset = AnchorPresets.StretchAll, - Parent = DropdownSettingsPanel, - Offsets = new Margin(2, 4, 4, 4) }; // Split panel @@ -231,10 +269,6 @@ namespace FlaxEditor.Windows private ContextMenu OnViewDropdownPopupCreate(ComboBox comboBox) { var menu = new ContextMenu(); - - var showFileExtensionsButton = menu.AddButton("Show file extensions", () => View.ShowFileExtensions = !View.ShowFileExtensions); - showFileExtensionsButton.Checked = View.ShowFileExtensions; - showFileExtensionsButton.AutoCheck = true; var viewScale = menu.AddButton("View Scale"); viewScale.CloseMenuOnClick = false; @@ -259,6 +293,33 @@ namespace FlaxEditor.Windows } }; + var show = menu.AddChildMenu("Show"); + { + var b = show.ContextMenu.AddButton("File extensions", () => View.ShowFileExtensions = !View.ShowFileExtensions); + b.TooltipText = "Shows all files with extensions"; + b.Checked = View.ShowFileExtensions; + b.CloseMenuOnClick = false; + b.AutoCheck = true; + + b = show.ContextMenu.AddButton("Engine files", () => ShowEngineFiles = !ShowEngineFiles); + b.TooltipText = "Shows in-built engine content"; + b.Checked = ShowEngineFiles; + b.CloseMenuOnClick = false; + b.AutoCheck = true; + + b = show.ContextMenu.AddButton("Plugins files", () => ShowPluginsFiles = !ShowPluginsFiles); + b.TooltipText = "Shows plugin projects content"; + b.Checked = ShowPluginsFiles; + b.CloseMenuOnClick = false; + b.AutoCheck = true; + + b = show.ContextMenu.AddButton("All files", () => ShowAllFiles = !ShowAllFiles); + b.TooltipText = "Shows all files including other than assets and source code"; + b.Checked = ShowAllFiles; + b.CloseMenuOnClick = false; + b.AutoCheck = true; + } + var filters = menu.AddChildMenu("Filters"); for (int i = 0; i < _viewDropdown.Items.Count; i++) { @@ -367,7 +428,7 @@ namespace FlaxEditor.Windows { if (!item.CanRename) return; - + // Show element in the view Select(item, true); @@ -444,10 +505,7 @@ namespace FlaxEditor.Windows if (!Editor.ContentEditing.IsValidAssetName(item, newShortName, out string hint)) { // Invalid name - MessageBox.Show("Given asset name is invalid. " + hint, - "Invalid name", - MessageBoxButtons.OK, - MessageBoxIcon.Error); + MessageBox.Show("Given asset name is invalid. " + hint, "Invalid name", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } @@ -893,7 +951,7 @@ namespace FlaxEditor.Windows if (target == _root) { // Special case for root folder - List items = new List(8); + var items = new List(8); for (int i = 0; i < _root.ChildrenCount; i++) { if (_root.GetChild(i) is ContentTreeNode node) @@ -905,9 +963,11 @@ namespace FlaxEditor.Windows } else { - // Show folder contents - _view.ShowItems(ContentFilter.FilterFolder(target.Folder), _sortType, false, true); + var items = target.Folder.Children; + if (!_showAllFiles) + items = items.Where(x => !(x is FileItem)).ToList(); + _view.ShowItems(items, _sortType, false, true); } } @@ -930,12 +990,6 @@ namespace FlaxEditor.Windows _navigateUpButton.Enabled = folder != null && _tree.SelectedNode != _root; } - private void RemoveFolder2Root(ContentTreeNode node) - { - // Remove from the root - _root.RemoveChild(node); - } - /// public override void OnInit() { @@ -947,18 +1001,20 @@ namespace FlaxEditor.Windows _root.Expand(true); // Add game project on top, plugins in the middle and engine at bottom - _root.AddChild(ContentFilter.Filter(Editor.ContentDatabase.Game)); + _root.AddChild(Editor.ContentDatabase.Game); Editor.ContentDatabase.Projects.Sort(); foreach (var project in Editor.ContentDatabase.Projects) { project.SortChildrenRecursive(); if (project == Editor.ContentDatabase.Game || project == Editor.ContentDatabase.Engine) continue; + project.Visible = _showPluginsFiles; + project.Folder.Visible = _showPluginsFiles; _root.AddChild(project); - ContentFilter.plugins.Add(project); } + Editor.ContentDatabase.Engine.Visible = _showEngineFiles; + Editor.ContentDatabase.Engine.Folder.Visible = _showEngineFiles; _root.AddChild(Editor.ContentDatabase.Engine); - ContentFilter.engine = Editor.ContentDatabase.Engine; Editor.ContentDatabase.Game?.Expand(true); _tree.Margin = new Margin(0.0f, 0.0f, -16.0f, 2.0f); // Hide root node @@ -1029,7 +1085,6 @@ namespace FlaxEditor.Windows /// public override bool OnMouseUp(Float2 location, MouseButton button) { - // Check if it's a right mouse button if (button == MouseButton.Right) { // Find control that is under the mouse @@ -1067,6 +1122,15 @@ namespace FlaxEditor.Windows return base.OnMouseUp(location, button); } + + /// + protected override void PerformLayoutBeforeChildren() + { + base.PerformLayoutBeforeChildren(); + + _navigationBar?.UpdateBounds(_toolStrip); + } + /// public override bool UseLayoutData => true; @@ -1076,13 +1140,10 @@ namespace FlaxEditor.Windows LayoutSerializeSplitter(writer, "Split", _split); writer.WriteAttributeString("Scale", _view.ViewScale.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("ShowFileExtensions", _view.ShowFileExtensions.ToString()); + writer.WriteAttributeString("ShowEngineFiles", ShowEngineFiles.ToString()); + writer.WriteAttributeString("ShowPluginsFiles", ShowPluginsFiles.ToString()); + writer.WriteAttributeString("ShowAllFiles", ShowAllFiles.ToString()); writer.WriteAttributeString("ViewType", _view.ViewType.ToString()); - for (int i = 0; i < _ContentSettingDropdown.Selection.Count; i++) - { - if(_ContentSettingDropdown.Selection.Count != 0) - writer.WriteAttributeString(_ContentSettingDropdown.Settings[_ContentSettingDropdown.Selection[i]].Replace(' ', '_'), _ContentSettingDropdown.Selection[i].ToString()); - } - } /// @@ -1093,17 +1154,14 @@ namespace FlaxEditor.Windows _view.ViewScale = value1; if (bool.TryParse(node.GetAttribute("ShowFileExtensions"), out bool value2)) _view.ShowFileExtensions = value2; + if (bool.TryParse(node.GetAttribute("ShowEngineFiles"), out value2)) + ShowEngineFiles = value2; + if (bool.TryParse(node.GetAttribute("ShowPluginsFiles"), out value2)) + ShowPluginsFiles = value2; + if (bool.TryParse(node.GetAttribute("ShowAllFiles"), out value2)) + ShowAllFiles = value2; if (Enum.TryParse(node.GetAttribute("ViewType"), out ContentViewType viewType)) _view.ViewType = viewType; - - for (int i = 0; i < _ContentSettingDropdown.Settings.Count; i++) - { - int flag; - if (int.TryParse(node.GetAttribute(_ContentSettingDropdown.Settings[i].Replace(' ', '_')), out flag)) - _ContentSettingDropdown.Selection.Add(flag); - - } - ContentFilter.UpdateFilterVisability(_ContentSettingDropdown); } /// From 0a5f983ff4eff7fc2b9bf23ad3f75423e77761ac Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 21:36:36 +0200 Subject: [PATCH 78/93] Fix error when model is missing in Editor for prefab editor #1354 --- Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index 277f9ecb0..9607680f2 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -39,6 +39,8 @@ namespace FlaxEditor.CustomEditors.Editors _entryIndex = entryIndex; _modelInstance = modelInstance; var slots = modelInstance.MaterialSlots; + if (slots == null || entryIndex >= slots.Length) + return; if (entry.Material == slots[entryIndex].Material) { // Ensure that entry with default material set is set back to null From 776fbe2a7b8a44b7c008c7337827c17031d226a6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 22:30:51 +0200 Subject: [PATCH 79/93] Fix creating plugin with whitespace or non-ASCII character in the name #1335 --- Source/Editor/Windows/PluginsWindow.cs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index cf68c6ad8..f4acfffc8 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -636,11 +636,17 @@ namespace FlaxEditor.Windows { Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to add plugin to project. {e}"); } - var oldpluginPath = Path.Combine(extractPath, "ExamplePlugin-master"); var newPluginPath = Path.Combine(extractPath , pluginName); Directory.Move(oldpluginPath, newPluginPath); + // Format plugin name into a valid name for code (C#/C++ typename) + var pluginCodeName = Content.ScriptItem.FilterScriptName(pluginName); + if (string.IsNullOrEmpty(pluginCodeName)) + pluginCodeName = "MyPlugin"; + Editor.Log($"Using plugin code type name: {pluginCodeName}"); + + var oldFlaxProjFile = Path.Combine(newPluginPath, "ExamplePlugin.flaxproj"); var newFlaxProjFile = Path.Combine(newPluginPath, $"{pluginName}.flaxproj"); File.Move(oldFlaxProjFile, newFlaxProjFile); @@ -659,8 +665,8 @@ namespace FlaxEditor.Windows flaxPluginProjContents.Version = new Version(pluginVersion); if (!string.IsNullOrEmpty(companyName)) flaxPluginProjContents.Company = companyName; - flaxPluginProjContents.GameTarget = $"{pluginName}Target"; - flaxPluginProjContents.EditorTarget = $"{pluginName}EditorTarget"; + flaxPluginProjContents.GameTarget = $"{pluginCodeName}Target"; + flaxPluginProjContents.EditorTarget = $"{pluginCodeName}EditorTarget"; await File.WriteAllTextAsync(newFlaxProjFile, JsonSerializer.Serialize(flaxPluginProjContents, typeof(ProjectInfo))); // Rename source directories @@ -676,14 +682,14 @@ namespace FlaxEditor.Windows File.Delete(file); continue; } - + var fileText = await File.ReadAllTextAsync(file); - await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginName)); - var fileName = Path.GetFileName(file).Replace("ExamplePlugin", pluginName); + await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginCodeName)); + var fileName = Path.GetFileName(file).Replace("ExamplePlugin", pluginCodeName); File.Move(file, Path.Combine(directory, fileName)); } - - var newName = directory.Replace("ExamplePlugin", pluginName); + + var newName = directory.Replace("ExamplePlugin", pluginCodeName); Directory.Move(directory, newName); } @@ -692,8 +698,8 @@ namespace FlaxEditor.Windows foreach (var file in targetFiles) { var fileText = await File.ReadAllTextAsync(file); - await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginName)); - var newName = file.Replace("ExamplePlugin", pluginName); + await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginCodeName)); + var newName = file.Replace("ExamplePlugin", pluginCodeName); File.Move(file, newName); } Debug.Logger.LogHandler.LogWrite(LogType.Info, $"Plugin project {pluginName} has successfully been created."); From 99c2adc1f3f91ee9f6240b35b38858fb57747393 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 22:32:17 +0200 Subject: [PATCH 80/93] Add game settings formatting for created plugin ProjectInfo #1335 --- Source/Editor/Windows/PluginsWindow.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index f4acfffc8..fa2547797 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -669,6 +669,17 @@ namespace FlaxEditor.Windows flaxPluginProjContents.EditorTarget = $"{pluginCodeName}EditorTarget"; await File.WriteAllTextAsync(newFlaxProjFile, JsonSerializer.Serialize(flaxPluginProjContents, typeof(ProjectInfo))); + // Format game settings + var gameSettingsPath = Path.Combine(newPluginPath, "Content", "GameSettings.json"); + if (File.Exists(gameSettingsPath)) + { + var contents = await File.ReadAllTextAsync(gameSettingsPath); + contents = contents.Replace("Example Plugin", pluginName); + contents = contents.Replace("\"CompanyName\": \"Flax\"", $"\"CompanyName\": \"{companyName}\""); + contents = contents.Replace("1.0", pluginVersion); + await File.WriteAllTextAsync(gameSettingsPath, contents, Encoding.UTF8); + } + // Rename source directories var sourcePath = Path.Combine(newPluginPath, "Source"); var sourceDirectories = Directory.GetDirectories(sourcePath); From 985d6cc811134f5321b71fd81d16680efb941bef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 22:32:37 +0200 Subject: [PATCH 81/93] Add UTF8 format for created plugin files and format code #1335 --- Source/Editor/Content/Items/ScriptItem.cs | 2 +- Source/Editor/Windows/PluginsWindow.cs | 87 ++++++++++++----------- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/Source/Editor/Content/Items/ScriptItem.cs b/Source/Editor/Content/Items/ScriptItem.cs index 811afc179..95b879b79 100644 --- a/Source/Editor/Content/Items/ScriptItem.cs +++ b/Source/Editor/Content/Items/ScriptItem.cs @@ -30,7 +30,7 @@ namespace FlaxEditor.Content ShowFileExtension = true; } - private static string FilterScriptName(string input) + internal static string FilterScriptName(string input) { var length = input.Length; var sb = new StringBuilder(length); diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index fa2547797..d53b64e22 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -6,6 +6,7 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Net.Http; +using System.Text; using System.Threading.Tasks; using FlaxEditor.GUI; using FlaxEditor.GUI.ContextMenu; @@ -13,7 +14,6 @@ using FlaxEditor.GUI.Tabs; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; -using Debug = FlaxEngine.Debug; namespace FlaxEditor.Windows { @@ -193,7 +193,7 @@ namespace FlaxEditor.Windows Text = "Create Plugin Project", TooltipText = "Add new plugin project.", AnchorPreset = AnchorPresets.TopLeft, - LocalLocation = new Float2(70,18), + LocalLocation = new Float2(70, 18), Size = new Float2(150, 25), Parent = vp, }; @@ -209,7 +209,7 @@ namespace FlaxEditor.Windows Parent = vp, }; _cloneProjectButton.Clicked += OnCloneProjectButtonClicked; - + _tabs = new Tabs { Orientation = Orientation.Vertical, @@ -254,7 +254,7 @@ namespace FlaxEditor.Windows nameTextBox.LocalX += (300 - (10)) * 0.5f; nameTextBox.LocalY += 10; nameLabel.LocalX += (300 - (nameLabel.Width + nameTextBox.Width)) * 0.5f + 10; - + var defaultTextBoxBorderColor = nameTextBox.BorderColor; var defaultTextBoxBorderSelectedColor = nameTextBox.BorderSelectedColor; nameTextBox.TextChanged += () => @@ -265,7 +265,7 @@ namespace FlaxEditor.Windows nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; return; } - + var pluginPath = Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text); if (Directory.Exists(pluginPath)) { @@ -315,7 +315,7 @@ namespace FlaxEditor.Windows { if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text)) && !string.IsNullOrEmpty(nameTextBox.Text)) { - Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Cannot create plugin due to name conflict."); + Editor.LogWarning("Cannot create plugin due to name conflict."); return; } OnCloneButtonClicked(nameTextBox.Text, gitPathTextBox.Text); @@ -346,7 +346,7 @@ namespace FlaxEditor.Windows { if (string.IsNullOrEmpty(gitPath)) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to create plugin project due to no GIT path."); + Editor.LogError($"Failed to create plugin project due to no GIT path."); return; } if (string.IsNullOrEmpty(pluginName)) @@ -363,32 +363,34 @@ namespace FlaxEditor.Windows pluginName = name; } } - + var clonePath = Path.Combine(Globals.ProjectFolder, "Plugins", pluginName); if (!Directory.Exists(clonePath)) Directory.CreateDirectory(clonePath); else { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Plugin Name is already used. Pick a different Name."); + Editor.LogError($"Plugin Name is already used. Pick a different Name."); return; } try { // Start git clone - var settings = new CreateProcessSettings(); - settings.FileName = "git"; - settings.Arguments = $"clone {gitPath} \"{clonePath}\""; - settings.ShellExecute = false; - settings.LogOutput = true; + var settings = new CreateProcessSettings + { + FileName = "git", + Arguments = $"clone {gitPath} \"{clonePath}\"", + ShellExecute = false, + LogOutput = true, + }; Platform.CreateProcess(ref settings); } catch (Exception e) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed Git process. {e}"); + Editor.LogError($"Failed Git process. {e}"); return; } - - Debug.Logger.LogHandler.LogWrite(LogType.Info, $"Plugin project has been cloned."); + + Editor.Log($"Plugin project has been cloned."); // Find project config file. Could be different then what the user named the folder. var files = Directory.GetFiles(clonePath); @@ -401,9 +403,9 @@ namespace FlaxEditor.Windows Debug.Log(pluginProjectName); } } - + if (string.IsNullOrEmpty(pluginProjectName)) - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to find plugin project file to add to Project config. Please add manually."); + Editor.LogError($"Failed to find plugin project file to add to Project config. Please add manually."); else { await AddReferenceToProject(pluginName, pluginProjectName); @@ -450,7 +452,7 @@ namespace FlaxEditor.Windows nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; return; } - + var pluginPath = Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text); if (Directory.Exists(pluginPath)) { @@ -518,7 +520,7 @@ namespace FlaxEditor.Windows { if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text))) { - Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Cannot create plugin due to name conflict."); + Editor.LogWarning("Cannot create plugin due to name conflict."); return; } OnCreateButtonClicked(nameTextBox.Text, versionTextBox.Text, companyTextBox.Text); @@ -551,10 +553,10 @@ namespace FlaxEditor.Windows { if (string.IsNullOrEmpty(pluginName)) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to create plugin project due to no plugin name."); + Editor.LogError($"Failed to create plugin project due to no plugin name."); return; } - + var templateUrl = "https://github.com/FlaxEngine/ExamplePlugin/archive/refs/heads/master.zip"; var localTemplateFolderLocation = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache"); if (!Directory.Exists(localTemplateFolderLocation)) @@ -564,24 +566,24 @@ namespace FlaxEditor.Windows try { // Download example plugin - using (HttpClient client = new HttpClient()) + using (var client = new HttpClient()) { byte[] zipBytes = await client.GetByteArrayAsync(templateUrl); - await File.WriteAllBytesAsync(!File.Exists(localTemplatePath) ? Path.Combine(localTemplatePath) : Path.Combine(Editor.LocalCachePath, "TemplatePluginCache" , "TemplatePlugin1.zip"), zipBytes); - - Debug.Logger.LogHandler.LogWrite(LogType.Info, "Template for plugin project has downloaded"); + await File.WriteAllBytesAsync(!File.Exists(localTemplatePath) ? Path.Combine(localTemplatePath) : Path.Combine(Editor.LocalCachePath, "TemplatePluginCache", "TemplatePlugin1.zip"), zipBytes); + + Editor.Log("Template for plugin project has downloaded"); } } catch (Exception e) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to download template project. Trying to use local file. {e}"); + Editor.LogError($"Failed to download template project. Trying to use local file. {e}"); if (!File.Exists(localTemplatePath)) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to use local file. Does not exist."); + Editor.LogError($"Failed to use local file. Does not exist."); return; } } - + // Check if any changes in new downloaded file if (File.Exists(Path.Combine(Editor.LocalCachePath, "TemplatePluginCache", "TemplatePlugin1.zip"))) { @@ -622,7 +624,7 @@ namespace FlaxEditor.Windows File.Delete(localTemplatePath2); } } - + var extractPath = Path.Combine(Globals.ProjectFolder, "Plugins"); if (!Directory.Exists(extractPath)) Directory.CreateDirectory(extractPath); @@ -630,15 +632,12 @@ namespace FlaxEditor.Windows try { await Task.Run(() => ZipFile.ExtractToDirectory(localTemplatePath, extractPath)); - Debug.Logger.LogHandler.LogWrite(LogType.Info, "Template for plugin project successfully moved to project."); + Editor.Log("Template for plugin project successfully moved to project."); } catch (IOException e) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to add plugin to project. {e}"); + Editor.LogError($"Failed to add plugin to project. {e}"); } - var oldpluginPath = Path.Combine(extractPath, "ExamplePlugin-master"); - var newPluginPath = Path.Combine(extractPath , pluginName); - Directory.Move(oldpluginPath, newPluginPath); // Format plugin name into a valid name for code (C#/C++ typename) var pluginCodeName = Content.ScriptItem.FilterScriptName(pluginName); @@ -646,11 +645,14 @@ namespace FlaxEditor.Windows pluginCodeName = "MyPlugin"; Editor.Log($"Using plugin code type name: {pluginCodeName}"); + var oldPluginPath = Path.Combine(extractPath, "ExamplePlugin-master"); + var newPluginPath = Path.Combine(extractPath, pluginName); + Directory.Move(oldPluginPath, newPluginPath); var oldFlaxProjFile = Path.Combine(newPluginPath, "ExamplePlugin.flaxproj"); var newFlaxProjFile = Path.Combine(newPluginPath, $"{pluginName}.flaxproj"); File.Move(oldFlaxProjFile, newFlaxProjFile); - + var readme = Path.Combine(newPluginPath, "README.md"); if (File.Exists(readme)) File.Delete(readme); @@ -667,7 +669,7 @@ namespace FlaxEditor.Windows flaxPluginProjContents.Company = companyName; flaxPluginProjContents.GameTarget = $"{pluginCodeName}Target"; flaxPluginProjContents.EditorTarget = $"{pluginCodeName}EditorTarget"; - await File.WriteAllTextAsync(newFlaxProjFile, JsonSerializer.Serialize(flaxPluginProjContents, typeof(ProjectInfo))); + await File.WriteAllTextAsync(newFlaxProjFile, JsonSerializer.Serialize(flaxPluginProjContents, typeof(ProjectInfo)), Encoding.UTF8); // Format game settings var gameSettingsPath = Path.Combine(newPluginPath, "Content", "GameSettings.json"); @@ -703,17 +705,17 @@ namespace FlaxEditor.Windows var newName = directory.Replace("ExamplePlugin", pluginCodeName); Directory.Move(directory, newName); } - + // Rename targets var targetFiles = Directory.GetFiles(sourcePath); foreach (var file in targetFiles) { var fileText = await File.ReadAllTextAsync(file); - await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginCodeName)); + await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginCodeName), Encoding.UTF8); var newName = file.Replace("ExamplePlugin", pluginCodeName); File.Move(file, newName); } - Debug.Logger.LogHandler.LogWrite(LogType.Info, $"Plugin project {pluginName} has successfully been created."); + Editor.Log($"Plugin project {pluginName} has successfully been created."); await AddReferenceToProject(pluginName, pluginName); MessageBox.Show($"{pluginName} has been successfully created. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); @@ -738,11 +740,10 @@ namespace FlaxEditor.Windows references.Add(newReference); } flaxProjContents.References = references.ToArray(); - await File.WriteAllTextAsync(flaxProjPath, JsonSerializer.Serialize(flaxProjContents, typeof(ProjectInfo))); + await File.WriteAllTextAsync(flaxProjPath, JsonSerializer.Serialize(flaxProjContents, typeof(ProjectInfo)), Encoding.UTF8); } } - private void OnPluginsChanged() { List toRemove = null; From 9d494679ac3cb06baf5f414bb643abe620f29e9f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 22:42:48 +0200 Subject: [PATCH 82/93] Add formatting `MyPlugin.cs` for new plugin setup #1335 --- Source/Editor/Windows/PluginsWindow.cs | 29 ++++++++++++++++---------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index d53b64e22..c5ec02b31 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -346,7 +346,7 @@ namespace FlaxEditor.Windows { if (string.IsNullOrEmpty(gitPath)) { - Editor.LogError($"Failed to create plugin project due to no GIT path."); + Editor.LogError("Failed to create plugin project due to no GIT path."); return; } if (string.IsNullOrEmpty(pluginName)) @@ -369,7 +369,7 @@ namespace FlaxEditor.Windows Directory.CreateDirectory(clonePath); else { - Editor.LogError($"Plugin Name is already used. Pick a different Name."); + Editor.LogError("Plugin Name is already used. Pick a different Name."); return; } try @@ -390,7 +390,7 @@ namespace FlaxEditor.Windows return; } - Editor.Log($"Plugin project has been cloned."); + Editor.Log("Plugin project has been cloned."); // Find project config file. Could be different then what the user named the folder. var files = Directory.GetFiles(clonePath); @@ -405,7 +405,7 @@ namespace FlaxEditor.Windows } if (string.IsNullOrEmpty(pluginProjectName)) - Editor.LogError($"Failed to find plugin project file to add to Project config. Please add manually."); + Editor.LogError("Failed to find plugin project file to add to Project config. Please add manually."); else { await AddReferenceToProject(pluginName, pluginProjectName); @@ -553,7 +553,7 @@ namespace FlaxEditor.Windows { if (string.IsNullOrEmpty(pluginName)) { - Editor.LogError($"Failed to create plugin project due to no plugin name."); + Editor.LogError("Failed to create plugin project due to no plugin name."); return; } @@ -579,7 +579,7 @@ namespace FlaxEditor.Windows Editor.LogError($"Failed to download template project. Trying to use local file. {e}"); if (!File.Exists(localTemplatePath)) { - Editor.LogError($"Failed to use local file. Does not exist."); + Editor.LogError("Failed to use local file. Does not exist."); return; } } @@ -690,15 +690,24 @@ namespace FlaxEditor.Windows var files = Directory.GetFiles(directory); foreach (var file in files) { - if (file.Contains("MyPlugin.cs") || file.Contains("MyPluginEditor.cs")) + if (file.Contains("MyPluginEditor.cs")) { File.Delete(file); continue; } - var fileText = await File.ReadAllTextAsync(file); - await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginCodeName)); var fileName = Path.GetFileName(file).Replace("ExamplePlugin", pluginCodeName); + var fileText = await File.ReadAllTextAsync(file); + fileText = fileText.Replace("ExamplePlugin", pluginCodeName); + if (file.Contains("MyPlugin.cs")) + { + fileName = "ExamplePlugin.cs"; + fileText = fileText.Replace("MyPlugin", pluginCodeName); + fileText = fileText.Replace("My Plugin", pluginName); + fileText = fileText.Replace("Flax Engine", companyName); + fileText = fileText.Replace("new Version(1, 0)", $"new Version({pluginVersion.Trim().Replace(".", ", ")})"); + } + await File.WriteAllTextAsync(file, fileText); File.Move(file, Path.Combine(directory, fileName)); } @@ -856,13 +865,11 @@ namespace FlaxEditor.Windows { if (pluginType == null) return null; - foreach (var e in _entries.Keys) { if (e.GetType() == pluginType && _entries.ContainsKey(e)) return _entries[e]; } - return null; } From 639149b63b4a609802e984137fbb4ecd3728543f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Sep 2023 11:41:33 +0200 Subject: [PATCH 83/93] Fix crash when toggling `WheeledVehicle` active state #1351 --- .../Engine/Physics/Actors/WheeledVehicle.cpp | 24 +++++++++++++++---- Source/Engine/Physics/Actors/WheeledVehicle.h | 2 ++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.cpp b/Source/Engine/Physics/Actors/WheeledVehicle.cpp index 286f5b24e..afb5309ea 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.cpp +++ b/Source/Engine/Physics/Actors/WheeledVehicle.cpp @@ -223,9 +223,10 @@ void WheeledVehicle::Setup() return; // Release previous + void* scene = GetPhysicsScene()->GetPhysicsScene(); if (_vehicle) { - PhysicsBackend::RemoveVehicle(GetPhysicsScene()->GetPhysicsScene(), this); + PhysicsBackend::RemoveVehicle(scene, this); PhysicsBackend::DestroyVehicle(_vehicle, (int32)_driveTypeCurrent); _vehicle = nullptr; } @@ -236,7 +237,7 @@ void WheeledVehicle::Setup() if (!_vehicle) return; _driveTypeCurrent = _driveType; - PhysicsBackend::AddVehicle(GetPhysicsScene()->GetPhysicsScene(), this); + PhysicsBackend::AddVehicle(scene, this); PhysicsBackend::SetRigidDynamicActorSolverIterationCounts(_actor, 12, 4); #else LOG(Fatal, "Vehicles are not supported."); @@ -358,8 +359,23 @@ void WheeledVehicle::OnColliderChanged(Collider* c) { RigidBody::OnColliderChanged(c); - // Rebuild vehicle when someone adds/removed wheels - Setup(); + if (_useWheelsUpdates) + { + // Rebuild vehicle when someone adds/removed wheels + Setup(); + } +} + +void WheeledVehicle::OnActiveInTreeChanged() +{ + // Skip rebuilds from per-wheel OnColliderChanged when whole vehicle is toggled + _useWheelsUpdates = false; + RigidBody::OnActiveInTreeChanged(); + _useWheelsUpdates = true; + + // Perform whole rebuild when it gets activated + if (IsActiveInHierarchy()) + Setup(); } void WheeledVehicle::OnPhysicsSceneChanged(PhysicsScene* previous) diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.h b/Source/Engine/Physics/Actors/WheeledVehicle.h index b478006d9..3a329edc4 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.h +++ b/Source/Engine/Physics/Actors/WheeledVehicle.h @@ -329,6 +329,7 @@ private: DifferentialSettings _differential; GearboxSettings _gearbox; bool _fixInvalidForwardDir = false; // [Deprecated on 13.06.2023, expires on 13.06.2025] + bool _useWheelsUpdates = true; public: /// @@ -484,6 +485,7 @@ public: void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; void OnColliderChanged(Collider* c) override; + void OnActiveInTreeChanged() override; protected: void OnPhysicsSceneChanged(PhysicsScene* previous) override; From c10658c3fc927f885b4b3eea5860aa2f53877b75 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Sep 2023 13:50:22 +0200 Subject: [PATCH 84/93] Fix deserialization, add more docs and tweaks to #1345 --- .../Physics/PhysX/PhysicsBackendPhysX.cpp | 13 ++++--------- Source/Engine/Physics/Physics.cpp | 2 ++ Source/Engine/Physics/PhysicsSettings.h | 18 +++++++++--------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index f6c681d78..c8f3e0d50 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -889,37 +889,33 @@ void* PhysicsBackend::CreateScene(const PhysicsSettings& settings) PxSceneDesc sceneDesc(ToleranceScale); sceneDesc.gravity = C2P(settings.DefaultGravity); sceneDesc.flags |= PxSceneFlag::eENABLE_ACTIVE_ACTORS; + sceneDesc.flags |= PxSceneFlag::eENABLE_PCM; if (!settings.DisableCCD) - sceneDesc.flags |= PxSceneFlag::eENABLE_CCD | PxSceneFlag::eENABLE_PCM; + sceneDesc.flags |= PxSceneFlag::eENABLE_CCD; sceneDesc.simulationEventCallback = &scenePhysX->EventsCallback; sceneDesc.filterShader = FilterShader; sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity; - switch (settings.SolverType) { case PhysicsSolverType::ProjectedGaussSeidelIterativeSolver: sceneDesc.solverType = PxSolverType::ePGS; break; - case PhysicsSolverType::DefaultTemporalGaussSeidelSolver: + case PhysicsSolverType::TemporalGaussSeidelSolver: sceneDesc.solverType = PxSolverType::eTGS; break; - default: ; } - - sceneDesc.solverType = PxSolverType::eTGS; if (sceneDesc.cpuDispatcher == nullptr) { scenePhysX->CpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp(Platform::GetCPUInfo().ProcessorCoreCount - 1, 1, 4)); CHECK_INIT(scenePhysX->CpuDispatcher, "PxDefaultCpuDispatcherCreate failed!"); sceneDesc.cpuDispatcher = scenePhysX->CpuDispatcher; } - switch (settings.BroadPhaseType) { case PhysicsBroadPhaseType::SweepAndPrune: sceneDesc.broadPhaseType = PxBroadPhaseType::eSAP; break; - case PhysicsBroadPhaseType::MultiboxPruning: + case PhysicsBroadPhaseType::MultiBoxPruning: sceneDesc.broadPhaseType = PxBroadPhaseType::eMBP; break; case PhysicsBroadPhaseType::AutomaticBoxPruning: @@ -928,7 +924,6 @@ void* PhysicsBackend::CreateScene(const PhysicsSettings& settings) case PhysicsBroadPhaseType::ParallelAutomaticBoxPruning: sceneDesc.broadPhaseType = PxBroadPhaseType::ePABP; break; - default: ; } // Create scene diff --git a/Source/Engine/Physics/Physics.cpp b/Source/Engine/Physics/Physics.cpp index 1f9b242ad..7087b2e70 100644 --- a/Source/Engine/Physics/Physics.cpp +++ b/Source/Engine/Physics/Physics.cpp @@ -57,6 +57,8 @@ void PhysicsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* DESERIALIZE(FrictionCombineMode); DESERIALIZE(RestitutionCombineMode); DESERIALIZE(DisableCCD); + DESERIALIZE(BroadPhaseType); + DESERIALIZE(SolverType); DESERIALIZE(MaxDeltaTime); DESERIALIZE(EnableSubstepping); DESERIALIZE(SubstepDeltaTime); diff --git a/Source/Engine/Physics/PhysicsSettings.h b/Source/Engine/Physics/PhysicsSettings.h index 8fa2a7295..e43e96bc6 100644 --- a/Source/Engine/Physics/PhysicsSettings.h +++ b/Source/Engine/Physics/PhysicsSettings.h @@ -13,22 +13,22 @@ API_ENUM() enum class PhysicsBroadPhaseType { /// - /// Sweep and prune. + /// 3-axes sweep-and-prune. Good generic choice with great performance when many objects are sleeping. /// SweepAndPrune = 0, /// - /// Multi box pruning. + /// Alternative broad phase algorithm that does not suffer from the same performance issues as SAP when all objects are moving or when inserting large numbers of objects. /// - MultiboxPruning = 1, + MultiBoxPruning = 1, /// - /// Automatic box pruning. + /// Revisited implementation of MBP, which automatically manages broad-phase regions. /// AutomaticBoxPruning = 2, /// - /// Parallel automatic box pruning. + /// Parallel implementation of ABP. It can often be the fastest (CPU) broadphase, but it can use more memory than ABP. /// ParallelAutomaticBoxPruning = 3, }; @@ -40,14 +40,14 @@ API_ENUM() enum class PhysicsBroadPhaseType API_ENUM() enum class PhysicsSolverType { /// - /// Projected Gauss-Seidel iterative solver. + /// The iterative sequential impulse solver. /// ProjectedGaussSeidelIterativeSolver = 0, /// - /// Default Temporal Gauss-Seidel solver. + /// Non linear iterative solver. This kind of solver can lead to improved convergence and handle large mass ratios, long chains and jointed systems better. It is slightly more expensive than the default solver and can introduce more energy to correct joint and contact errors. /// - DefaultTemporalGaussSeidelSolver = 1, + TemporalGaussSeidelSolver = 1, }; /// @@ -91,7 +91,7 @@ public: /// Broad phase algorithm to use in the simulation. /// API_FIELD(Attributes="EditorOrder(71), EditorDisplay(\"Simulation\")") - PhysicsBroadPhaseType BroadPhaseType = PhysicsBroadPhaseType::SweepAndPrune; + PhysicsBroadPhaseType BroadPhaseType = PhysicsBroadPhaseType::ParallelAutomaticBoxPruning; /// /// The solver type to use in the simulation. From 7b4c8e391a901edb056d2ea5a301fc33eca66286 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Sep 2023 13:50:50 +0200 Subject: [PATCH 85/93] Minor optimization for clearing collider from simulation events cache --- .../Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp b/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp index d25beb2d3..4878750eb 100644 --- a/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp +++ b/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp @@ -11,8 +11,10 @@ namespace { - void ClearColliderFromCollection(PhysicsColliderActor* collider, Array& collection) + void ClearColliderFromCollection(const PhysicsColliderActor* collider, Array& collection) { + if (collection.IsEmpty()) + return; for (int32 i = 0; i < collection.Count(); i++) { if (collection[i].First == collider || collection[i].Second == collider) @@ -24,8 +26,10 @@ namespace } } - void ClearColliderFromCollection(PhysicsColliderActor* collider, SimulationEventCallback::CollisionsPool& collection) + void ClearColliderFromCollection(const PhysicsColliderActor* collider, SimulationEventCallback::CollisionsPool& collection) { + if (collection.IsEmpty()) + return; for (auto i = collection.Begin(); i.IsNotEnd(); ++i) { if (i->Key.First == collider || i->Key.Second == collider) From 44eba3fdae6ed321ebc0361064458e5a937a26e9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Sep 2023 13:54:01 +0200 Subject: [PATCH 86/93] Fix missing xml comment line from #1291 --- Source/Engine/Level/Actor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 5d66e7a16..a1f2dc78f 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -153,6 +153,7 @@ public: /// The tag to add. API_FUNCTION() void AddTagRecursive(const Tag& tag); + /// /// Removes a tag to the actor /// /// The tag to remove. From 9bbdf16623ee5e86b61c00f1c45e16b6c2ae44a6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Sep 2023 14:03:53 +0200 Subject: [PATCH 87/93] Fix potential content tree freeze when toggling engine/plugins files showing --- Source/Editor/Windows/ContentWindow.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index f4fb4afdc..0f3c12286 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -79,6 +79,7 @@ namespace FlaxEditor.Windows Editor.ContentDatabase.Engine.Visible = value; Editor.ContentDatabase.Engine.Folder.Visible = value; RefreshView(); + _tree.PerformLayout(); } } } @@ -99,6 +100,7 @@ namespace FlaxEditor.Windows project.Visible = value; project.Folder.Visible = value; RefreshView(); + _tree.PerformLayout(); } } } From 097d26f8cbfdc10d022f25686859a4850b17b23d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Sep 2023 14:21:27 +0200 Subject: [PATCH 88/93] Codestyle fix #1262 --- .../CustomEditors/Editors/InputEditor.cs | 75 ++++++------------- Source/Engine/UI/UICanvas.cs | 4 +- 2 files changed, 26 insertions(+), 53 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/InputEditor.cs b/Source/Editor/CustomEditors/Editors/InputEditor.cs index 3f8aaf093..416626e81 100644 --- a/Source/Editor/CustomEditors/Editors/InputEditor.cs +++ b/Source/Editor/CustomEditors/Editors/InputEditor.cs @@ -1,5 +1,5 @@ - -using System; +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + using System.Collections.Generic; using FlaxEngine; using FlaxEngine.GUI; @@ -13,31 +13,24 @@ namespace FlaxEditor.CustomEditors.Editors public class InputEventEditor : CustomEditor { private Dropdown _dropdown; - + /// public override DisplayStyle Style => DisplayStyle.Inline; - + /// public override void Initialize(LayoutElementsContainer layout) { var dropdownElement = layout.Custom(); _dropdown = dropdownElement.CustomControl; - var eventNames = new List(); + var names = new List(); foreach (var mapping in Input.ActionMappings) { - if (eventNames.Contains(mapping.Name)) - continue; - eventNames.Add(mapping.Name); + if (!names.Contains(mapping.Name)) + names.Add(mapping.Name); } - _dropdown.Items = eventNames; - if (Values[0] is InputEvent value) - { - if (eventNames.Contains(value.Name)) - { - _dropdown.SelectedItem = value.Name; - } - } - + _dropdown.Items = names; + if (Values[0] is InputEvent inputEvent && names.Contains(inputEvent.Name)) + _dropdown.SelectedItem = inputEvent.Name; _dropdown.SelectedIndexChanged += OnSelectedIndexChanged; } @@ -45,7 +38,7 @@ namespace FlaxEditor.CustomEditors.Editors { SetValue(new InputEvent(dropdown.SelectedItem)); } - + /// public override void Refresh() { @@ -53,17 +46,11 @@ namespace FlaxEditor.CustomEditors.Editors if (HasDifferentValues) { - } else { - if (Values[0] is InputEvent eventValue) - { - if (_dropdown.Items.Contains(eventValue.Name) && string.Equals(_dropdown.SelectedItem, eventValue.Name, StringComparison.Ordinal)) - { - _dropdown.SelectedItem = eventValue.Name; - } - } + if (Values[0] is InputEvent inputEvent && _dropdown.Items.Contains(inputEvent.Name)) + _dropdown.SelectedItem = inputEvent.Name; } } @@ -73,10 +60,9 @@ namespace FlaxEditor.CustomEditors.Editors if (_dropdown != null) _dropdown.SelectedIndexChanged -= OnSelectedIndexChanged; _dropdown = null; - base.Deinitialize(); } } - + /// /// Default implementation of the inspector used to edit input axis properties. /// @@ -84,7 +70,7 @@ namespace FlaxEditor.CustomEditors.Editors public class InputAxisEditor : CustomEditor { private Dropdown _dropdown; - + /// public override DisplayStyle Style => DisplayStyle.Inline; @@ -93,21 +79,15 @@ namespace FlaxEditor.CustomEditors.Editors { var dropdownElement = layout.Custom(); _dropdown = dropdownElement.CustomControl; - var axisNames = new List(); + var names = new List(); foreach (var mapping in Input.AxisMappings) { - if (axisNames.Contains(mapping.Name)) - continue; - axisNames.Add(mapping.Name); - } - _dropdown.Items = axisNames; - if (Values[0] is InputAxis value) - { - if (axisNames.Contains(value.Name)) - { - _dropdown.SelectedItem = value.Name; - } + if (!names.Contains(mapping.Name)) + names.Add(mapping.Name); } + _dropdown.Items = names; + if (Values[0] is InputAxis inputAxis && names.Contains(inputAxis.Name)) + _dropdown.SelectedItem = inputAxis.Name; _dropdown.SelectedIndexChanged += OnSelectedIndexChanged; } @@ -123,27 +103,20 @@ namespace FlaxEditor.CustomEditors.Editors if (HasDifferentValues) { - } else { - if (Values[0] is InputAxis axisValue) - { - if (_dropdown.Items.Contains(axisValue.Name) && string.Equals(_dropdown.SelectedItem, axisValue.Name, StringComparison.Ordinal)) - { - _dropdown.SelectedItem = axisValue.Name; - } - } + if (Values[0] is InputAxis inputAxis && _dropdown.Items.Contains(inputAxis.Name)) + _dropdown.SelectedItem = inputAxis.Name; } } - + /// protected override void Deinitialize() { if (_dropdown != null) _dropdown.SelectedIndexChanged -= OnSelectedIndexChanged; _dropdown = null; - base.Deinitialize(); } } } diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index 024fb8be0..8cb43af62 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -282,7 +282,7 @@ namespace FlaxEngine [EditorOrder(530), EditorDisplay("Navigation", "Navigate Left")] [Tooltip("The input action for performing UI navigation Left (from Input Settings).")] public InputEvent NavigateLeft { get; set; } = new InputEvent("NavigateLeft"); - + /// /// The input action for performing UI navigation Right (from Input Settings). /// @@ -775,7 +775,7 @@ namespace FlaxEngine jsonWriter.WriteValue(NavigateSubmit.Name); jsonWriter.WriteEndObject(); } - + jsonWriter.WriteEndObject(); } From 2a8e35d1ff2b4d79cc9d898922174c3450d51586 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Sep 2023 14:38:23 +0200 Subject: [PATCH 89/93] Format code #1292 --- .../CustomEditors/Dedicated/SplineEditor.cs | 167 ++++++++++++------ Source/Editor/SceneGraph/Actors/SplineNode.cs | 35 +--- Source/Engine/Level/Actors/Spline.cpp | 6 +- Source/Engine/Level/Actors/Spline.h | 1 + 4 files changed, 119 insertions(+), 90 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 2fe601108..a83a8ce35 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -22,8 +22,8 @@ namespace FlaxEditor.CustomEditors.Dedicated /// private struct UndoData { - public Spline spline; - public BezierCurve.Keyframe[] beforeKeyframes; + public Spline Spline; + public BezierCurve.Keyframe[] BeforeKeyframes; } /// @@ -82,16 +82,24 @@ namespace FlaxEditor.CustomEditors.Dedicated } /// - public override void OnMoveTangentIn(Spline spline, int index) { } + public override void OnMoveTangentIn(Spline spline, int index) + { + } /// - public override void OnMoveTangentOut(Spline spline, int index) { } + public override void OnMoveTangentOut(Spline spline, int index) + { + } /// - public override void OnSelectKeyframe(Spline spline, int index) { } + public override void OnSelectKeyframe(Spline spline, int index) + { + } /// - public override void OnSelectTangent(Spline spline, int index) { } + public override void OnSelectTangent(Spline spline, int index) + { + } } /// @@ -109,16 +117,24 @@ namespace FlaxEditor.CustomEditors.Dedicated } /// - public override void OnMoveTangentIn(Spline spline, int index) { } + public override void OnMoveTangentIn(Spline spline, int index) + { + } /// - public override void OnMoveTangentOut(Spline spline, int index) { } + public override void OnMoveTangentOut(Spline spline, int index) + { + } /// - public override void OnSelectKeyframe(Spline spline, int index) { } + public override void OnSelectKeyframe(Spline spline, int index) + { + } /// - public override void OnSelectTangent(Spline spline, int index) { } + public override void OnSelectTangent(Spline spline, int index) + { + } } /// @@ -139,7 +155,9 @@ namespace FlaxEditor.CustomEditors.Dedicated } /// - public override void OnSelectTangent(Spline selectedSpline, int index) { } + public override void OnSelectTangent(Spline selectedSpline, int index) + { + } /// public override void OnMoveTangentIn(Spline spline, int index) @@ -170,8 +188,10 @@ namespace FlaxEditor.CustomEditors.Dedicated // inverse of reference tangent otherTangent.Translation = -referenceTangent.Translation.Normalized * otherTangent.Translation.Length; - if (alignWithIn) keyframe.TangentOut = otherTangent; - if (!alignWithIn) keyframe.TangentIn = otherTangent; + if (alignWithIn) + keyframe.TangentOut = otherTangent; + if (!alignWithIn) + keyframe.TangentIn = otherTangent; spline.SetSplineKeyframe(index, keyframe); } @@ -191,16 +211,24 @@ namespace FlaxEditor.CustomEditors.Dedicated } /// - public override void OnMoveTangentIn(Spline spline, int index) { } + public override void OnMoveTangentIn(Spline spline, int index) + { + } /// - public override void OnMoveTangentOut(Spline spline, int index) { } + public override void OnMoveTangentOut(Spline spline, int index) + { + } /// - public override void OnSelectKeyframe(Spline spline, int index) { } + public override void OnSelectKeyframe(Spline spline, int index) + { + } /// - public override void OnSelectTangent(Spline spline, int index) { } + public override void OnSelectTangent(Spline spline, int index) + { + } } /// @@ -217,16 +245,24 @@ namespace FlaxEditor.CustomEditors.Dedicated } /// - public override void OnMoveTangentIn(Spline spline, int index) { } + public override void OnMoveTangentIn(Spline spline, int index) + { + } /// - public override void OnMoveTangentOut(Spline spline, int index) { } + public override void OnMoveTangentOut(Spline spline, int index) + { + } /// - public override void OnSelectKeyframe(Spline spline, int index) { } + public override void OnSelectKeyframe(Spline spline, int index) + { + } /// - public override void OnSelectTangent(Spline spline, int index) { } + public override void OnSelectTangent(Spline spline, int index) + { + } } private readonly Color HighlightedColor = FlaxEngine.GUI.Style.Current.BackgroundSelected; @@ -327,7 +363,8 @@ namespace FlaxEditor.CustomEditors.Dedicated _setLinearAllTangentsButton.Button.Clicked += EndEditSpline; _setSmoothAllTangentsButton.Button.Clicked += EndEditSpline; - if (_selectedSpline) _selectedSpline.SplineUpdated += OnSplineEdited; + if (_selectedSpline) + _selectedSpline.SplineUpdated += OnSplineEdited; SetSelectedTangentTypeAsCurrent(); SetEditButtonsColor(); @@ -338,7 +375,8 @@ namespace FlaxEditor.CustomEditors.Dedicated /// protected override void Deinitialize() { - if (_selectedSpline) _selectedSpline.SplineUpdated -= OnSplineEdited; + if (_selectedSpline) + _selectedSpline.SplineUpdated -= OnSplineEdited; } private void OnSplineEdited() @@ -376,27 +414,37 @@ 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; // 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 void SetSelectedTangentTypeAsCurrent() { - 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 (_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(); } private void SetEditButtonsColor() @@ -442,13 +490,15 @@ namespace FlaxEditor.CustomEditors.Dedicated private bool CanSetTangentSmoothIn() { - if (!CanEditTangent()) return false; + if (!CanEditTangent()) + return false; return _lastPointSelected.Index != 0; } private bool CanSetTangentSmoothOut() { - if (!CanEditTangent()) return false; + if (!CanEditTangent()) + return false; return _lastPointSelected.Index < _selectedSpline.SplinePointsCount - 1; } @@ -464,14 +514,16 @@ namespace FlaxEditor.CustomEditors.Dedicated private void SetModeLinear() { - if (_currentTangentMode is LinearTangentMode) return; + if (_currentTangentMode is LinearTangentMode) + return; _currentTangentMode = new LinearTangentMode(); _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); } private void SetModeFree() { - if (_currentTangentMode is FreeTangentMode) return; + if (_currentTangentMode is FreeTangentMode) + return; _currentTangentMode = new FreeTangentMode(); _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); SetEditButtonsColor(); @@ -479,21 +531,24 @@ namespace FlaxEditor.CustomEditors.Dedicated private void SetModeAligned() { - if (_currentTangentMode is AlignedTangentMode) return; + if (_currentTangentMode is AlignedTangentMode) + return; _currentTangentMode = new AlignedTangentMode(); _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); } private void SetModeSmoothIn() { - if (_currentTangentMode is SmoothInTangentMode) return; + if (_currentTangentMode is SmoothInTangentMode) + return; _currentTangentMode = new SmoothInTangentMode(); _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); } private void SetModeSmoothOut() { - if (_currentTangentMode is SmoothOutTangentMode) return; + if (_currentTangentMode is SmoothOutTangentMode) + return; _currentTangentMode = new SmoothOutTangentMode(); _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); } @@ -505,7 +560,8 @@ namespace FlaxEditor.CustomEditors.Dedicated { var currentSelected = Editor.Instance.SceneEditing.Selection[0]; - if (currentSelected == _selectedPoint) return; + if (currentSelected == _selectedPoint) + return; if (currentSelected is SplineNode.SplinePointNode) { _selectedPoint = currentSelected as SplineNode.SplinePointNode; @@ -546,8 +602,10 @@ namespace FlaxEditor.CustomEditors.Dedicated return; } - if (currentSelected == _selectedTangentIn) return; - if (currentSelected == _selectedTangentOut) return; + if (currentSelected == _selectedTangentIn) + return; + if (currentSelected == _selectedTangentOut) + return; var index = _lastPointSelected.Index; @@ -556,7 +614,7 @@ namespace FlaxEditor.CustomEditors.Dedicated _selectedTangentIn = currentSelected as SplineNode.SplinePointTangentNode; _selectedTangentOut = null; _currentTangentMode.OnSelectTangent(_selectedSpline, index); - + return; } @@ -587,9 +645,10 @@ namespace FlaxEditor.CustomEditors.Dedicated { if (Values[i] is Spline spline) { - splines.Add(new UndoData { - spline = spline, - beforeKeyframes = spline.SplineKeyframes.Clone() as BezierCurve.Keyframe[] + splines.Add(new UndoData + { + Spline = spline, + BeforeKeyframes = spline.SplineKeyframes.Clone() as BezierCurve.Keyframe[] }); } } @@ -609,9 +668,9 @@ namespace FlaxEditor.CustomEditors.Dedicated 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); + Presenter.Undo.AddAction(new EditSplineAction(_selectedSpline, splineUndoData.BeforeKeyframes)); + SplineNode.OnSplineEdited(splineUndoData.Spline); + Editor.Instance.Scene.MarkSceneEdited(splineUndoData.Spline.Scene); } } @@ -629,7 +688,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private static bool IsFreeTangentMode(Spline spline, int index) { - if (IsLinearTangentMode(spline, index) || + if (IsLinearTangentMode(spline, index) || IsAlignedTangentMode(spline, index) || IsSmoothInTangentMode(spline, index) || IsSmoothOutTangentMode(spline, index)) @@ -748,8 +807,10 @@ namespace FlaxEditor.CustomEditors.Dedicated var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplinePoint(index), 10f); // force smooth it's linear point - if (tangentInSize == 0f) tangentInSize = smoothRange; - if (tangentOutSize == 0f) tangentOutSize = smoothRange; + if (tangentInSize == 0f) + tangentInSize = smoothRange; + if (tangentOutSize == 0f) + tangentOutSize = smoothRange; // try get next / last keyframe var nextKeyframe = !isLastKeyframe ? spline.GetSplineKeyframe(index + 1) : keyframe; diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 9af736fac..1b35cdc35 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -12,7 +12,6 @@ using FlaxEditor.Modules; using FlaxEngine; using FlaxEngine.Json; using Object = FlaxEngine.Object; -using FlaxEditor.Viewport.Cameras; namespace FlaxEditor.SceneGraph.Actors { @@ -22,7 +21,7 @@ namespace FlaxEditor.SceneGraph.Actors [HideInEditor] public sealed class SplineNode : ActorNode { - public sealed class SplinePointNode : ActorChildNode + internal sealed class SplinePointNode : ActorChildNode { public unsafe SplinePointNode(SplineNode node, Guid id, int index) : base(node, id, index) @@ -224,7 +223,7 @@ namespace FlaxEditor.SceneGraph.Actors } } - public sealed class SplinePointTangentNode : ActorChildNode + internal sealed class SplinePointTangentNode : ActorChildNode { private SplineNode _node; private int _index; @@ -415,36 +414,6 @@ namespace FlaxEditor.SceneGraph.Actors return distance * nodeSize; } - public override void OnDebugDraw(ViewportDebugDrawData data) - { - DrawSpline((Spline)Actor, Color.White, Actor.Transform, true); - } - - private void DrawSpline(Spline spline, Color color, Transform transform, bool depthTest) - { - var count = spline.SplineKeyframes.Length; - if (count == 0) - return; - var keyframes = spline.SplineKeyframes; - var pointIndex = 0; - var prev = spline.GetSplineKeyframe(0); - var prevPos = transform.LocalToWorld(prev.Value.Translation); - var pointSize = NodeSizeByDistance(spline.GetSplinePoint(0), PointNodeSize); - DebugDraw.DrawWireSphere(new BoundingSphere(prevPos, pointSize), color, 0.0f, depthTest); - for (int i = 0; i < count; i++) - { - var next = keyframes[pointIndex]; - var nextPos = transform.LocalToWorld(next.Value.Translation); - var d = (next.Time - prev.Time) / 3.0f; - pointSize = NodeSizeByDistance(spline.GetSplinePoint(i), PointNodeSize); - DebugDraw.DrawWireSphere(new BoundingSphere(nextPos, pointSize), color, 0.0f, depthTest); - DebugDraw.DrawBezier(prevPos, prevPos + prev.TangentOut.Translation * d, nextPos + next.TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); - prev = next; - prevPos = nextPos; - pointIndex++; - } - } - /// public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal) { diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index dbd7ef045..194dbd9a9 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -484,8 +484,7 @@ namespace void Spline::OnDebugDraw() { - const Color color = GetSplineColor(); - DrawSpline(this, color.AlphaMultiplied(0.7f), _transform, true); + DrawSpline(this, GetSplineColor().AlphaMultiplied(0.7f), _transform, true); // Base Actor::OnDebugDraw(); @@ -493,8 +492,7 @@ void Spline::OnDebugDraw() void Spline::OnDebugDrawSelected() { - const Color color = GetSplineColor(); - DrawSpline(this, color.AlphaMultiplied(0.3f), _transform, false); + DrawSpline(this, Color::White, _transform, false); // Base Actor::OnDebugDrawSelected(); diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index ea3cf8569..cd022890b 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -354,6 +354,7 @@ public: protected: #if USE_EDITOR + // Spline color getter for debug drawing, can be overriden by custom spline types. virtual Color GetSplineColor() { return Color::White; From 3e1940c799eb62c2ce5df43f1f96e048011a8a5d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Sep 2023 22:18:26 +0200 Subject: [PATCH 90/93] Add new spline icons #1292 --- Content/Editor/IconsAtlas.flax | 4 ++-- Source/Editor/EditorIcons.cs | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Content/Editor/IconsAtlas.flax b/Content/Editor/IconsAtlas.flax index ae46fcf06..bbac95de6 100644 --- a/Content/Editor/IconsAtlas.flax +++ b/Content/Editor/IconsAtlas.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:745ce418c493445abde98aea91a34592b00c5c46b68cd7e61604a05b3043c76c -size 5608587 +oid sha256:0f43bf2050d47a1e4d6db35e5da66c2f999236939b9d962cd0fba56dcb171852 +size 5611913 diff --git a/Source/Editor/EditorIcons.cs b/Source/Editor/EditorIcons.cs index fb3c46a41..c16ec27cc 100644 --- a/Source/Editor/EditorIcons.cs +++ b/Source/Editor/EditorIcons.cs @@ -97,6 +97,10 @@ namespace FlaxEditor public SpriteHandle Build64; public SpriteHandle Add64; public SpriteHandle ShipIt64; + public SpriteHandle SplineFree64; + public SpriteHandle SplineLinear64; + public SpriteHandle SplineAligned64; + public SpriteHandle SplineSmoothIn64; // 96px public SpriteHandle Toolbox96; From dbd85fddfec6208ba09ad129598dc8aedb44159e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Sep 2023 22:24:15 +0200 Subject: [PATCH 91/93] Refactor Editor `Tab` controls system to allows customizing the tab header controls --- Source/Editor/GUI/Tabs/Tab.cs | 9 +++ Source/Editor/GUI/Tabs/Tabs.cs | 112 +++++++++++++++++++++------------ 2 files changed, 81 insertions(+), 40 deletions(-) diff --git a/Source/Editor/GUI/Tabs/Tab.cs b/Source/Editor/GUI/Tabs/Tab.cs index 67be0259b..3968cc17d 100644 --- a/Source/Editor/GUI/Tabs/Tab.cs +++ b/Source/Editor/GUI/Tabs/Tab.cs @@ -77,5 +77,14 @@ namespace FlaxEditor.GUI.Tabs { Deselected?.Invoke(this); } + + /// + /// Factory method for tabs header control (UI for the tabs strip to represent this tab). + /// + /// The tab header control. + public virtual Tabs.TabHeader CreateHeader() + { + return new Tabs.TabHeader((Tabs)Parent, this); + } } } diff --git a/Source/Editor/GUI/Tabs/Tabs.cs b/Source/Editor/GUI/Tabs/Tabs.cs index e01178c3d..f9bfd3f6c 100644 --- a/Source/Editor/GUI/Tabs/Tabs.cs +++ b/Source/Editor/GUI/Tabs/Tabs.cs @@ -24,11 +24,6 @@ namespace FlaxEditor.GUI.Tabs /// protected Tabs Tabs; - /// - /// The index of the tab. - /// - protected int Index; - /// /// The tab. /// @@ -38,21 +33,22 @@ namespace FlaxEditor.GUI.Tabs /// Initializes a new instance of the class. /// /// The tabs. - /// The tab index. /// The tab. - public TabHeader(Tabs tabs, int index, Tab tab) + public TabHeader(Tabs tabs, Tab tab) : base(Float2.Zero, tabs._tabsSize) { Tabs = tabs; - Index = index; Tab = tab; } /// public override bool OnMouseUp(Float2 location, MouseButton button) { - Tabs.SelectedTabIndex = Index; - Tabs.Focus(); + if (EnabledInHierarchy && Tab.Enabled) + { + Tabs.SelectedTab = Tab; + Tabs.Focus(); + } return true; } @@ -61,19 +57,20 @@ namespace FlaxEditor.GUI.Tabs { base.Draw(); - // Cache data var style = Style.Current; + var enabled = EnabledInHierarchy && Tab.EnabledInHierarchy; var tabRect = new Rectangle(Float2.Zero, Size); - bool isTabSelected = Tabs._selectedIndex == Index; - bool isMouseOverTab = IsMouseOver; var textOffset = Tabs._orientation == Orientation.Horizontal ? 0 : 8; // Draw bar - if (isTabSelected) + if (Tabs.SelectedTab == Tab) { + var color = style.BackgroundSelected; + if (!enabled) + color *= 0.6f; if (Tabs._orientation == Orientation.Horizontal) { - Render2D.FillRectangle(tabRect, style.BackgroundSelected); + Render2D.FillRectangle(tabRect, color); } else { @@ -84,10 +81,10 @@ namespace FlaxEditor.GUI.Tabs fillRect.Size.X -= lefEdgeWidth; fillRect.Location.X += lefEdgeWidth; Render2D.FillRectangle(fillRect, style.Background); - Render2D.FillRectangle(leftEdgeRect, style.BackgroundSelected); + Render2D.FillRectangle(leftEdgeRect, color); } } - else if (isMouseOverTab) + else if (IsMouseOver && enabled) { Render2D.FillRectangle(tabRect, style.BackgroundHighlighted); } @@ -131,20 +128,15 @@ namespace FlaxEditor.GUI.Tabs { base.PerformLayoutBeforeChildren(); - // Cache data - var tabsSize = Tabs._tabsSize; - var clientSize = GetClientArea(); - tabsSize = Float2.Min(tabsSize, clientSize.Size); - var tabRect = new Rectangle(Float2.Zero, tabsSize); - var tabStripOffset = Tabs._orientation == Orientation.Horizontal ? new Float2(tabsSize.X, 0) : new Float2(0, tabsSize.Y); - // Arrange tab header controls + var pos = Float2.Zero; + var sizeMask = Tabs._orientation == Orientation.Horizontal ? Float2.UnitX : Float2.UnitY; for (int i = 0; i < Children.Count; i++) { if (Children[i] is TabHeader tabHeader) { - tabHeader.Bounds = tabRect; - tabRect.Offset(tabStripOffset); + tabHeader.Location = pos; + pos += tabHeader.Size * sizeMask; } } } @@ -160,6 +152,11 @@ namespace FlaxEditor.GUI.Tabs /// protected Float2 _tabsSize; + /// + /// Automatic tab size based on the fill axis. + /// + protected bool _autoTabsSizeAuto; + /// /// The orientation. /// @@ -174,15 +171,31 @@ namespace FlaxEditor.GUI.Tabs set { _tabsSize = value; - for (int i = 0; i < TabsPanel.ChildrenCount; i++) + if (!_autoTabsSizeAuto) { - if (TabsPanel.Children[i] is TabHeader tabHeader) - tabHeader.Size = value; + for (int i = 0; i < TabsPanel.ChildrenCount; i++) + { + if (TabsPanel.Children[i] is TabHeader tabHeader) + tabHeader.Size = value; + } } PerformLayout(); } } + /// + /// Enables automatic tabs size to fill the space. + /// + public bool AutoTabsSize + { + get => _autoTabsSizeAuto; + set + { + _autoTabsSizeAuto = value; + PerformLayout(); + } + } + /// /// Gets or sets the orientation. /// @@ -339,14 +352,10 @@ namespace FlaxEditor.GUI.Tabs // Update tabs headers TabsPanel.DisposeChildren(); - int index = 0; for (int i = 0; i < Children.Count; i++) { if (Children[i] is Tab tab) - { - var tabHeader = new TabHeader(this, index++, tab); - TabsPanel.AddChild(tabHeader); - } + TabsPanel.AddChild(tab.CreateHeader()); } TabsPanel.IsLayoutLocked = wasLocked; @@ -371,23 +380,46 @@ namespace FlaxEditor.GUI.Tabs /// protected override void PerformLayoutBeforeChildren() { + var tabsSize = _tabsSize; + if (_autoTabsSizeAuto) + { + // Horizontal is default for Toolbox so tabs go to the right + int tabsCount = 0; + for (int i = 0; i < TabsPanel.ChildrenCount; i++) + { + if (TabsPanel.Children[i] is TabHeader) + tabsCount++; + } + if (tabsCount == 0) + tabsCount = 1; + if (_orientation == Orientation.Horizontal) + tabsSize.X = Width / tabsCount; + else + tabsSize.Y = Height / tabsCount; + for (int i = 0; i < TabsPanel.ChildrenCount; i++) + { + if (TabsPanel.Children[i] is TabHeader tabHeader) + tabHeader.Size = tabsSize; + } + } + // Fit the tabs panel - TabsPanel.Size = _orientation == Orientation.Horizontal ? new Float2(Width, _tabsSize.Y) : new Float2(_tabsSize.X, Height); + TabsPanel.Size = _orientation == Orientation.Horizontal + ? new Float2(Width, tabsSize.Y) + : new Float2(tabsSize.X, Height); // Hide all pages except selected one - var clientArea = _orientation == Orientation.Horizontal - ? new Rectangle(0, _tabsSize.Y, Width, Height - _tabsSize.Y) - : new Rectangle(_tabsSize.X, 0, Width - _tabsSize.X, Height); for (int i = 0; i < Children.Count; i++) { if (Children[i] is Tab tab) { - // Check if is selected or not if (i - 1 == _selectedIndex) { // Show and fit size tab.Visible = true; - tab.Bounds = clientArea; + tab.Bounds = _orientation == Orientation.Horizontal + ? new Rectangle(0, tabsSize.Y, Width, Height - tabsSize.Y) + : new Rectangle(tabsSize.X, 0, Width - tabsSize.X, Height); } else { From cb44c9370e29d4079e2bfe1b1249edc2880db2e0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Sep 2023 22:25:18 +0200 Subject: [PATCH 92/93] Refactor new spline editor to use icons via `Tabs` control #1292 --- .../CustomEditors/Dedicated/SplineEditor.cs | 446 +++++++++--------- 1 file changed, 213 insertions(+), 233 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index a83a8ce35..03d58ef33 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -1,12 +1,12 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System; using System.Collections.Generic; using FlaxEngine; -using FlaxEngine.GUI; using FlaxEditor.Actions; using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph.Actors; -using FlaxEditor.CustomEditors.Elements; +using FlaxEditor.GUI.Tabs; namespace FlaxEditor.CustomEditors.Dedicated { @@ -29,42 +29,52 @@ namespace FlaxEditor.CustomEditors.Dedicated /// /// Basis for creating tangent manipulation types for bezier curves. /// - private abstract class EditTangentOptionBase + private class EditTangentOptionBase { /// /// 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); + public virtual 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); + public virtual 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); + public virtual 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); + public virtual 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); + public virtual void OnMoveTangentOut(Spline spline, int index) + { + } } /// @@ -80,26 +90,6 @@ namespace FlaxEditor.CustomEditors.Dedicated SetPointSmooth(spline, index); } } - - /// - 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) - { - } } /// @@ -115,26 +105,6 @@ namespace FlaxEditor.CustomEditors.Dedicated // change the selection to tangent parent (a spline point / keyframe) SetSelectSplinePointNode(spline, index); } - - /// - 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) - { - } } /// @@ -154,11 +124,6 @@ namespace FlaxEditor.CustomEditors.Dedicated SmoothIfNotAligned(spline, index); } - /// - public override void OnSelectTangent(Spline selectedSpline, int index) - { - } - /// public override void OnMoveTangentIn(Spline spline, int index) { @@ -209,26 +174,6 @@ namespace FlaxEditor.CustomEditors.Dedicated SetTangentSmoothIn(spline, index); SetSelectTangentIn(spline, index); } - - /// - 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) - { - } } /// @@ -243,41 +188,78 @@ namespace FlaxEditor.CustomEditors.Dedicated SetTangentSmoothOut(spline, index); SetSelectTangentOut(spline, index); } + } - /// - public override void OnMoveTangentIn(Spline spline, int index) + private sealed class IconTab : Tab + { + private sealed class IconTabHeader : Tabs.TabHeader { + public IconTabHeader(Tabs tabs, Tab tab) + : base(tabs, tab) + { + } + + public override bool OnMouseUp(Float2 location, MouseButton button) + { + if (EnabledInHierarchy && Tab.Enabled) + ((IconTab)Tab)._action(); + return true; + } + + public override void Draw() + { + base.Draw(); + + var tab = (IconTab)Tab; + var enabled = EnabledInHierarchy && tab.EnabledInHierarchy; + var style = FlaxEngine.GUI.Style.Current; + var size = Size; + var textHeight = 16.0f; + var iconSize = size.Y - textHeight; + var iconRect = new Rectangle((Width - iconSize) / 2, 0, iconSize, iconSize); + if (tab._mirrorIcon) + { + iconRect.Location.X += iconRect.Size.X; + iconRect.Size.X *= -1; + } + var color = style.Foreground; + if (!enabled) + color *= 0.6f; + Render2D.DrawSprite(tab._customIcon, iconRect, color); + Render2D.DrawText(style.FontMedium, tab._customText, new Rectangle(0, iconSize, size.X, textHeight), color, TextAlignment.Center, TextAlignment.Center); + } } - /// - public override void OnMoveTangentOut(Spline spline, int index) + private readonly Action _action; + private readonly string _customText; + private readonly SpriteHandle _customIcon; + private readonly bool _mirrorIcon; + + public IconTab(Action action, string text, SpriteHandle icon, bool mirrorIcon = false) + : base(string.Empty, SpriteHandle.Invalid) { + _action = action; + _customText = text; + _customIcon = icon; + _mirrorIcon = mirrorIcon; } - /// - public override void OnSelectKeyframe(Spline spline, int index) - { - } - - /// - public override void OnSelectTangent(Spline spline, int index) + public override Tabs.TabHeader CreateHeader() { + return new IconTabHeader((Tabs)Parent, this); } } - 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; - private ButtonElement _linearTangentButton; - private ButtonElement _alignedTangentButton; - private ButtonElement _smoothInTangentButton; - private ButtonElement _smoothOutTangentButton; - private ButtonElement _setLinearAllTangentsButton; - private ButtonElement _setSmoothAllTangentsButton; + private Tabs _selectedPointsTabs, _allPointsTabs; + private Tab _freeTangentTab; + private Tab _linearTangentTab; + private Tab _alignedTangentTab; + private Tab _smoothInTangentTab; + private Tab _smoothOutTangentTab; + private Tab _setLinearAllTangentsTab; + private Tab _setSmoothAllTangentsTab; private bool _tanInChanged; private bool _tanOutChanged; @@ -289,7 +271,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private SplineNode.SplinePointTangentNode _selectedTangentIn; private SplineNode.SplinePointTangentNode _selectedTangentOut; - private UndoData[] selectedSplinesUndoData; + private UndoData[] _selectedSplinesUndoData; private bool HasPointSelected => _selectedPoint != null; private bool HasTangentsSelected => _selectedTangentIn != null || _selectedTangentOut != null; @@ -300,76 +282,47 @@ namespace FlaxEditor.CustomEditors.Dedicated base.Initialize(layout); _currentTangentMode = new FreeTangentMode(); + if (Values.HasDifferentTypes || !(Values[0] is Spline spline)) + return; + _selectedSpline = spline; - if (Values.HasDifferentTypes == false) + layout.Space(10); + var tabSize = 46; + var icons = Editor.Instance.Icons; + + layout.Header("Selected spline point"); + _selectedPointsTabs = new Tabs { - _selectedSpline = !Values.HasDifferentValues && Values[0] is Spline ? (Spline)Values[0] : null; + Height = tabSize, + TabsSize = new Float2(tabSize), + AutoTabsSize = true, + Parent = layout.ContainerControl, + }; + _linearTangentTab = _selectedPointsTabs.AddTab(new IconTab(OnSetSelectedLinear, "Linear", icons.SplineLinear64)); + _freeTangentTab = _selectedPointsTabs.AddTab(new IconTab(OnSetSelectedFree, "Free", icons.SplineFree64)); + _alignedTangentTab = _selectedPointsTabs.AddTab(new IconTab(OnSetSelectedAligned, "Aligned", icons.SplineAligned64)); + _smoothInTangentTab = _selectedPointsTabs.AddTab(new IconTab(OnSetSelectedSmoothIn, "Smooth In", icons.SplineSmoothIn64)); + _smoothOutTangentTab = _selectedPointsTabs.AddTab(new IconTab(OnSetSelectedSmoothOut, "Smooth Out", icons.SplineSmoothIn64, true)); + _selectedPointsTabs.SelectedTabIndex = -1; - layout.Space(10); + layout.Header("All spline points"); + _allPointsTabs = new Tabs + { + Height = tabSize, + TabsSize = new Float2(tabSize), + AutoTabsSize = true, + Parent = layout.ContainerControl, + }; + _setLinearAllTangentsTab = _allPointsTabs.AddTab(new IconTab(OnSetTangentsLinear, "Set Linear Tangents", icons.SplineLinear64)); + _setSmoothAllTangentsTab = _allPointsTabs.AddTab(new IconTab(OnSetTangentsSmooth, "Set Smooth Tangents", icons.SplineAligned64)); + _allPointsTabs.SelectedTabIndex = -1; - layout.Header("Selected spline point"); - var selectedPointsGrid = layout.CustomContainer(); - selectedPointsGrid.CustomControl.SlotsHorizontally = 3; - selectedPointsGrid.CustomControl.SlotsVertically = 2; + if (_selectedSpline) + _selectedSpline.SplineUpdated += OnSplineEdited; - selectedPointsGrid.Control.Size *= new Float2(1, 2); - - _linearTangentButton = selectedPointsGrid.Button("Linear"); - _freeTangentButton = selectedPointsGrid.Button("Free"); - _alignedTangentButton = selectedPointsGrid.Button("Aligned"); - _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 += 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; - - _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; - - SetSelectedTangentTypeAsCurrent(); - SetEditButtonsColor(); - SetEditButtonsEnabled(); - } + SetSelectedTangentTypeAsCurrent(); + UpdateEditTabsSelection(); + UpdateButtonsEnabled(); } /// @@ -381,8 +334,8 @@ namespace FlaxEditor.CustomEditors.Dedicated private void OnSplineEdited() { - SetEditButtonsColor(); - SetEditButtonsEnabled(); + UpdateEditTabsSelection(); + UpdateButtonsEnabled(); } /// @@ -394,10 +347,8 @@ namespace FlaxEditor.CustomEditors.Dedicated UpdateSelectedTangent(); if (!CanEditTangent()) - { return; - } - + var index = _lastPointSelected.Index; var currentTangentInPosition = _selectedSpline.GetSplineLocalTangent(index, true).Translation; var currentTangentOutPosition = _selectedSpline.GetSplineLocalTangent(index, false).Translation; @@ -422,7 +373,7 @@ namespace FlaxEditor.CustomEditors.Dedicated currentTangentInPosition = _selectedSpline.GetSplineLocalTangent(index, true).Translation; currentTangentOutPosition = _selectedSpline.GetSplineLocalTangent(index, false).Translation; - // update last tangents position after changes + // Update last tangents position after changes if (_selectedSpline) _lastTanInPos = currentTangentInPosition; if (_selectedSpline) @@ -447,15 +398,12 @@ namespace FlaxEditor.CustomEditors.Dedicated SetModeFree(); } - private void SetEditButtonsColor() + private void UpdateEditTabsSelection() { - if (!CanEditTangent()) + _selectedPointsTabs.Enabled = CanEditTangent(); + if (!_selectedPointsTabs.Enabled) { - _linearTangentButton.Button.BackgroundColor = NormalButtonColor; - _freeTangentButton.Button.BackgroundColor = NormalButtonColor; - _alignedTangentButton.Button.BackgroundColor = NormalButtonColor; - _smoothInTangentButton.Button.BackgroundColor = NormalButtonColor; - _smoothOutTangentButton.Button.BackgroundColor = NormalButtonColor; + _selectedPointsTabs.SelectedTabIndex = -1; return; } @@ -465,22 +413,29 @@ namespace FlaxEditor.CustomEditors.Dedicated var isSmoothIn = _currentTangentMode is SmoothInTangentMode; var isSmoothOut = _currentTangentMode is SmoothOutTangentMode; - _linearTangentButton.Button.BackgroundColor = isLinear ? SelectedButtonColor : NormalButtonColor; - _freeTangentButton.Button.BackgroundColor = isFree ? SelectedButtonColor : NormalButtonColor; - _alignedTangentButton.Button.BackgroundColor = isAligned ? SelectedButtonColor : NormalButtonColor; - _smoothInTangentButton.Button.BackgroundColor = isSmoothIn ? SelectedButtonColor : NormalButtonColor; - _smoothOutTangentButton.Button.BackgroundColor = isSmoothOut ? SelectedButtonColor : NormalButtonColor; + if (isFree) + _selectedPointsTabs.SelectedTab = _freeTangentTab; + else if (isLinear) + _selectedPointsTabs.SelectedTab = _linearTangentTab; + else if (isAligned) + _selectedPointsTabs.SelectedTab = _alignedTangentTab; + else if (isSmoothIn) + _selectedPointsTabs.SelectedTab = _smoothInTangentTab; + else if (isSmoothOut) + _selectedPointsTabs.SelectedTab = _smoothOutTangentTab; + else + _selectedPointsTabs.SelectedTabIndex = -1; } - private void SetEditButtonsEnabled() + private void UpdateButtonsEnabled() { - _linearTangentButton.Button.Enabled = CanEditTangent(); - _freeTangentButton.Button.Enabled = CanEditTangent(); - _alignedTangentButton.Button.Enabled = CanEditTangent(); - _smoothInTangentButton.Button.Enabled = CanSetTangentSmoothIn(); - _smoothOutTangentButton.Button.Enabled = CanSetTangentSmoothOut(); - _setLinearAllTangentsButton.Button.Enabled = CanSetAllTangentsLinear(); - _setSmoothAllTangentsButton.Button.Enabled = CanSetAllTangentsSmooth(); + _linearTangentTab.Enabled = CanEditTangent(); + _freeTangentTab.Enabled = CanEditTangent(); + _alignedTangentTab.Enabled = CanEditTangent(); + _smoothInTangentTab.Enabled = CanSetTangentSmoothIn(); + _smoothOutTangentTab.Enabled = CanSetTangentSmoothOut(); + _setLinearAllTangentsTab.Enabled = CanSetAllTangentsLinear(); + _setSmoothAllTangentsTab.Enabled = CanSetAllTangentsSmooth(); } private bool CanEditTangent() @@ -526,7 +481,6 @@ namespace FlaxEditor.CustomEditors.Dedicated return; _currentTangentMode = new FreeTangentMode(); _currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index); - SetEditButtonsColor(); } private void SetModeAligned() @@ -562,9 +516,9 @@ namespace FlaxEditor.CustomEditors.Dedicated if (currentSelected == _selectedPoint) return; - if (currentSelected is SplineNode.SplinePointNode) + if (currentSelected is SplineNode.SplinePointNode selectedPoint) { - _selectedPoint = currentSelected as SplineNode.SplinePointNode; + _selectedPoint = selectedPoint; _lastPointSelected = _selectedPoint; _currentTangentMode.OnSelectKeyframe(_selectedSpline, _lastPointSelected.Index); } @@ -579,8 +533,8 @@ namespace FlaxEditor.CustomEditors.Dedicated } SetSelectedTangentTypeAsCurrent(); - SetEditButtonsColor(); - SetEditButtonsEnabled(); + UpdateEditTabsSelection(); + UpdateButtonsEnabled(); } private void UpdateSelectedTangent() @@ -595,7 +549,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var currentSelected = Editor.Instance.SceneEditing.Selection[0]; - if (currentSelected is not SplineNode.SplinePointTangentNode) + if (currentSelected is not SplineNode.SplinePointTangentNode selectedPoint) { _selectedTangentIn = null; _selectedTangentOut = null; @@ -611,7 +565,7 @@ namespace FlaxEditor.CustomEditors.Dedicated if (currentSelected.Transform == _selectedSpline.GetSplineTangent(index, true)) { - _selectedTangentIn = currentSelected as SplineNode.SplinePointTangentNode; + _selectedTangentIn = selectedPoint; _selectedTangentOut = null; _currentTangentMode.OnSelectTangent(_selectedSpline, index); @@ -620,7 +574,7 @@ namespace FlaxEditor.CustomEditors.Dedicated if (currentSelected.Transform == _selectedSpline.GetSplineTangent(index, false)) { - _selectedTangentOut = currentSelected as SplineNode.SplinePointTangentNode; + _selectedTangentOut = selectedPoint; _selectedTangentIn = null; _currentTangentMode.OnSelectTangent(_selectedSpline, index); return; @@ -632,58 +586,91 @@ namespace FlaxEditor.CustomEditors.Dedicated private void StartEditSpline() { - var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled; - - if (!enableUndo) + if (Presenter.Undo != null && Presenter.Undo.Enabled) { - return; - } - - var splines = new List(); - - for (int i = 0; i < Values.Count; i++) - { - if (Values[i] is Spline spline) + // Capture 'before' state for undo + var splines = new List(); + for (int i = 0; i < Values.Count; i++) { - splines.Add(new UndoData + if (Values[i] is Spline spline) { - Spline = spline, - BeforeKeyframes = spline.SplineKeyframes.Clone() as BezierCurve.Keyframe[] - }); + splines.Add(new UndoData + { + Spline = spline, + BeforeKeyframes = spline.SplineKeyframes.Clone() as BezierCurve.Keyframe[] + }); + } } + _selectedSplinesUndoData = splines.ToArray(); } - - selectedSplinesUndoData = splines.ToArray(); } private void EndEditSpline() { - var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled; + // Update buttons state + UpdateEditTabsSelection(); - if (!enableUndo) + if (Presenter.Undo != null && Presenter.Undo.Enabled) { - return; + // Add undo + foreach (var splineUndoData in _selectedSplinesUndoData) + { + Presenter.Undo.AddAction(new EditSplineAction(_selectedSpline, splineUndoData.BeforeKeyframes)); + SplineNode.OnSplineEdited(splineUndoData.Spline); + Editor.Instance.Scene.MarkSceneEdited(splineUndoData.Spline.Scene); + } } + } - 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 OnSetSelectedLinear() + { + StartEditSpline(); + SetModeLinear(); + EndEditSpline(); + } + + private void OnSetSelectedFree() + { + StartEditSpline(); + SetModeFree(); + EndEditSpline(); + } + + private void OnSetSelectedAligned() + { + StartEditSpline(); + SetModeAligned(); + EndEditSpline(); + } + + private void OnSetSelectedSmoothIn() + { + StartEditSpline(); + SetModeSmoothIn(); + EndEditSpline(); + } + + private void OnSetSelectedSmoothOut() + { + StartEditSpline(); + SetModeSmoothOut(); + EndEditSpline(); } private void OnSetTangentsLinear() { + StartEditSpline(); _selectedSpline.SetTangentsLinear(); _selectedSpline.UpdateSpline(); + EndEditSpline(); } private void OnSetTangentsSmooth() { + StartEditSpline(); _selectedSpline.SetTangentsSmooth(); _selectedSpline.UpdateSpline(); + EndEditSpline(); } private static bool IsFreeTangentMode(Spline spline, int index) @@ -695,7 +682,6 @@ namespace FlaxEditor.CustomEditors.Dedicated { return false; } - return true; } @@ -710,18 +696,12 @@ namespace FlaxEditor.CustomEditors.Dedicated var keyframe = spline.GetSplineKeyframe(index); var tangentIn = keyframe.TangentIn.Translation; var tangentOut = keyframe.TangentOut.Translation; - if (tangentIn.Length == 0 || tangentOut.Length == 0) - { return false; - } var angleBetweenTwoTangents = Vector3.Dot(tangentIn.Normalized, tangentOut.Normalized); - if (angleBetweenTwoTangents < -0.99f) - { return true; - } return false; } @@ -761,7 +741,7 @@ namespace FlaxEditor.CustomEditors.Dedicated { var keyframe = spline.GetSplineKeyframe(index); - // auto smooth tangent if's linear + // Auto smooth tangent if's linear if (keyframe.TangentIn.Translation.Length == 0) { var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplineTangent(index, false).Translation, 10f); @@ -780,7 +760,7 @@ namespace FlaxEditor.CustomEditors.Dedicated { var keyframe = spline.GetSplineKeyframe(index); - // auto smooth tangent if's linear + // Auto smooth tangent if's linear if (keyframe.TangentOut.Translation.Length == 0) { var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplineTangent(index, false).Translation, 10f); @@ -806,13 +786,13 @@ namespace FlaxEditor.CustomEditors.Dedicated var isFirstKeyframe = index <= 0; var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplinePoint(index), 10f); - // force smooth it's linear point + // Force smooth it's linear point if (tangentInSize == 0f) tangentInSize = smoothRange; if (tangentOutSize == 0f) tangentOutSize = smoothRange; - // try get next / last keyframe + // Try get next / last keyframe var nextKeyframe = !isLastKeyframe ? spline.GetSplineKeyframe(index + 1) : keyframe; var previousKeyframe = !isFirstKeyframe ? spline.GetSplineKeyframe(index - 1) : keyframe; From 722133165b21757bbe643f7baae9a5c3442b4681 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Sep 2023 13:18:52 +0200 Subject: [PATCH 93/93] Fix marshalling `Array` as `object[]` (C# codegen error) --- Source/Engine/Engine/NativeInterop.Managed.cs | 1 - .../Engine/NativeInterop.Marshallers.cs | 71 +++++++++++++++++++ .../Bindings/BindingsGenerator.CSharp.cs | 6 +- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs index e4dc449bc..f36798ece 100644 --- a/Source/Engine/Engine/NativeInterop.Managed.cs +++ b/Source/Engine/Engine/NativeInterop.Managed.cs @@ -371,7 +371,6 @@ namespace FlaxEngine.Interop { if (handle == IntPtr.Zero) return; - ManagedHandlePool.FreeHandle(handle); handle = IntPtr.Zero; } diff --git a/Source/Engine/Engine/NativeInterop.Marshallers.cs b/Source/Engine/Engine/NativeInterop.Marshallers.cs index 3ea8a5faa..74d6e6bad 100644 --- a/Source/Engine/Engine/NativeInterop.Marshallers.cs +++ b/Source/Engine/Engine/NativeInterop.Marshallers.cs @@ -183,6 +183,9 @@ namespace FlaxEngine.Interop public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged); } +#if FLAX_EDITOR + [HideInEditor] +#endif [CustomMarshaller(typeof(Array), MarshalMode.ManagedToUnmanagedIn, typeof(SystemArrayMarshaller.ManagedToNative))] [CustomMarshaller(typeof(Array), MarshalMode.UnmanagedToManagedOut, typeof(SystemArrayMarshaller.ManagedToNative))] [CustomMarshaller(typeof(Array), MarshalMode.ManagedToUnmanagedOut, typeof(SystemArrayMarshaller.NativeToManaged))] @@ -251,6 +254,74 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif + [CustomMarshaller(typeof(object[]), MarshalMode.ManagedToUnmanagedIn, typeof(SystemObjectArrayMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(object[]), MarshalMode.UnmanagedToManagedOut, typeof(SystemObjectArrayMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(object[]), MarshalMode.ManagedToUnmanagedOut, typeof(SystemObjectArrayMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(object[]), MarshalMode.UnmanagedToManagedIn, typeof(SystemObjectArrayMarshaller.NativeToManaged))] + public static unsafe class SystemObjectArrayMarshaller + { +#if FLAX_EDITOR + [HideInEditor] +#endif + public struct ManagedToNative + { + ManagedHandle handle; + + public void FromManaged(object[] managed) + { + if (managed != null) + { + var managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(managed); + handle = ManagedHandle.Alloc(managedArray, GCHandleType.Weak); + } + } + + public IntPtr ToUnmanaged() + { + return ManagedHandle.ToIntPtr(handle); + } + + public void Free() + { + handle.Free(); + } + } + +#if FLAX_EDITOR + [HideInEditor] +#endif + public struct NativeToManaged + { + ManagedHandle handle; + + public void FromUnmanaged(IntPtr unmanaged) + { + if (unmanaged == IntPtr.Zero) + return; + handle = ManagedHandle.FromIntPtr(unmanaged); + } + + public object[] ToManaged() + { + object[] result = null; + if (handle.IsAllocated) + { + var managedArray = Unsafe.As(handle.Target); + result = NativeInterop.GCHandleArrayToManagedArray(managedArray); + } + return result; + } + + public void Free() + { + handle.Free(); + } + } + } + #if FLAX_EDITOR [HideInEditor] #endif diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index f399dd67c..b825f5ac1 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -521,7 +521,7 @@ namespace Flax.Build.Bindings returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemTypeMarshaller))"; else if (returnValueType == "CultureInfo") returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.CultureInfoMarshaller))"; - else if (functionInfo.ReturnType.Type == "Variant") + else if (functionInfo.ReturnType.Type == "Variant") // object returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ManagedHandleMarshaller))"; else if (FindApiTypeInfo(buildData, functionInfo.ReturnType, caller)?.IsInterface ?? false) { @@ -530,6 +530,8 @@ namespace Flax.Build.Bindings } else if (functionInfo.ReturnType.Type == "MonoArray" || functionInfo.ReturnType.Type == "MArray") returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemArrayMarshaller))"; + else if (returnValueType == "object[]") + returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))"; else if (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer" || functionInfo.ReturnType.Type == "BytesContainer" || returnNativeType == "Array") returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = nameof(__returnCount))"; else if (functionInfo.ReturnType.Type == "Dictionary") @@ -579,6 +581,8 @@ namespace Flax.Build.Bindings parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ManagedHandleMarshaller))"; else if (parameterInfo.Type.Type == "MonoArray" || parameterInfo.Type.Type == "MArray") parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemArrayMarshaller))"; + else if (nativeType == "object[]") + parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))"; else if (parameterInfo.Type.Type == "Array" && parameterInfo.Type.GenericArgs.Count > 0 && parameterInfo.Type.GenericArgs[0].Type == "bool") parameterMarshalType = $"MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == $"__{parameterInfo.Name}Count"))})"; else if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer" || nativeType == "Array")