From d389348260f3b3b56a18bb82de188ed14aa7c9a3 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Tue, 12 Sep 2023 21:38:26 +0300 Subject: [PATCH 01/33] Add View Layers button & Reset/Disable/Copy/Paste buttons to View Flags/Debug View & Camera RenderFlags/RenderView addition --- Source/Editor/Viewport/EditorViewport.cs | 120 +++++++++++++++++++++++ Source/Engine/Graphics/RenderView.cpp | 2 + Source/Engine/Graphics/RenderView.cs | 2 + Source/Engine/Level/Actors/Camera.cpp | 4 + Source/Engine/Level/Actors/Camera.h | 13 +++ 5 files changed, 141 insertions(+) diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index ad7e06ba5..f5ef17ecb 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -2,6 +2,7 @@ using System; using System.Linq; +using FlaxEditor.Content.Settings; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEditor.Options; @@ -9,6 +10,9 @@ using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Widgets; using FlaxEngine; using FlaxEngine.GUI; +using FlaxEngine.Utilities; +using Newtonsoft.Json; +using JsonSerializer = FlaxEngine.Json.JsonSerializer; namespace FlaxEditor.Viewport { @@ -483,10 +487,89 @@ namespace FlaxEditor.Viewport } } + // View Layers + { + var viewLayers = ViewWidgetButtonMenu.AddChildMenu("View Layers").ContextMenu; + viewLayers.AddButton("Copy layers", () => Clipboard.Text = JsonSerializer.Serialize(Task.View.RenderLayersMask)); + viewLayers.AddButton("Paste layers", () => + { + object obj; + try + { + obj = JsonConvert.DeserializeObject(Clipboard.Text, typeof(LayersMask), JsonSerializer.Settings); + } + catch + { + obj = null; + } + if (obj != null && obj is LayersMask layer) + { + RenderView view = Task.View; + view.RenderLayersMask = layer; + Task.View = view; + } + }); + viewLayers.AddButton("Reset layers", () => + { + RenderView view = Task.View; + view.RenderLayersMask = LayersMask.Default; + Task.View = view; + }).Icon = Editor.Instance.Icons.Rotate32; + viewLayers.AddButton("Disable layers", () => + { + RenderView view = Task.View; + view.RenderLayersMask = new LayersMask(0); + Task.View = view; + }).Icon = Editor.Instance.Icons.Rotate32; + viewLayers.AddSeparator(); + var layers = LayersAndTagsSettings.GetCurrentLayers(); + if (layers != null && layers.Length > 0) + { + for (int i = 0; i < layers.Length; i++) + { + var layer = layers[i]; + var button = viewLayers.AddButton(layer); + button.CloseMenuOnClick = false; + button.Tag = 1 << i; + } + } + viewLayers.ButtonClicked += button => + { + if (button.Tag != null) + { + int layerIndex = (int)button.Tag; + LayersMask mask = new LayersMask(layerIndex); + RenderView view = Task.View; + view.RenderLayersMask ^= mask; + Task.View = view; + button.Icon = (Task.View.RenderLayersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + } + }; + viewLayers.VisibleChanged += WidgetViewLayersShowHide; + } + // View Flags { var viewFlags = ViewWidgetButtonMenu.AddChildMenu("View Flags").ContextMenu; + viewFlags.AddButton("Copy flags", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewFlags)); + viewFlags.AddButton("Paste flags", () => + { + object obj; + try + { + obj = JsonConvert.DeserializeObject(Clipboard.Text, typeof(ViewFlags), JsonSerializer.Settings); + } + catch + { + obj = null; + } + if (obj != null && obj is ViewFlags flags) + { + Task.ViewFlags = flags; + } + }); viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32; + viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32; viewFlags.AddSeparator(); for (int i = 0; i < EditorViewportViewFlagsValues.Length; i++) { @@ -510,6 +593,24 @@ namespace FlaxEditor.Viewport // Debug View { var debugView = ViewWidgetButtonMenu.AddChildMenu("Debug View").ContextMenu; + debugView.AddButton("Copy view", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewMode)); + debugView.AddButton("Paste view", () => + { + object obj; + try + { + obj = JsonConvert.DeserializeObject(Clipboard.Text, typeof(ViewMode), JsonSerializer.Settings); + } + catch + { + obj = null; + } + if (obj != null && obj is ViewMode mode) + { + Task.ViewMode = mode; + } + }); + debugView.AddSeparator(); for (int i = 0; i < EditorViewportViewModeValues.Length; i++) { ref var v = ref EditorViewportViewModeValues[i]; @@ -1561,6 +1662,25 @@ namespace FlaxEditor.Viewport } } + private void WidgetViewLayersShowHide(Control cm) + { + if (cm.Visible == false) + return; + + var ccm = (ContextMenu)cm; + foreach (var e in ccm.Items) + { + if (e is ContextMenuButton b && b != null && b.Tag != null) + { + int layerIndex = (int)b.Tag; + LayersMask mask = new LayersMask(layerIndex); + b.Icon = (Task.View.RenderLayersMask & mask) != 0 + ? Style.Current.CheckBoxTick + : SpriteHandle.Invalid; + } + } + } + private float GetGamepadAxis(GamepadAxis axis) { var value = FlaxEngine.Input.GetGamepadAxis(InputGamepadIndex.All, axis); diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp index 46766c34d..c84351bdc 100644 --- a/Source/Engine/Graphics/RenderView.cpp +++ b/Source/Engine/Graphics/RenderView.cpp @@ -192,6 +192,8 @@ void RenderView::CopyFrom(Camera* camera, Viewport* viewport) Frustum.GetInvMatrix(IVP); CullingFrustum = Frustum; RenderLayersMask = camera->RenderLayersMask; + Flags = camera->RenderFlags; + Mode = camera->RenderView; } void RenderView::GetWorldMatrix(const Transform& transform, Matrix& world) const diff --git a/Source/Engine/Graphics/RenderView.cs b/Source/Engine/Graphics/RenderView.cs index 091661514..0d35bd360 100644 --- a/Source/Engine/Graphics/RenderView.cs +++ b/Source/Engine/Graphics/RenderView.cs @@ -102,6 +102,8 @@ namespace FlaxEngine NonJitteredProjection = Projection; TemporalAAJitter = Float4.Zero; RenderLayersMask = camera.RenderLayersMask; + Flags = camera.RenderFlags; + Mode = camera.RenderView; UpdateCachedData(); } diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp index f2625ca3b..2a5c44a7e 100644 --- a/Source/Engine/Level/Actors/Camera.cpp +++ b/Source/Engine/Level/Actors/Camera.cpp @@ -415,6 +415,8 @@ void Camera::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_MEMBER(Far, _far); SERIALIZE_MEMBER(OrthoScale, _orthoScale); SERIALIZE(RenderLayersMask); + SERIALIZE(RenderFlags); + SERIALIZE(RenderView); } void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) @@ -429,6 +431,8 @@ void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier DESERIALIZE_MEMBER(Far, _far); DESERIALIZE_MEMBER(OrthoScale, _orthoScale); DESERIALIZE(RenderLayersMask); + DESERIALIZE(RenderFlags); + DESERIALIZE(RenderView); } void Camera::OnEnable() diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h index 5a0bece33..21f13665f 100644 --- a/Source/Engine/Level/Actors/Camera.h +++ b/Source/Engine/Level/Actors/Camera.h @@ -7,6 +7,7 @@ #include "Engine/Core/Math/Viewport.h" #include "Engine/Core/Math/Ray.h" #include "Engine/Core/Types/LayersMask.h" +#include "Engine/Graphics/Enums.h" #include "Engine/Scripting/ScriptingObjectReference.h" #if USE_EDITOR #include "Engine/Content/AssetReference.h" @@ -134,6 +135,18 @@ public: API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Camera\")") LayersMask RenderLayersMask; + /// + /// Frame rendering flags used to switch between graphics features for this camera. + /// + API_FIELD(Attributes = "EditorOrder(110), EditorDisplay(\"Camera\")") + ViewFlags RenderFlags = ViewFlags::DefaultGame; + + /// + /// Describes frame rendering modes for this camera. + /// + API_FIELD(Attributes = "EditorOrder(120), EditorDisplay(\"Camera\")") + ViewMode RenderView = ViewMode::Default; + public: /// /// Projects the point from 3D world-space to game window coordinates (in screen pixels for default viewport calculated from ). From 5eff51d47eb8881433cfa56282347ea71642da71 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 25 Sep 2023 11:46:37 -0500 Subject: [PATCH 02/33] Add button to position camera to editor viewport view. --- Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index e48e59f80..4575cc157 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -45,6 +45,15 @@ namespace FlaxEditor.Windows if (hasSthSelected) { contextMenu.AddButton(Editor.Windows.EditWin.IsPilotActorActive ? "Stop piloting actor" : "Pilot actor", Editor.UI.PilotActor); + // Position camera to viewport view + if (Editor.SceneEditing.Selection[0] is ActorNode a && a.Actor is Camera c && isSingleActorSelected) + { + contextMenu.AddButton("Position Camera to View", () => + { + c.Position = Editor.Windows.EditWin.Viewport.ViewPosition; + c.Orientation = Editor.Windows.EditWin.Viewport.ViewOrientation; + }); + } } contextMenu.AddSeparator(); From fd3a5c55e11863d4c222ce7a8dff010290fbab15 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 25 Sep 2023 11:50:04 -0500 Subject: [PATCH 03/33] Simplify code --- Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index 4575cc157..80f58be0a 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -50,8 +50,9 @@ namespace FlaxEditor.Windows { contextMenu.AddButton("Position Camera to View", () => { - c.Position = Editor.Windows.EditWin.Viewport.ViewPosition; - c.Orientation = Editor.Windows.EditWin.Viewport.ViewOrientation; + var viewport = Editor.Windows.EditWin.Viewport; + c.Position = viewport.ViewPosition; + c.Orientation = viewport.ViewOrientation; }); } } From 9667848c96649c59026ab6646dd2ab247191fe29 Mon Sep 17 00:00:00 2001 From: ontrigger Date: Mon, 25 Sep 2023 23:28:29 +0300 Subject: [PATCH 04/33] make scrolling duration based and deprecate SmoothingScale --- Source/Engine/UI/GUI/Panels/ScrollBar.cs | 92 +++++++++++++++++++----- 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs index a51a1df40..5dfcf5e53 100644 --- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs +++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs @@ -23,9 +23,9 @@ namespace FlaxEngine.GUI // Scrolling - private float _clickChange = 20, _scrollChange = 100; + private float _clickChange = 20, _scrollChange = 50; private float _minimum, _maximum = 100; - private float _value, _targetValue; + private float _startValue, _value, _targetValue; private readonly Orientation _orientation; private RootControl.UpdateDelegate _update; @@ -42,6 +42,7 @@ namespace FlaxEngine.GUI // Smoothing private float _thumbOpacity = DefaultMinimumOpacity; + private float _scrollAnimationProgress = 0f; /// /// Gets the orientation. @@ -60,13 +61,35 @@ namespace FlaxEngine.GUI /// /// Gets or sets the value smoothing scale (0 to not use it). + /// [Deprecated on 26.09.2023, expires on 26.09.2025] + /// This property is deprecated, use instead + /// Set this to >= 0 to use legacy behavior /// - public float SmoothingScale { get; set; } = 0.6f; + [Obsolete("Deprecated in 1.7")] + public float SmoothingScale { get; set; } = -1f; + + /// + /// The maximum time it takes to animate from current to target scroll position + /// + public float ScrollAnimationDuration { get; set; } = 0.18f; /// /// Gets a value indicating whether use scroll value smoothing. /// - public bool UseSmoothing => !Mathf.IsZero(SmoothingScale); + public bool UseSmoothing + { + get + { + if (!EnableSmoothing || Mathf.IsZero(SmoothingScale)) { return false; } + + return SmoothingScale > 0 || SmoothingScale < 0 && !Mathf.IsZero(ScrollAnimationDuration); + } + } + + /// + /// Enables scroll smoothing + /// + public bool EnableSmoothing { get; set; } = true; /// /// Gets or sets the minimum value. @@ -112,11 +135,15 @@ namespace FlaxEngine.GUI if (!Mathf.NearEqual(value, _targetValue)) { _targetValue = value; + _startValue = _value; + _scrollAnimationProgress = 0f; // Check if skip smoothing if (!UseSmoothing) { _value = value; + _startValue = value; + _scrollAnimationProgress = 1f; OnValueChanged(); } else @@ -208,7 +235,8 @@ namespace FlaxEngine.GUI { if (!Mathf.NearEqual(_value, _targetValue)) { - _value = _targetValue; + _value = _targetValue = _startValue; + _scrollAnimationProgress = 0f; SetUpdate(ref _update, null); OnValueChanged(); } @@ -274,7 +302,8 @@ namespace FlaxEngine.GUI internal void Reset() { - _value = _targetValue = 0; + _value = _targetValue = _startValue = 0; + _scrollAnimationProgress = 0f; } /// @@ -296,22 +325,49 @@ namespace FlaxEngine.GUI _thumbOpacity = isDeltaSlow ? targetOpacity : Mathf.Lerp(_thumbOpacity, targetOpacity, deltaTime * 10.0f); bool needUpdate = Mathf.Abs(_thumbOpacity - targetOpacity) > 0.001f; - // Ensure scroll bar is visible - if (Visible) + // Ensure scroll bar is visible and smoothing is required + if (Visible && Mathf.Abs(_targetValue - _value) > 0.01f) { - // Value smoothing - if (Mathf.Abs(_targetValue - _value) > 0.01f) + // Interpolate or not if running slow + float value; + if (!isDeltaSlow && UseSmoothing) { - // Interpolate or not if running slow - float value; - if (!isDeltaSlow && UseSmoothing) + // use legacy behavior + if (SmoothingScale >= 0) + { value = Mathf.Lerp(_value, _targetValue, deltaTime * 20.0f * SmoothingScale); + } else - value = _targetValue; - _value = value; - OnValueChanged(); - needUpdate = true; + { + // percentage of scroll from 0 to _scrollChange, ex. 0.5 at _scrollChange / 2 + var minScrollChangeRatio = Mathf.Clamp(Mathf.Abs(_targetValue - _startValue) / _scrollChange, 0, 1); + + // shorten the duration if we scrolled less than _scrollChange + var actualDuration = ScrollAnimationDuration * minScrollChangeRatio; + var step = deltaTime / actualDuration; + + var progress = _scrollAnimationProgress; + progress = Mathf.Clamp(progress + step, 0, 1); + + Debug.Log($"why {progress} {step} {minScrollChangeRatio}"); + + // https://easings.net/#easeInOutQuad + var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2); + value = Mathf.Lerp(_startValue, _targetValue, easedProgress); + + _scrollAnimationProgress = progress; + } } + else + { + value = _targetValue; + _startValue = _targetValue; + _scrollAnimationProgress = 0f; + } + + _value = value; + OnValueChanged(); + needUpdate = true; } // End updating if all animations are done @@ -381,7 +437,7 @@ namespace FlaxEngine.GUI if (ThumbEnabled) { // Scroll - Value = _value - delta * _scrollChange; + Value = _targetValue - delta * _scrollChange; } return true; } From 6a62dac49b39af711131c7c02b1d4de20a22006c Mon Sep 17 00:00:00 2001 From: ontrigger Date: Mon, 25 Sep 2023 23:29:29 +0300 Subject: [PATCH 05/33] remove usages of SmoothingScale across the codebase --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 4 +--- Source/Editor/Windows/Search/ContentFinder.cs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index bae62556e..8fb775b6e 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -671,9 +671,7 @@ namespace FlaxEditor.Surface.ContextMenu SelectedItem = previousSelectedItem; // Scroll into view (without smoothing) - _panel1.VScrollBar.SmoothingScale = 0; - _panel1.ScrollViewTo(SelectedItem); - _panel1.VScrollBar.SmoothingScale = 1; + _panel1.ScrollViewTo(SelectedItem, true); } return true; } diff --git a/Source/Editor/Windows/Search/ContentFinder.cs b/Source/Editor/Windows/Search/ContentFinder.cs index 05e1d6792..e19d212b8 100644 --- a/Source/Editor/Windows/Search/ContentFinder.cs +++ b/Source/Editor/Windows/Search/ContentFinder.cs @@ -54,9 +54,7 @@ namespace FlaxEditor.Windows.Search _selectedItem.BackgroundColor = Style.Current.BackgroundSelected; if (_matchedItems.Count > VisibleItemCount) { - _resultPanel.VScrollBar.SmoothingScale = 0; - _resultPanel.ScrollViewTo(_selectedItem); - _resultPanel.VScrollBar.SmoothingScale = 1; + _resultPanel.ScrollViewTo(_selectedItem, true); } } } From a18d2d0ebabce8603b14d7469497a311a5b9cd92 Mon Sep 17 00:00:00 2001 From: ontrigger Date: Mon, 25 Sep 2023 23:35:52 +0300 Subject: [PATCH 06/33] remove logging --- Source/Engine/UI/GUI/Panels/ScrollBar.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs index 5dfcf5e53..b2f1202b3 100644 --- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs +++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs @@ -349,8 +349,6 @@ namespace FlaxEngine.GUI var progress = _scrollAnimationProgress; progress = Mathf.Clamp(progress + step, 0, 1); - Debug.Log($"why {progress} {step} {minScrollChangeRatio}"); - // https://easings.net/#easeInOutQuad var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2); value = Mathf.Lerp(_startValue, _targetValue, easedProgress); From 056d8d5b6cb2103faefd4365cbd198fa845ff7f7 Mon Sep 17 00:00:00 2001 From: ontrigger Date: Mon, 25 Sep 2023 23:46:16 +0300 Subject: [PATCH 07/33] don't animate when scrollbar is held --- Source/Engine/UI/GUI/Panels/ScrollBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs index b2f1202b3..c6f70fc2c 100644 --- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs +++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs @@ -425,7 +425,7 @@ namespace FlaxEngine.GUI float mousePosition = _orientation == Orientation.Vertical ? slidePosition.Y : slidePosition.X; float percentage = (mousePosition - _mouseOffset - _thumbSize / 2) / (TrackSize - _thumbSize); - Value = _minimum + percentage * (_maximum - _minimum); + TargetValue = _minimum + percentage * (_maximum - _minimum); } } From 2d37e59e732f8b3b5388989ef319ec223a9f3c9f Mon Sep 17 00:00:00 2001 From: ontrigger Date: Tue, 26 Sep 2023 00:51:44 +0300 Subject: [PATCH 08/33] completely remove SmoothingScale --- Source/Engine/UI/GUI/Panels/ScrollBar.cs | 49 ++++++------------------ 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs index c6f70fc2c..db47976a8 100644 --- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs +++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs @@ -59,15 +59,6 @@ namespace FlaxEngine.GUI /// public float TrackThickness { get; set; } = 2.0f; - /// - /// Gets or sets the value smoothing scale (0 to not use it). - /// [Deprecated on 26.09.2023, expires on 26.09.2025] - /// This property is deprecated, use instead - /// Set this to >= 0 to use legacy behavior - /// - [Obsolete("Deprecated in 1.7")] - public float SmoothingScale { get; set; } = -1f; - /// /// The maximum time it takes to animate from current to target scroll position /// @@ -76,15 +67,7 @@ namespace FlaxEngine.GUI /// /// Gets a value indicating whether use scroll value smoothing. /// - public bool UseSmoothing - { - get - { - if (!EnableSmoothing || Mathf.IsZero(SmoothingScale)) { return false; } - - return SmoothingScale > 0 || SmoothingScale < 0 && !Mathf.IsZero(ScrollAnimationDuration); - } - } + public bool UseSmoothing => EnableSmoothing && !Mathf.IsZero(ScrollAnimationDuration); /// /// Enables scroll smoothing @@ -332,29 +315,21 @@ namespace FlaxEngine.GUI float value; if (!isDeltaSlow && UseSmoothing) { - // use legacy behavior - if (SmoothingScale >= 0) - { - value = Mathf.Lerp(_value, _targetValue, deltaTime * 20.0f * SmoothingScale); - } - else - { - // percentage of scroll from 0 to _scrollChange, ex. 0.5 at _scrollChange / 2 - var minScrollChangeRatio = Mathf.Clamp(Mathf.Abs(_targetValue - _startValue) / _scrollChange, 0, 1); + // percentage of scroll from 0 to _scrollChange, ex. 0.5 at _scrollChange / 2 + var minScrollChangeRatio = Mathf.Clamp(Mathf.Abs(_targetValue - _startValue) / _scrollChange, 0, 1); - // shorten the duration if we scrolled less than _scrollChange - var actualDuration = ScrollAnimationDuration * minScrollChangeRatio; - var step = deltaTime / actualDuration; + // shorten the duration if we scrolled less than _scrollChange + var actualDuration = ScrollAnimationDuration * minScrollChangeRatio; + var step = deltaTime / actualDuration; - var progress = _scrollAnimationProgress; - progress = Mathf.Clamp(progress + step, 0, 1); + var progress = _scrollAnimationProgress; + progress = Mathf.Clamp(progress + step, 0, 1); - // https://easings.net/#easeInOutQuad - var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2); - value = Mathf.Lerp(_startValue, _targetValue, easedProgress); + // https://easings.net/#easeInOutQuad + var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2); + value = Mathf.Lerp(_startValue, _targetValue, easedProgress); - _scrollAnimationProgress = progress; - } + _scrollAnimationProgress = progress; } else { From 249ded3d1fb13d56f89873b63201b8f9b6b02cc5 Mon Sep 17 00:00:00 2001 From: ontrigger Date: Wed, 27 Sep 2023 22:12:21 +0300 Subject: [PATCH 09/33] fix incorrect link --- Source/Engine/UI/GUI/Panels/ScrollBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs index db47976a8..abde37b37 100644 --- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs +++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs @@ -325,7 +325,7 @@ namespace FlaxEngine.GUI var progress = _scrollAnimationProgress; progress = Mathf.Clamp(progress + step, 0, 1); - // https://easings.net/#easeInOutQuad + // https://easings.net/#easeOutSine var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2); value = Mathf.Lerp(_startValue, _targetValue, easedProgress); From 299f68ebfea2f0655c5f8b680895b2e6d9990634 Mon Sep 17 00:00:00 2001 From: MZ Date: Thu, 28 Sep 2023 16:00:31 +1000 Subject: [PATCH 10/33] Vert color deduplication check --- .../Engine/Graphics/Models/ModelData.Tool.cpp | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Source/Engine/Graphics/Models/ModelData.Tool.cpp b/Source/Engine/Graphics/Models/ModelData.Tool.cpp index 89a227eb7..099e448f2 100644 --- a/Source/Engine/Graphics/Models/ModelData.Tool.cpp +++ b/Source/Engine/Graphics/Models/ModelData.Tool.cpp @@ -154,8 +154,8 @@ bool MeshData::GenerateLightmapUVs() int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int32 searchRange, const Array& mapping #if USE_SPARIAL_SORT - , const Assimp::SpatialSort& spatialSort - , std::vector& sparialSortCache + , const Assimp::SpatialSort& spatialSort + , std::vector& sparialSortCache #endif ) { @@ -171,6 +171,8 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3 const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero; const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero; const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero; + const Color vColor = mesh.Colors.HasItems() ? mesh.Colors[vertexIndex] : Color::Black; // Assuming Color::Black as a default color + const int32 end = startIndex + searchRange; for (size_t i = 0; i < sparialSortCache.size(); i++) @@ -179,17 +181,18 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3 if (v < startIndex || v >= end) continue; #else - const Float3 vPosition = mesh.Positions[vertexIndex]; - const Float2 vUV = mesh.UVs.HasItems() ? mesh.UVs[vertexIndex] : Float2::Zero; - const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero; - const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero; - const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero; - const int32 end = startIndex + searchRange; + const Float3 vPosition = mesh.Positions[vertexIndex]; + const Float2 vUV = mesh.UVs.HasItems() ? mesh.UVs[vertexIndex] : Float2::Zero; + const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero; + const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero; + const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero; + const Color vColor = mesh.Colors.HasItems() ? mesh.Colors[vertexIndex] : Color::Black; // Assuming Color::Black as a default color + const int32 end = startIndex + searchRange; - for (int32 v = startIndex; v < end; v++) - { - if (!Float3::NearEqual(vPosition, mesh.Positions[v])) - continue; + for (int32 v = startIndex; v < end; v++) + { + if (!Float3::NearEqual(vPosition, mesh.Positions[v])) + continue; #endif if (mapping[v] == INVALID_INDEX) continue; @@ -201,6 +204,8 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3 continue; if (mesh.LightmapUVs.HasItems() && (vLightmapUV - mesh.LightmapUVs[v]).LengthSquared() > uvEpsSqr) continue; + if (mesh.Colors.HasItems() && vColor != mesh.Colors[v]) + continue; // TODO: check more components? return v; From 2cc6e809585cb349ba77d72894a9401e5c54a660 Mon Sep 17 00:00:00 2001 From: MZ Date: Thu, 28 Sep 2023 16:07:38 +1000 Subject: [PATCH 11/33] Checked my working. --- .../Engine/Graphics/Models/ModelData.Tool.cpp | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Source/Engine/Graphics/Models/ModelData.Tool.cpp b/Source/Engine/Graphics/Models/ModelData.Tool.cpp index 099e448f2..d061d0745 100644 --- a/Source/Engine/Graphics/Models/ModelData.Tool.cpp +++ b/Source/Engine/Graphics/Models/ModelData.Tool.cpp @@ -154,8 +154,8 @@ bool MeshData::GenerateLightmapUVs() int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int32 searchRange, const Array& mapping #if USE_SPARIAL_SORT - , const Assimp::SpatialSort& spatialSort - , std::vector& sparialSortCache + , const Assimp::SpatialSort& spatialSort + , std::vector& sparialSortCache #endif ) { @@ -181,18 +181,19 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3 if (v < startIndex || v >= end) continue; #else - const Float3 vPosition = mesh.Positions[vertexIndex]; - const Float2 vUV = mesh.UVs.HasItems() ? mesh.UVs[vertexIndex] : Float2::Zero; - const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero; - const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero; - const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero; + const Float3 vPosition = mesh.Positions[vertexIndex]; + const Float2 vUV = mesh.UVs.HasItems() ? mesh.UVs[vertexIndex] : Float2::Zero; + const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero; + const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero; + const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero; const Color vColor = mesh.Colors.HasItems() ? mesh.Colors[vertexIndex] : Color::Black; // Assuming Color::Black as a default color - const int32 end = startIndex + searchRange; - for (int32 v = startIndex; v < end; v++) - { - if (!Float3::NearEqual(vPosition, mesh.Positions[v])) - continue; + const int32 end = startIndex + searchRange; + + for (int32 v = startIndex; v < end; v++) + { + if (!Float3::NearEqual(vPosition, mesh.Positions[v])) + continue; #endif if (mapping[v] == INVALID_INDEX) continue; From 137951201d52b25146cd7850877f846f97323e48 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 28 Sep 2023 14:09:08 -0500 Subject: [PATCH 12/33] Refactor to use Camera Node. --- .../SceneGraph/Actors/AnimatedModelNode.cs | 5 +++-- Source/Editor/SceneGraph/Actors/CameraNode.cs | 21 +++++++++++++++++++ Source/Editor/SceneGraph/Actors/SceneNode.cs | 5 +++-- Source/Editor/SceneGraph/Actors/SplineNode.cs | 13 ++++++------ .../SceneGraph/Actors/StaticModelNode.cs | 5 +++-- Source/Editor/SceneGraph/SceneGraphNode.cs | 3 ++- .../Windows/Assets/PrefabWindow.Hierarchy.cs | 2 +- .../Windows/SceneTreeWindow.ContextMenu.cs | 12 +---------- 8 files changed, 41 insertions(+), 25 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs b/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs index 604907ae4..0bf7f6afc 100644 --- a/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs +++ b/Source/Editor/SceneGraph/Actors/AnimatedModelNode.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.Windows; using FlaxEngine; namespace FlaxEditor.SceneGraph.Actors @@ -22,9 +23,9 @@ namespace FlaxEditor.SceneGraph.Actors } /// - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { - base.OnContextMenu(contextMenu); + base.OnContextMenu(contextMenu, window); var actor = (AnimatedModel)Actor; if (actor && actor.SkinnedModel) diff --git a/Source/Editor/SceneGraph/Actors/CameraNode.cs b/Source/Editor/SceneGraph/Actors/CameraNode.cs index 400a2e06f..4b05ca430 100644 --- a/Source/Editor/SceneGraph/Actors/CameraNode.cs +++ b/Source/Editor/SceneGraph/Actors/CameraNode.cs @@ -6,6 +6,8 @@ using Real = System.Double; using Real = System.Single; #endif +using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.Windows; using FlaxEngine; namespace FlaxEditor.SceneGraph.Actors @@ -23,6 +25,25 @@ namespace FlaxEditor.SceneGraph.Actors { } + /// + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) + { + base.OnContextMenu(contextMenu,window); + if (window is not SceneTreeWindow win) + return; + var button = new ContextMenuButton(contextMenu, "Move Camera to View"); + button.Parent = contextMenu.ItemsContainer; + contextMenu.ItemsContainer.Children.Remove(button); + contextMenu.ItemsContainer.Children.Insert(4, button); + button.Clicked += () => + { + var c = Actor as Camera; + var viewport = Editor.Instance.Windows.EditWin.Viewport; + c.Position = viewport.ViewPosition; + c.Orientation = viewport.ViewOrientation; + }; + } + /// public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal) { diff --git a/Source/Editor/SceneGraph/Actors/SceneNode.cs b/Source/Editor/SceneGraph/Actors/SceneNode.cs index 0405e4fdf..c2ee3645a 100644 --- a/Source/Editor/SceneGraph/Actors/SceneNode.cs +++ b/Source/Editor/SceneGraph/Actors/SceneNode.cs @@ -3,6 +3,7 @@ using System.IO; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.SceneGraph.GUI; +using FlaxEditor.Windows; using FlaxEngine; namespace FlaxEditor.SceneGraph.Actors @@ -65,7 +66,7 @@ namespace FlaxEditor.SceneGraph.Actors public override SceneNode ParentScene => this; /// - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { contextMenu.AddSeparator(); var path = Scene.Path; @@ -80,7 +81,7 @@ namespace FlaxEditor.SceneGraph.Actors if (Level.ScenesCount > 1) contextMenu.AddButton("Unload all but this scene", OnUnloadAllButSelectedScene).LinkTooltip("Unloads all of the active scenes except for the selected scene.").Enabled = Editor.Instance.StateMachine.CurrentState.CanChangeScene; - base.OnContextMenu(contextMenu); + base.OnContextMenu(contextMenu, window); } private void OnSelect() diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 1b35cdc35..3b9e8651d 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -9,6 +9,7 @@ using Real = System.Single; using System; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.Modules; +using FlaxEditor.Windows; using FlaxEngine; using FlaxEngine.Json; using Object = FlaxEngine.Object; @@ -203,9 +204,9 @@ namespace FlaxEditor.SceneGraph.Actors } } - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { - ParentNode.OnContextMenu(contextMenu); + ParentNode.OnContextMenu(contextMenu, window); } public static SceneGraphNode Create(StateData state) @@ -272,9 +273,9 @@ namespace FlaxEditor.SceneGraph.Actors DebugDraw.DrawSphere(new BoundingSphere(pos, tangentSize), Color.YellowGreen, 0, false); } - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { - ParentNode.OnContextMenu(contextMenu); + ParentNode.OnContextMenu(contextMenu, window); } public override void OnDispose() @@ -354,9 +355,9 @@ namespace FlaxEditor.SceneGraph.Actors } /// - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { - base.OnContextMenu(contextMenu); + base.OnContextMenu(contextMenu, window); contextMenu.AddButton("Add spline model", OnAddSplineModel); contextMenu.AddButton("Add spline collider", OnAddSplineCollider); diff --git a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs index 52176ab8b..8de1a7f22 100644 --- a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs +++ b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs @@ -3,6 +3,7 @@ using System; using FlaxEditor.Content; using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.Windows; using FlaxEngine; namespace FlaxEditor.SceneGraph.Actors @@ -21,9 +22,9 @@ namespace FlaxEditor.SceneGraph.Actors } /// - public override void OnContextMenu(ContextMenu contextMenu) + public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { - base.OnContextMenu(contextMenu); + base.OnContextMenu(contextMenu, window); contextMenu.AddButton("Add collider", OnAddMeshCollider).Enabled = ((StaticModel)Actor).Model != null; } diff --git a/Source/Editor/SceneGraph/SceneGraphNode.cs b/Source/Editor/SceneGraph/SceneGraphNode.cs index a9c350bb2..672a239dc 100644 --- a/Source/Editor/SceneGraph/SceneGraphNode.cs +++ b/Source/Editor/SceneGraph/SceneGraphNode.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Linq; using FlaxEditor.Modules; using FlaxEditor.SceneGraph.Actors; +using FlaxEditor.Windows; using FlaxEngine; namespace FlaxEditor.SceneGraph @@ -339,7 +340,7 @@ namespace FlaxEditor.SceneGraph /// /// Called when scene tree window wants to show the context menu. Allows to add custom options. /// - public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu) + public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu, EditorWindow window) { } diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 5a532e362..4915807bd 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -313,7 +313,7 @@ namespace FlaxEditor.Windows.Assets } if (showCustomNodeOptions) { - Selection[0].OnContextMenu(contextMenu); + Selection[0].OnContextMenu(contextMenu, this); } ContextMenuShow?.Invoke(contextMenu); diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index 80f58be0a..1f888e7c7 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -45,16 +45,6 @@ namespace FlaxEditor.Windows if (hasSthSelected) { contextMenu.AddButton(Editor.Windows.EditWin.IsPilotActorActive ? "Stop piloting actor" : "Pilot actor", Editor.UI.PilotActor); - // Position camera to viewport view - if (Editor.SceneEditing.Selection[0] is ActorNode a && a.Actor is Camera c && isSingleActorSelected) - { - contextMenu.AddButton("Position Camera to View", () => - { - var viewport = Editor.Windows.EditWin.Viewport; - c.Position = viewport.ViewPosition; - c.Orientation = viewport.ViewOrientation; - }); - } } contextMenu.AddSeparator(); @@ -224,7 +214,7 @@ namespace FlaxEditor.Windows } if (showCustomNodeOptions) { - Editor.SceneEditing.Selection[0].OnContextMenu(contextMenu); + Editor.SceneEditing.Selection[0].OnContextMenu(contextMenu, this); } ContextMenuShow?.Invoke(contextMenu); From 8a34ae3ece1a0c30a4755e3ebb3eda0901f33973 Mon Sep 17 00:00:00 2001 From: MineBill Date: Sun, 1 Oct 2023 18:41:32 +0300 Subject: [PATCH 13/33] Handle double clicking to eliminate perceived lag. --- Source/Engine/UI/GUI/Common/Button.cs | 22 ++++++++++++++++++++++ Source/Engine/UI/GUI/Common/CheckBox.cs | 21 +++++++++++++++++++++ Source/Engine/UI/GUI/Common/Dropdown.cs | 24 ++++++++++++++++++++++++ Source/Engine/UI/GUI/Panels/DropPanel.cs | 23 +++++++++++++++++++++++ 4 files changed, 90 insertions(+) diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index b50f3dd46..02337411b 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -314,6 +314,28 @@ namespace FlaxEngine.GUI return false; } + /// + public override bool OnMouseDoubleClick(Float2 location, MouseButton button) + { + if (base.OnMouseDoubleClick(location, button)) + return true; + + if (button == MouseButton.Left && _isPressed) + { + OnPressEnd(); + OnClick(); + return true; + } + + if (button == MouseButton.Left && !_isPressed) + { + OnPressBegin(); + return true; + } + + return false; + } + /// public override bool OnTouchDown(Float2 location, int pointerId) { diff --git a/Source/Engine/UI/GUI/Common/CheckBox.cs b/Source/Engine/UI/GUI/Common/CheckBox.cs index 2f1ff42a9..2128c0311 100644 --- a/Source/Engine/UI/GUI/Common/CheckBox.cs +++ b/Source/Engine/UI/GUI/Common/CheckBox.cs @@ -277,6 +277,27 @@ namespace FlaxEngine.GUI return base.OnMouseDown(location, button); } + /// + public override bool OnMouseDoubleClick(Float2 location, MouseButton button) + { + if (button == MouseButton.Left && !_isPressed) + { + OnPressBegin(); + return true; + } + + if (button == MouseButton.Left && _isPressed) + { + OnPressEnd(); + if (_box.Contains(ref location)) + { + OnClick(); + return true; + } + } + return base.OnMouseDoubleClick(location, button); + } + /// public override bool OnMouseUp(Float2 location, MouseButton button) { diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 6ad962d6d..aea4e173a 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -666,6 +666,30 @@ namespace FlaxEngine.GUI return false; } + /// + public override bool OnMouseDoubleClick(Float2 location, MouseButton button) + { + if (base.OnMouseDoubleClick(location, button)) + return true; + + if (_touchDown && button == MouseButton.Left) + { + _touchDown = false; + ShowPopup(); + return true; + } + + if (button == MouseButton.Left) + { + _touchDown = true; + if (!IsPopupOpened) + Focus(); + return true; + } + + return false; + } + /// public override bool OnTouchDown(Float2 location, int pointerId) { diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index d650d6205..66e7413eb 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -501,6 +501,29 @@ namespace FlaxEngine.GUI return false; } + /// + public override bool OnMouseDoubleClick(Float2 location, MouseButton button) + { + if (base.OnMouseDoubleClick(location, button)) + return true; + + _mouseOverHeader = HeaderRectangle.Contains(location); + if (button == MouseButton.Left && _mouseOverHeader) + { + _mouseButtonLeftDown = true; + return true; + } + + if (button == MouseButton.Left && _mouseButtonLeftDown) + { + _mouseButtonLeftDown = false; + if (_mouseOverHeader) + Toggle(); + return true; + } + return false; + } + /// public override void OnMouseLeave() { From bc872ebff50808bd824a97f968667d136ee23e9c Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Thu, 5 Oct 2023 00:06:51 +0200 Subject: [PATCH 14/33] fix menu misalignment problem on Linux --- .../Engine/Platform/Linux/LinuxPlatform.cpp | 30 +++++++++++++++++-- Source/Engine/Platform/Linux/LinuxWindow.cpp | 6 +++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index ab314ab72..6195215e3 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -2707,8 +2707,34 @@ Float2 LinuxPlatform::GetDesktopSize() Rectangle LinuxPlatform::GetMonitorBounds(const Float2& screenPos) { - // TODO: do it in a proper way - return Rectangle(Float2::Zero, GetDesktopSize()); + if (!xDisplay) + return Rectangle::Empty; + + int event, err; + const bool ok = X11::XineramaQueryExtension(xDisplay, &event, &err); + if (!ok) + return Rectangle::Empty; + + int count; + int screenIdx = 0; + X11::XineramaScreenInfo* xsi = X11::XineramaQueryScreens(xDisplay, &count); + if (screenIdx >= count) + return Rectangle::Empty; + // find the screen for this screenPos + for (int i = 0; i < count; i++) + { + if (screenPos.X >= xsi[i].x_org && screenPos.X < xsi[i].x_org+xsi[i].width + && screenPos.Y >= xsi[i].y_org && screenPos.Y < xsi[i].y_org+xsi[i].height) + { + screenIdx = i; + break; + } + } + + Float2 org((float)xsi[screenIdx].x_org, (float)xsi[screenIdx].y_org); + Float2 size((float)xsi[screenIdx].width, (float)xsi[screenIdx].height); + X11::XFree(xsi); + return Rectangle(org, size); } Rectangle LinuxPlatform::GetVirtualDesktopBounds() diff --git a/Source/Engine/Platform/Linux/LinuxWindow.cpp b/Source/Engine/Platform/Linux/LinuxWindow.cpp index 7694836cb..e6fe63f9c 100644 --- a/Source/Engine/Platform/Linux/LinuxWindow.cpp +++ b/Source/Engine/Platform/Linux/LinuxWindow.cpp @@ -153,7 +153,11 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings) hints.max_height = (int)settings.MaximumSize.Y; hints.flags |= USSize; } - X11::XSetNormalHints(display, window, &hints); + // honor the WM placement except for manual (overriding) placements + if (settings.StartPosition == WindowStartPosition::Manual) + { + X11::XSetNormalHints(display, window, &hints); + } // Ensures the child window is always on top of the parent window if (settings.Parent) From 86d1d298206697b80ef83bd75eb93faf2762e2de Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Oct 2023 10:13:49 +0200 Subject: [PATCH 15/33] Fix deadlock regression from fd679f0af59eb73244c39cb69107faf93b9358b9 when applying Game Settings itself --- Source/Engine/Content/Asset.h | 2 +- Source/Engine/Content/JsonAsset.cpp | 21 ++++++++++++++------- Source/Engine/Content/JsonAsset.h | 1 + 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Content/Asset.h b/Source/Engine/Content/Asset.h index 2d2ba4b3f..be188b2b9 100644 --- a/Source/Engine/Content/Asset.h +++ b/Source/Engine/Content/Asset.h @@ -227,7 +227,7 @@ protected: bool onLoad(LoadAssetTask* task); void onLoaded(); - void onLoaded_MainThread(); + virtual void onLoaded_MainThread(); virtual void onUnload_MainThread(); #if USE_EDITOR virtual void onRename(const StringView& newPath) = 0; diff --git a/Source/Engine/Content/JsonAsset.cpp b/Source/Engine/Content/JsonAsset.cpp index b48ef02e5..9977a28e1 100644 --- a/Source/Engine/Content/JsonAsset.cpp +++ b/Source/Engine/Content/JsonAsset.cpp @@ -367,6 +367,20 @@ void JsonAsset::unload(bool isReloading) JsonAssetBase::unload(isReloading); } +void JsonAsset::onLoaded_MainThread() +{ + JsonAssetBase::onLoaded_MainThread(); + + // Special case for Settings assets to flush them after edited and saved in Editor + const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length()); + const auto typeHandle = Scripting::FindScriptingType(StringAnsiView(dataTypeNameAnsi.Get(), DataTypeName.Length())); + if (Instance && typeHandle && typeHandle.IsSubclassOf(SettingsBase::TypeInitializer) && _isAfterReload) + { + _isAfterReload = false; + ((SettingsBase*)Instance)->Apply(); + } +} + bool JsonAsset::CreateInstance() { ScopeLock lock(Locker); @@ -409,13 +423,6 @@ bool JsonAsset::CreateInstance() } } - // Special case for Settings assets to flush them after edited and saved in Editor - if (typeHandle && typeHandle.IsSubclassOf(SettingsBase::TypeInitializer) && _isAfterReload) - { - _isAfterReload = false; - ((SettingsBase*)Instance)->Apply(); - } - return false; } diff --git a/Source/Engine/Content/JsonAsset.h b/Source/Engine/Content/JsonAsset.h index 0b6e3ca86..a8e89cec0 100644 --- a/Source/Engine/Content/JsonAsset.h +++ b/Source/Engine/Content/JsonAsset.h @@ -150,6 +150,7 @@ protected: // [JsonAssetBase] LoadResult loadAsset() override; void unload(bool isReloading) override; + void onLoaded_MainThread() override; private: bool CreateInstance(); From 9af6048bec7398973d1eca2cf998d7282518fb1e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Oct 2023 11:35:52 +0200 Subject: [PATCH 16/33] Fix regression from #1312 when passing structure from C++ to C# thunk as already boxed value --- Source/Engine/Engine/NativeInterop.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 500636290..f3bb57665 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -685,8 +685,10 @@ namespace FlaxEngine.Interop T value = default; if (nativePtr == IntPtr.Zero) return value; - - MarshalHelper.ToManaged(ref value, nativePtr, false); + if (typeof(T).IsValueType) + value = (T)ManagedHandle.FromIntPtr(nativePtr).Target; + else + MarshalHelper.ToManaged(ref value, nativePtr, false); return value; } From 546553b82dec3f9516a2e4819c8448f5d4030bce Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Oct 2023 11:44:14 +0200 Subject: [PATCH 17/33] Add some new splash quotes --- Source/Editor/Windows/SplashScreen.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index d2371a540..92894b537 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -127,6 +127,14 @@ const Char* SplashScreenQuotes[] = TEXT("Who is signing all these integers?!"), TEXT("Flax fact: Flax was called Celelej once."), TEXT("Changing text overflow setti-"), + TEXT("Testing tests"), + TEXT("Free hugs"), + TEXT("Think outside the box"), + TEXT("Let's make something fantastic"), + TEXT("Be brave"), + TEXT("Drum roll please"), + TEXT("Good Luck Have Fun"), + TEXT("GG Well Played"), }; SplashScreen::~SplashScreen() From d7ade326a96d2e39a248882d7a3c0e8ea397d526 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Thu, 5 Oct 2023 18:04:55 +0200 Subject: [PATCH 18/33] - Implemented automatic casting to visject --- Source/Editor/Surface/Archetypes/Tools.cs | 12 ++++- Source/Editor/Surface/Elements/Box.cs | 65 ++++++++++++++++++++++- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index 9f6db7ea4..cacb7e910 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -787,7 +787,7 @@ namespace FlaxEditor.Surface.Archetypes } } - private class AsNode : SurfaceNode + internal class AsNode : SurfaceNode { private TypePickerControl _picker; @@ -838,6 +838,11 @@ namespace FlaxEditor.Surface.Archetypes box.CurrentType = type ? type : ScriptType.FlaxObject; } + public void SetPickerValue(ScriptType type) + { + _picker.Value = type; + } + /// public override void OnDestroy() { @@ -999,6 +1004,11 @@ namespace FlaxEditor.Surface.Archetypes GetBox(4).CurrentType = type ? type : _picker.Type; } + public void SetPickerValue(ScriptType type) + { + _picker.Value = type; + } + /// public override void OnDestroy() { diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index a03c8f701..445a2f1e8 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Scripting; using FlaxEditor.Surface.Undo; using FlaxEngine; @@ -820,8 +821,68 @@ namespace FlaxEditor.Surface.Elements if (useCaster) { // Connect via Caster - //AddCaster(oB, iB); - throw new NotImplementedException("AddCaster(..) function"); + const float casterXOffset = 250; + if (Surface.Undo != null && Surface.Undo.Enabled) + { + bool undoEnabled = Surface.Undo.Enabled; + Surface.Undo.Enabled = false; + SurfaceNode node = Surface.Context.SpawnNode(7, 22, Float2.Zero); // 22 AsNode, 25 CastNode + Surface.Undo.Enabled = undoEnabled; + + if(node is not Archetypes.Tools.AsNode castNode) + throw new Exception("Node is not a casting node!"); + + // Set the type of the casting node + undoEnabled = castNode.Surface.Undo.Enabled; + castNode.Surface.Undo.Enabled = false; + castNode.SetPickerValue(iB.CurrentType); + castNode.Surface.Undo.Enabled = undoEnabled; + + if (node.GetBox(0) is not OutputBox castOutputBox || node.GetBox(1) is not InputBox castInputBox) + { + throw new NullReferenceException("Casting failed. Cast node is invalid!"); + } + + undoEnabled = castNode.Surface.Undo.Enabled; + castNode.Surface.Undo.Enabled = false; + var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY)); + castNode.Location = Surface.Root.PointFromParent(ref wantedOffset); + castNode.Surface.Undo.Enabled = undoEnabled; + + var spawnNodeAction = new AddRemoveNodeAction(castNode, true); + + var connectToCastNodeAction = new ConnectBoxesAction(castInputBox, oB, true); + castInputBox.CreateConnection(oB); + connectToCastNodeAction.End(); + + var connectCastToTargetNodeAction = new ConnectBoxesAction(iB, castOutputBox, true); + iB.CreateConnection(castOutputBox); + connectCastToTargetNodeAction.End(); + + Surface.AddBatchedUndoAction(new MultiUndoAction(spawnNodeAction, connectToCastNodeAction, connectCastToTargetNodeAction)); + } + else + { + SurfaceNode node = Surface.Context.SpawnNode(7, 22, Float2.Zero); // 22 AsNode, 25 CastNode + + if(node is not Archetypes.Tools.AsNode castNode) + throw new Exception("Node is not a casting node!"); + + // Set the type of the casting node + castNode.SetPickerValue(iB.CurrentType); + + if (node.GetBox(0) is not OutputBox castOutputBox || node.GetBox(1) is not InputBox castInputBox) + { + throw new NullReferenceException("Casting failed. Cast node is invalid!"); + } + + var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY)); + castNode.Location = Surface.Root.PointFromParent(ref wantedOffset); + + castInputBox.CreateConnection(oB); + iB.CreateConnection(castOutputBox); + } + Surface.MarkAsEdited(); } else { From dc6a2d4d256f0ab20fad90bb223ddec634ea30fb Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Thu, 5 Oct 2023 18:11:08 +0200 Subject: [PATCH 19/33] - Cleanup and comments --- Source/Editor/Surface/Archetypes/Tools.cs | 8 ++++++++ Source/Editor/Surface/Elements/Box.cs | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index cacb7e910..201a29418 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -838,6 +838,10 @@ namespace FlaxEditor.Surface.Archetypes box.CurrentType = type ? type : ScriptType.FlaxObject; } + /// + /// Sets the type of the picker and the type of the output box + /// + /// Target Type public void SetPickerValue(ScriptType type) { _picker.Value = type; @@ -1004,6 +1008,10 @@ namespace FlaxEditor.Surface.Archetypes GetBox(4).CurrentType = type ? type : _picker.Type; } + /// + /// Sets the type of the picker and the type of the output box + /// + /// Target Type public void SetPickerValue(ScriptType type) { _picker.Value = type; diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index 445a2f1e8..87cafd64b 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.Scripting; using FlaxEditor.Surface.Undo; using FlaxEngine; @@ -843,6 +842,7 @@ namespace FlaxEditor.Surface.Elements throw new NullReferenceException("Casting failed. Cast node is invalid!"); } + // We set the position of the cast node here to set it relative to the target nodes input box undoEnabled = castNode.Surface.Undo.Enabled; castNode.Surface.Undo.Enabled = false; var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY)); @@ -876,6 +876,7 @@ namespace FlaxEditor.Surface.Elements throw new NullReferenceException("Casting failed. Cast node is invalid!"); } + // We set the position of the cast node here to set it relative to the target nodes input box var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY)); castNode.Location = Surface.Root.PointFromParent(ref wantedOffset); From 858baa0ee054770c684956883973e73054b8cde1 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Thu, 5 Oct 2023 19:28:32 +0300 Subject: [PATCH 20/33] Use latest supported C# version for building rules assemblies --- Source/Tools/Flax.Build/Build/Assembler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Build/Assembler.cs b/Source/Tools/Flax.Build/Build/Assembler.cs index be4990087..cbe92dd8d 100644 --- a/Source/Tools/Flax.Build/Build/Assembler.cs +++ b/Source/Tools/Flax.Build/Build/Assembler.cs @@ -154,7 +154,7 @@ namespace Flax.Build // Run the compilation using var memoryStream = new MemoryStream(); - CSharpParseOptions parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9).WithPreprocessorSymbols(PreprocessorSymbols); + CSharpParseOptions parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest).WithPreprocessorSymbols(PreprocessorSymbols); var syntaxTrees = new List(); foreach (var sourceFile in SourceFiles) { From 88d9e60beeb86da4be0063feb8eca26bfa31ccfd Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Thu, 5 Oct 2023 19:30:30 +0300 Subject: [PATCH 21/33] Generate Rider-specific user solution configuration files Disables "Use Resharper Build" option by default in generated solutions, Resharper can't detect file changes in project using custom build commands. --- .../VisualStudioProjectGenerator.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index a344eea50..1ba8e6709 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -641,6 +641,44 @@ namespace Flax.Build.Projects.VisualStudio File.WriteAllText(e.Key, profile.ToString(), Encoding.UTF8); } } + + // Generate Rider-specific configuration files + { + StringBuilder dotSettingsFileContent = new StringBuilder(); + string dotSettingsUserFilePath = solution.Path + ".DotSettings.user"; + + // Solution settings (user layer) + bool useResharperBuild = false; // This needs to be disabled for custom build steps to run properly + + if (File.Exists(dotSettingsUserFilePath)) + { + foreach (var line in File.ReadAllLines(dotSettingsUserFilePath)) + { + if (line.Contains(@"/UseMsbuildSolutionBuilder/@EntryValue")) + { + if (!useResharperBuild) + { + dotSettingsFileContent.Append("\t").Append(@"No"); + if (line.Contains("")) + dotSettingsFileContent.Append("\n"); + else + dotSettingsFileContent.Append("\n"); + } + continue; + } + dotSettingsFileContent.Append(line).Append("\n"); + } + } + else + { + dotSettingsFileContent.Append(@"").Append("\n"); + if (!useResharperBuild) + dotSettingsFileContent.Append("\t").Append(@"No"); + dotSettingsFileContent.Append("\n"); + } + + Utilities.WriteFileIfChanged(dotSettingsUserFilePath, dotSettingsFileContent.ToString()); + } } /// From 544ff3d5f04f37a25dec668b1abdf845d3a8d977 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 5 Oct 2023 12:10:49 -0500 Subject: [PATCH 22/33] Fix HScroll bar from covering nodes in scene window. --- Source/Editor/Windows/SceneTreeWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index fba052269..cbaa27371 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -74,7 +74,7 @@ namespace FlaxEditor.Windows root.TreeNode.Expand(); _tree = new Tree(true) { - Margin = new Margin(0.0f, 0.0f, -16.0f, 0.0f), // Hide root node + Margin = new Margin(0.0f, 0.0f, -16.0f, _sceneTreePanel.ScrollBarsSize), // Hide root node IsScrollable = true, }; _tree.AddChild(root.TreeNode); From 233eb8a39d83a8a051ba9ad7e3a8ed76eedaced5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 5 Oct 2023 12:13:41 -0500 Subject: [PATCH 23/33] Fix prefab window tree --- Source/Editor/Windows/Assets/PrefabWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 659bab249..4e72067c6 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -146,7 +146,7 @@ namespace FlaxEditor.Windows.Assets Graph = new LocalSceneGraph(new CustomRootNode(this)); _tree = new PrefabTree { - Margin = new Margin(0.0f, 0.0f, -16.0f, 0.0f), // Hide root node + Margin = new Margin(0.0f, 0.0f, -16.0f, _treePanel.ScrollBarsSize), // Hide root node IsScrollable = true, }; _tree.AddChild(Graph.Root.TreeNode); From 9870d162e4c5e86706dbc3e91ffe5aa241079e39 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Oct 2023 10:07:57 +0200 Subject: [PATCH 24/33] Fix creating prefabs directly from prefab objects #1432 --- Source/Editor/Modules/PrefabsModule.cs | 54 ++++++++++--------- .../Windows/Assets/PrefabWindow.Hierarchy.cs | 2 +- Source/Editor/Windows/Assets/PrefabWindow.cs | 5 ++ Source/Engine/Level/Prefabs/PrefabManager.cpp | 23 +++++++- Source/Engine/Serialization/JsonTools.cpp | 1 - 5 files changed, 56 insertions(+), 29 deletions(-) diff --git a/Source/Editor/Modules/PrefabsModule.cs b/Source/Editor/Modules/PrefabsModule.cs index 7b3ffebb3..bb76e125b 100644 --- a/Source/Editor/Modules/PrefabsModule.cs +++ b/Source/Editor/Modules/PrefabsModule.cs @@ -37,11 +37,6 @@ namespace FlaxEditor.Modules /// public event Action PrefabApplied; - /// - /// Locally cached actor for prefab creation. - /// - private Actor _prefabCreationActor; - internal PrefabsModule(Editor editor) : base(editor) { @@ -65,13 +60,14 @@ namespace FlaxEditor.Modules /// To create prefab manually (from code) use method. /// /// The scene selection to use. - public void CreatePrefab(List selection) + /// The prefab window that creates it. + public void CreatePrefab(List selection, Windows.Assets.PrefabWindow prefabWindow = null) { if (selection == null) selection = Editor.SceneEditing.Selection; if (selection.Count == 1 && selection[0] is ActorNode actorNode && actorNode.CanCreatePrefab) { - CreatePrefab(actorNode.Actor); + CreatePrefab(actorNode.Actor, true, prefabWindow); } } @@ -92,7 +88,8 @@ namespace FlaxEditor.Modules /// /// The root prefab actor. /// Allow renaming or not - public void CreatePrefab(Actor actor, bool rename) + /// The prefab window that creates it. + public void CreatePrefab(Actor actor, bool rename, Windows.Assets.PrefabWindow prefabWindow = null) { // Skip in invalid states if (!Editor.StateMachine.CurrentState.CanEditContent) @@ -105,42 +102,47 @@ namespace FlaxEditor.Modules PrefabCreating?.Invoke(actor); var proxy = Editor.ContentDatabase.GetProxy(); - _prefabCreationActor = actor; - Editor.Windows.ContentWin.NewItem(proxy, actor, OnPrefabCreated, actor.Name, rename); + Editor.Windows.ContentWin.NewItem(proxy, actor, contentItem => OnPrefabCreated(contentItem, actor, prefabWindow), actor.Name, rename); } - private void OnPrefabCreated(ContentItem contentItem) + private void OnPrefabCreated(ContentItem contentItem, Actor actor, Windows.Assets.PrefabWindow prefabWindow) { if (contentItem is PrefabItem prefabItem) { PrefabCreated?.Invoke(prefabItem); } - // Skip in invalid states - if (!Editor.StateMachine.CurrentState.CanEditScene) - return; + Undo undo = null; + if (prefabWindow != null) + { + prefabWindow.MarkAsEdited(); + undo = prefabWindow.Undo; + } + else + { + // Skip in invalid states + if (!Editor.StateMachine.CurrentState.CanEditScene) + return; + undo = Editor.Undo; + } // Record undo for prefab creating (backend links the target instance with the prefab) - if (Editor.Undo.Enabled) + if (undo.Enabled) { - if (!_prefabCreationActor) + if (!actor) return; - var actorsList = new List(); - Utilities.Utils.GetActorsTree(actorsList, _prefabCreationActor); + Utilities.Utils.GetActorsTree(actorsList, actor); var actions = new IUndoAction[actorsList.Count]; for (int i = 0; i < actorsList.Count; i++) - { - var action = BreakPrefabLinkAction.Linked(actorsList[i]); - actions[i] = action; - } - Undo.AddAction(new MultiUndoAction(actions)); - - _prefabCreationActor = null; + actions[i] = BreakPrefabLinkAction.Linked(actorsList[i]); + undo.AddAction(new MultiUndoAction(actions)); } - Editor.Instance.Windows.PropertiesWin.Presenter.BuildLayout(); + Editor.Windows.PropertiesWin.Presenter.BuildLayout(); + if (prefabWindow != null) + prefabWindow.Presenter.BuildLayout(); } /// diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 5a532e362..6448ebbb2 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -233,7 +233,7 @@ namespace FlaxEditor.Windows.Assets contextMenu.AddSeparator(); - b = contextMenu.AddButton("Create Prefab", () => Editor.Prefabs.CreatePrefab(Selection)); + b = contextMenu.AddButton("Create Prefab", () => Editor.Prefabs.CreatePrefab(Selection, this)); b.Enabled = isSingleActorSelected && (Selection[0] as ActorNode).CanCreatePrefab && Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets; diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 659bab249..50aafd46a 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -53,6 +53,11 @@ namespace FlaxEditor.Windows.Assets /// public PrefabWindowViewport Viewport => _viewport; + /// + /// Gets the prefab objects properties editor. + /// + public CustomEditorPresenter Presenter => _propertiesEditor; + /// /// Gets the undo system used by this window for changes tracking. /// diff --git a/Source/Engine/Level/Prefabs/PrefabManager.cpp b/Source/Engine/Level/Prefabs/PrefabManager.cpp index a2b148f28..7d3933175 100644 --- a/Source/Engine/Level/Prefabs/PrefabManager.cpp +++ b/Source/Engine/Level/Prefabs/PrefabManager.cpp @@ -321,6 +321,8 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat // Serialize to json data ASSERT(!IsCreatingPrefab); IsCreatingPrefab = true; + const Guid targetPrefabId = targetActor->GetPrefabID(); + const bool hasTargetPrefabId = targetPrefabId.IsValid(); rapidjson_flax::StringBuffer actorsDataBuffer; { CompactJsonWriter writerObj(actorsDataBuffer); @@ -329,7 +331,27 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat for (int32 i = 0; i < sceneObjects->Count(); i++) { SceneObject* obj = sceneObjects->At(i); + + // Detect when creating prefab from object that is already part of prefab then serialize it as unlinked + const Guid prefabId = obj->GetPrefabID(); + const Guid prefabObjectId = obj->GetPrefabObjectID(); + bool isObjectFromPrefab = targetPrefabId == prefabId && prefabId.IsValid(); // Allow to use other nested prefabs properly (ignore only root object's prefab link) + if (isObjectFromPrefab) + { + //obj->BreakPrefabLink(); + obj->_prefabID = Guid::Empty; + obj->_prefabObjectID = Guid::Empty; + } + writer.SceneObject(obj); + + // Restore broken link + if (hasTargetPrefabId) + { + //obj->LinkPrefab(prefabId, prefabObjectId); + obj->_prefabID = prefabId; + obj->_prefabObjectID = prefabObjectId; + } } writer.EndArray(); } @@ -395,7 +417,6 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat { SceneObject* obj = sceneObjects->At(i); Guid prefabObjectId; - if (objectInstanceIdToPrefabObjectId.TryGet(obj->GetSceneObjectId(), prefabObjectId)) { obj->LinkPrefab(assetInfo.ID, prefabObjectId); diff --git a/Source/Engine/Serialization/JsonTools.cpp b/Source/Engine/Serialization/JsonTools.cpp index c395df4f1..3cbe8f65c 100644 --- a/Source/Engine/Serialization/JsonTools.cpp +++ b/Source/Engine/Serialization/JsonTools.cpp @@ -81,7 +81,6 @@ void JsonTools::ChangeIds(Document& doc, const Dictionary& mapping) ::ChangeIds(doc, doc, mapping); } - Float2 JsonTools::GetFloat2(const Value& value) { Float2 result; From 176123eb1f2ebaddf129ec474e161d8c2517ea73 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Oct 2023 11:40:39 +0200 Subject: [PATCH 25/33] Fix `AudioClip` loading error when buffer start times diff has rounding error --- Source/Engine/Audio/AudioClip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Audio/AudioClip.cpp b/Source/Engine/Audio/AudioClip.cpp index 8750bed03..911a8be33 100644 --- a/Source/Engine/Audio/AudioClip.cpp +++ b/Source/Engine/Audio/AudioClip.cpp @@ -348,7 +348,7 @@ Asset::LoadResult AudioClip::load() #if !BUILD_RELEASE // Validate buffer start times - if (Math::NotNearEqual(_buffersStartTimes[_totalChunks], GetLength())) + if (!Math::NearEqual(_buffersStartTimes[_totalChunks], GetLength(), 1.0f / 60.0f)) { LOG(Warning, "Invalid audio buffers data size. Expected length: {0}s", GetLength()); for (int32 i = 0; i < _totalChunks + 1; i++) From 560f699dd856cea66a8777132c4e1a9f532f5638 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Oct 2023 13:59:03 +0200 Subject: [PATCH 26/33] Fix various issues with OpenAL buffers playback (do proper bit convertion) --- Source/Engine/Audio/AudioClip.cpp | 6 +- .../Engine/Audio/OpenAL/AudioBackendOAL.cpp | 59 +++++++------------ Source/Engine/Tools/AudioTool/AudioTool.cpp | 13 ++-- 3 files changed, 28 insertions(+), 50 deletions(-) diff --git a/Source/Engine/Audio/AudioClip.cpp b/Source/Engine/Audio/AudioClip.cpp index 911a8be33..d04da7274 100644 --- a/Source/Engine/Audio/AudioClip.cpp +++ b/Source/Engine/Audio/AudioClip.cpp @@ -454,14 +454,12 @@ bool AudioClip::WriteBuffer(int32 chunkIndex) } break; case AudioFormat::Raw: - { data = Span(chunk->Get(), chunk->Size()); - } - break; + break; default: return true; } - info.NumSamples = data.Length() / bytesPerSample; + info.NumSamples = Math::AlignDown(data.Length() / bytesPerSample, info.NumChannels * bytesPerSample); // Convert to Mono if used as 3D source and backend doesn't support it if (Is3D() && info.NumChannels > 1 && EnumHasNoneFlags(AudioBackend::Features(), AudioBackend::FeatureFlags::SpatialMultiChannel)) diff --git a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp index 84642ac1f..c905c7eef 100644 --- a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp +++ b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp @@ -228,11 +228,9 @@ namespace ALC ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth) { // TODO: cache enum values in Init()?? - switch (bitDepth) { case 8: - { switch (numChannels) { case 1: @@ -247,13 +245,8 @@ ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth) return alGetEnumValue("AL_FORMAT_61CHN8"); case 8: return alGetEnumValue("AL_FORMAT_71CHN8"); - default: - CRASH; - return 0; } - } case 16: - { switch (numChannels) { case 1: @@ -268,19 +261,22 @@ ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth) return alGetEnumValue("AL_FORMAT_61CHN16"); case 8: return alGetEnumValue("AL_FORMAT_71CHN16"); - default: - CRASH; - return 0; } - } case 32: - { switch (numChannels) { case 1: +#ifdef AL_FORMAT_MONO_FLOAT32 + return AL_FORMAT_MONO_FLOAT32; +#else return alGetEnumValue("AL_FORMAT_MONO_FLOAT32"); +#endif case 2: +#ifdef AL_FORMAT_STEREO_FLOAT32 + return AL_FORMAT_STEREO_FLOAT32; +#else return alGetEnumValue("AL_FORMAT_STEREO_FLOAT32"); +#endif case 4: return alGetEnumValue("AL_FORMAT_QUAD32"); case 6: @@ -289,15 +285,9 @@ ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth) return alGetEnumValue("AL_FORMAT_61CHN32"); case 8: return alGetEnumValue("AL_FORMAT_71CHN32"); - default: - CRASH; - return 0; } } - default: - CRASH; - return 0; - } + return 0; } void AudioBackendOAL::Listener_OnAdd(AudioListener* listener) @@ -607,7 +597,8 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa { PROFILE_CPU(); - // TODO: maybe use temporary buffers per thread to reduce dynamic allocations when uploading data to OpenAL? + // Pick the format for the audio data (it might not be supported natively) + ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth); // Mono or stereo if (info.NumChannels <= 2) @@ -618,28 +609,23 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa { const uint32 bufferSize = info.NumSamples * sizeof(float); float* sampleBufferFloat = (float*)Allocator::Allocate(bufferSize); - AudioTool::ConvertToFloat(samples, info.BitDepth, sampleBufferFloat, info.NumSamples); - const ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth); + format = GetOpenALBufferFormat(info.NumChannels, 32); alBufferData(bufferId, format, sampleBufferFloat, bufferSize, info.SampleRate); ALC_CHECK_ERROR(alBufferData); - Allocator::Free(sampleBufferFloat); } else { LOG(Warning, "OpenAL doesn't support bit depth larger than 16. Your audio data will be truncated."); - const uint32 bufferSize = info.NumSamples * 2; byte* sampleBuffer16 = (byte*)Allocator::Allocate(bufferSize); - AudioTool::ConvertBitDepth(samples, info.BitDepth, sampleBuffer16, 16, info.NumSamples); - const ALenum format = GetOpenALBufferFormat(info.NumChannels, 16); + format = GetOpenALBufferFormat(info.NumChannels, 16); alBufferData(bufferId, format, sampleBuffer16, bufferSize, info.SampleRate); ALC_CHECK_ERROR(alBufferData); - Allocator::Free(sampleBuffer16); } } @@ -648,19 +634,15 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa // OpenAL expects unsigned 8-bit data, but engine stores it as signed, so convert const uint32 bufferSize = info.NumSamples * (info.BitDepth / 8); byte* sampleBuffer = (byte*)Allocator::Allocate(bufferSize); - for (uint32 i = 0; i < info.NumSamples; i++) sampleBuffer[i] = ((int8*)samples)[i] + 128; - const ALenum format = GetOpenALBufferFormat(info.NumChannels, 16); alBufferData(bufferId, format, sampleBuffer, bufferSize, info.SampleRate); ALC_CHECK_ERROR(alBufferData); - Allocator::Free(sampleBuffer); } - else + else if (format) { - const ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth); alBufferData(bufferId, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate); ALC_CHECK_ERROR(alBufferData); } @@ -675,10 +657,9 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa { const uint32 bufferSize = info.NumChannels * sizeof(int32); byte* sampleBuffer32 = (byte*)Allocator::Allocate(bufferSize); - AudioTool::ConvertBitDepth(samples, info.BitDepth, sampleBuffer32, 32, info.NumSamples); - const ALenum format = GetOpenALBufferFormat(info.NumChannels, 32); + format = GetOpenALBufferFormat(info.NumChannels, 32); alBufferData(bufferId, format, sampleBuffer32, bufferSize, info.SampleRate); ALC_CHECK_ERROR(alBufferData); @@ -693,19 +674,23 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa for (uint32 i = 0; i < info.NumSamples; i++) sampleBuffer[i] = ((int8*)samples)[i] + 128; - const ALenum format = GetOpenALBufferFormat(info.NumChannels, 16); + format = GetOpenALBufferFormat(info.NumChannels, 16); alBufferData(bufferId, format, sampleBuffer, bufferSize, info.SampleRate); ALC_CHECK_ERROR(alBufferData); Allocator::Free(sampleBuffer); } - else + else if (format) { - const ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth); alBufferData(bufferId, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate); ALC_CHECK_ERROR(alBufferData); } } + + if (!format) + { + LOG(Error, "Not suppported audio data format for OpenAL device: BitDepth={}, NumChannels={}", info.BitDepth, info.NumChannels); + } } const Char* AudioBackendOAL::Base_Name() diff --git a/Source/Engine/Tools/AudioTool/AudioTool.cpp b/Source/Engine/Tools/AudioTool/AudioTool.cpp index a5a5d4663..89d613116 100644 --- a/Source/Engine/Tools/AudioTool/AudioTool.cpp +++ b/Source/Engine/Tools/AudioTool/AudioTool.cpp @@ -231,8 +231,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp for (uint32 i = 0; i < numSamples; i++) { const int8 sample = *(int8*)input; - output[i] = sample / 127.0f; - + output[i] = sample * (1.0f / 127.0f); input++; } } @@ -241,8 +240,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp for (uint32 i = 0; i < numSamples; i++) { const int16 sample = *(int16*)input; - output[i] = sample / 32767.0f; - + output[i] = sample * (1.0f / 32767.0f); input += 2; } } @@ -251,8 +249,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp for (uint32 i = 0; i < numSamples; i++) { const int32 sample = Convert24To32Bits(input); - output[i] = sample / 2147483647.0f; - + output[i] = sample * (1.0f / 2147483647.0f); input += 3; } } @@ -261,8 +258,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp for (uint32 i = 0; i < numSamples; i++) { const int32 sample = *(int32*)input; - output[i] = sample / 2147483647.0f; - + output[i] = sample * (1.0f / 2147483647.0f); input += 4; } } @@ -278,7 +274,6 @@ void AudioTool::ConvertFromFloat(const float* input, int32* output, uint32 numSa { const float sample = *(float*)input; output[i] = static_cast(sample * 2147483647.0f); - input++; } } From e29d3d02a219683bb5065eb19ca851b1ee2af5a3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Oct 2023 14:08:48 +0200 Subject: [PATCH 27/33] Refactor audio clip import settings to use auto-generated bindings via `AudioTool` --- .../Content/Import/AudioImportSettings.cs | 188 +++++------------- Source/Editor/Editor.cs | 19 -- .../Editor/Managed/ManagedEditor.Internal.cpp | 77 ++----- Source/Editor/Managed/ManagedEditor.h | 20 ++ .../Editor/Windows/Assets/AudioClipWindow.cs | 2 +- .../Engine/ContentImporters/ImportAudio.cpp | 40 +--- Source/Engine/ContentImporters/ImportAudio.h | 24 +-- Source/Engine/Tools/AudioTool/AudioTool.cpp | 37 ++++ Source/Engine/Tools/AudioTool/AudioTool.h | 76 ++++++- 9 files changed, 200 insertions(+), 283 deletions(-) diff --git a/Source/Editor/Content/Import/AudioImportSettings.cs b/Source/Editor/Content/Import/AudioImportSettings.cs index 2fc0a1795..e0bfb6e20 100644 --- a/Source/Editor/Content/Import/AudioImportSettings.cs +++ b/Source/Editor/Content/Import/AudioImportSettings.cs @@ -1,144 +1,52 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System.ComponentModel; -using System.Reflection; -using System.Runtime.InteropServices; +using System.Collections.Generic; +using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.Scripting; using FlaxEngine; -using FlaxEngine.Interop; +using FlaxEngine.Tools; + +namespace FlaxEngine.Tools +{ + partial class AudioTool + { + partial struct Options + { + private bool ShowBtiDepth => Format != AudioFormat.Vorbis; + } + } +} + +namespace FlaxEditor.CustomEditors.Dedicated +{ + /// + /// Custom editor for . + /// + [CustomEditor(typeof(FlaxEngine.Tools.AudioTool.Options)), DefaultEditor] + public class AudioToolOptionsEditor : GenericEditor + { + /// + protected override List GetItemsForType(ScriptType type) + { + // Show both fields and properties + return GetItemsForType(type, true, true); + } + } +} namespace FlaxEditor.Content.Import { /// /// Proxy object to present audio import settings in . /// + [HideInEditor] public class AudioImportSettings { /// - /// A custom set of bit depth audio import sizes. + /// The settings data. /// - public enum CustomBitDepth - { - /// - /// The 8. - /// - _8 = 8, - - /// - /// The 16. - /// - _16 = 16, - - /// - /// The 24. - /// - _24 = 24, - - /// - /// The 32. - /// - _32 = 32, - } - - /// - /// Converts the bit depth to enum. - /// - /// The bit depth. - /// The converted enum. - public static CustomBitDepth ConvertBitDepth(int f) - { - FieldInfo[] fields = typeof(CustomBitDepth).GetFields(); - for (int i = 0; i < fields.Length; i++) - { - var field = fields[i]; - if (field.Name.Equals("value__")) - continue; - - if (f == (int)field.GetRawConstantValue()) - return (CustomBitDepth)f; - } - - return CustomBitDepth._16; - } - - /// - /// The audio data format to import the audio clip as. - /// - [EditorOrder(10), DefaultValue(AudioFormat.Vorbis), Tooltip("The audio data format to import the audio clip as.")] - public AudioFormat Format { get; set; } = AudioFormat.Vorbis; - - /// - /// The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality. - /// - [EditorOrder(15), DefaultValue(0.4f), Limit(0, 1, 0.01f), Tooltip("The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality.")] - public float CompressionQuality { get; set; } = 0.4f; - - /// - /// Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds). - /// - [EditorOrder(20), DefaultValue(false), Tooltip("Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds).")] - public bool DisableStreaming { get; set; } = false; - - /// - /// Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format. - /// - [EditorOrder(30), DefaultValue(false), EditorDisplay(null, "Is 3D"), Tooltip("Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format.")] - public bool Is3D { get; set; } = false; - - /// - /// The size of a single sample in bits. The clip will be converted to this bit depth on import. - /// - [EditorOrder(40), DefaultValue(CustomBitDepth._16), Tooltip("The size of a single sample in bits. The clip will be converted to this bit depth on import.")] - public CustomBitDepth BitDepth { get; set; } = CustomBitDepth._16; - - [StructLayout(LayoutKind.Sequential)] - internal struct InternalOptions - { - [MarshalAs(UnmanagedType.I1)] - public AudioFormat Format; - public byte DisableStreaming; - public byte Is3D; - public int BitDepth; - public float Quality; - } - - internal void ToInternal(out InternalOptions options) - { - options = new InternalOptions - { - Format = Format, - DisableStreaming = (byte)(DisableStreaming ? 1 : 0), - Is3D = (byte)(Is3D ? 1 : 0), - Quality = CompressionQuality, - BitDepth = (int)BitDepth, - }; - } - - internal void FromInternal(ref InternalOptions options) - { - Format = options.Format; - DisableStreaming = options.DisableStreaming != 0; - Is3D = options.Is3D != 0; - CompressionQuality = options.Quality; - BitDepth = ConvertBitDepth(options.BitDepth); - } - - /// - /// Tries the restore the asset import options from the target resource file. - /// - /// The options. - /// The asset path. - /// True settings has been restored, otherwise false. - public static bool TryRestore(ref AudioImportSettings options, string assetPath) - { - if (AudioImportEntry.Internal_GetAudioImportOptions(assetPath, out var internalOptions)) - { - // Restore settings - options.FromInternal(ref internalOptions); - return true; - } - - return false; - } + [EditorDisplay(null, EditorDisplayAttribute.InlineStyle)] + public AudioTool.Options Settings = AudioTool.Options.Default; } /// @@ -147,7 +55,7 @@ namespace FlaxEditor.Content.Import /// public partial class AudioImportEntry : AssetImportEntry { - private AudioImportSettings _settings = new AudioImportSettings(); + private AudioImportSettings _settings = new(); /// /// Initializes a new instance of the class. @@ -157,7 +65,7 @@ namespace FlaxEditor.Content.Import : base(ref request) { // Try to restore target asset Audio import options (useful for fast reimport) - AudioImportSettings.TryRestore(ref _settings, ResultUrl); + Editor.TryRestoreImportOptions(ref _settings.Settings, ResultUrl); } /// @@ -166,27 +74,23 @@ namespace FlaxEditor.Content.Import /// public override bool TryOverrideSettings(object settings) { - if (settings is AudioImportSettings o) + if (settings is AudioImportSettings s) { - _settings = o; + _settings.Settings = s.Settings; + return true; + } + if (settings is AudioTool.Options o) + { + _settings.Settings = o; return true; } - return false; } /// public override bool Import() { - return Editor.Import(SourceUrl, ResultUrl, _settings); + return Editor.Import(SourceUrl, ResultUrl, _settings.Settings); } - - #region Internal Calls - - [LibraryImport("FlaxEngine", EntryPoint = "AudioImportEntryInternal_GetAudioImportOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))] - [return: MarshalAs(UnmanagedType.U1)] - internal static partial bool Internal_GetAudioImportOptions(string path, out AudioImportSettings.InternalOptions result); - - #endregion } } diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 06a6f0979..e0c460d0c 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -935,21 +935,6 @@ namespace FlaxEditor Animation = 11, } - /// - /// Imports the audio asset file to the target location. - /// - /// The source file path. - /// The result asset file path. - /// The settings. - /// True if importing failed, otherwise false. - public static bool Import(string inputPath, string outputPath, AudioImportSettings settings) - { - if (settings == null) - throw new ArgumentNullException(); - settings.ToInternal(out var internalOptions); - return Internal_ImportAudio(inputPath, outputPath, ref internalOptions); - } - /// /// Serializes the given object to json asset. /// @@ -1667,10 +1652,6 @@ namespace FlaxEditor [return: MarshalAs(UnmanagedType.U1)] internal static partial bool Internal_CloneAssetFile(string dstPath, string srcPath, ref Guid dstId); - [LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_ImportAudio", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))] - [return: MarshalAs(UnmanagedType.U1)] - internal static partial bool Internal_ImportAudio(string inputPath, string outputPath, ref AudioImportSettings.InternalOptions options); - [LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAudioClipMetadata", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))] internal static partial void Internal_GetAudioClipMetadata(IntPtr obj, out int originalSize, out int importedSize); diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index afcbb740a..d2d2f216a 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -50,43 +50,6 @@ Guid ManagedEditor::ObjectID(0x91970b4e, 0x99634f61, 0x84723632, 0x54c776af); -// Disable warning C4800: 'const byte': forcing value to bool 'true' or 'false' (performance warning) -#if defined(_MSC_VER) -#pragma warning( push ) -#pragma warning( disable : 4800) -#endif - -struct InternalAudioOptions -{ - AudioFormat Format; - byte DisableStreaming; - byte Is3D; - int32 BitDepth; - float Quality; - - static void Convert(InternalAudioOptions* from, ImportAudio::Options* to) - { - to->Format = from->Format; - to->DisableStreaming = from->DisableStreaming; - to->Is3D = from->Is3D; - to->BitDepth = from->BitDepth; - to->Quality = from->Quality; - } - - static void Convert(ImportAudio::Options* from, InternalAudioOptions* to) - { - to->Format = from->Format; - to->DisableStreaming = from->DisableStreaming; - to->Is3D = from->Is3D; - to->BitDepth = from->BitDepth; - to->Quality = from->Quality; - } -}; - -#if defined(_MSC_VER) -#pragma warning( pop ) -#endif - // Pack log messages into a single scratch buffer to reduce dynamic memory allocations CriticalSection CachedLogDataLocker; Array CachedLogData; @@ -295,16 +258,6 @@ DEFINE_INTERNAL_CALL(MString*) EditorInternal_CanImport(MString* extensionObj) return importer ? MUtils::ToString(importer->ResultExtension) : nullptr; } -DEFINE_INTERNAL_CALL(bool) EditorInternal_ImportAudio(MString* inputPathObj, MString* outputPathObj, InternalAudioOptions* optionsObj) -{ - ImportAudio::Options options; - InternalAudioOptions::Convert(optionsObj, &options); - String inputPath, outputPath; - MUtils::ToString(inputPathObj, inputPath); - MUtils::ToString(outputPathObj, outputPath); - return ManagedEditor::Import(inputPath, outputPath, &options); -} - DEFINE_INTERNAL_CALL(void) EditorInternal_GetAudioClipMetadata(AudioClip* clip, int32* originalSize, int32* importedSize) { INTERNAL_CALL_CHECK(clip); @@ -765,24 +718,6 @@ DEFINE_INTERNAL_CALL(MTypeObject*) CustomEditorsUtilInternal_GetCustomEditor(MTy return CustomEditorsUtil::GetCustomEditor(targetType); } -DEFINE_INTERNAL_CALL(bool) AudioImportEntryInternal_GetAudioImportOptions(MString* pathObj, InternalAudioOptions* result) -{ - String path; - MUtils::ToString(pathObj, path); - FileSystem::NormalizePath(path); - - ImportAudio::Options options; - if (ImportAudio::TryGetImportOptions(path, options)) - { - // Convert into managed storage - InternalAudioOptions::Convert(&options, result); - - return true; - } - - return false; -} - DEFINE_INTERNAL_CALL(MArray*) LayersAndTagsSettingsInternal_GetCurrentLayers(int* layersCount) { *layersCount = Math::Max(1, Level::GetNonEmptyLayerNamesCount()); @@ -830,3 +765,15 @@ bool ManagedEditor::TryRestoreImportOptions(ModelTool::Options& options, String FileSystem::NormalizePath(assetPath); return ImportModelFile::TryGetImportOptions(assetPath, options); } + +bool ManagedEditor::Import(const String& inputPath, const String& outputPath, const AudioTool::Options& options) +{ + return Import(inputPath, outputPath, (void*)&options); +} + +bool ManagedEditor::TryRestoreImportOptions(AudioTool::Options& options, String assetPath) +{ + // Get options from model + FileSystem::NormalizePath(assetPath); + return ImportAudio::TryGetImportOptions(assetPath, options); +} diff --git a/Source/Editor/Managed/ManagedEditor.h b/Source/Editor/Managed/ManagedEditor.h index ddd94b47e..93d1723a3 100644 --- a/Source/Editor/Managed/ManagedEditor.h +++ b/Source/Editor/Managed/ManagedEditor.h @@ -7,6 +7,7 @@ #include "Engine/ShadowsOfMordor/Types.h" #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Tools/ModelTool/ModelTool.h" +#include "Engine/Tools/AudioTool/AudioTool.h" namespace CSG { @@ -190,6 +191,25 @@ public: API_FUNCTION() static bool TryRestoreImportOptions(API_PARAM(Ref) ModelTool::Options& options, String assetPath); #endif +#if COMPILE_WITH_AUDIO_TOOL + /// + /// Imports the audio asset file to the target location. + /// + /// The source file path. + /// The result asset file path. + /// The import settings. + /// True if importing failed, otherwise false. + API_FUNCTION() static bool Import(const String& inputPath, const String& outputPath, const AudioTool::Options& options); + + /// + /// Tries the restore the asset import options from the target resource file. + /// + /// The options. + /// The asset path. + /// True settings has been restored, otherwise false. + API_FUNCTION() static bool TryRestoreImportOptions(API_PARAM(Ref) AudioTool::Options& options, String assetPath); +#endif + private: void OnEditorAssemblyLoaded(MAssembly* assembly); diff --git a/Source/Editor/Windows/Assets/AudioClipWindow.cs b/Source/Editor/Windows/Assets/AudioClipWindow.cs index f5f244c58..e6fabc165 100644 --- a/Source/Editor/Windows/Assets/AudioClipWindow.cs +++ b/Source/Editor/Windows/Assets/AudioClipWindow.cs @@ -116,7 +116,7 @@ namespace FlaxEditor.Windows.Assets _window = window; // Try to restore target asset AudioClip import options (useful for fast reimport) - AudioImportSettings.TryRestore(ref ImportSettings, window.Item.Path); + Editor.TryRestoreImportOptions(ref ImportSettings.Settings, window.Item.Path); // Prepare restore data PeekState(); diff --git a/Source/Engine/ContentImporters/ImportAudio.cpp b/Source/Engine/ContentImporters/ImportAudio.cpp index 10915b2d1..7a8c952e4 100644 --- a/Source/Engine/ContentImporters/ImportAudio.cpp +++ b/Source/Engine/ContentImporters/ImportAudio.cpp @@ -18,32 +18,6 @@ #include "Engine/Tools/AudioTool/OggVorbisDecoder.h" #include "Engine/Tools/AudioTool/OggVorbisEncoder.h" #include "Engine/Serialization/JsonWriters.h" -#include "Engine/Scripting/Enums.h" - -String ImportAudio::Options::ToString() const -{ - return String::Format(TEXT("Format:{}, DisableStreaming:{}, Is3D:{}, Quality:{}, BitDepth:{}"), ScriptingEnum::ToString(Format), DisableStreaming, Is3D, Quality, BitDepth); -} - -void ImportAudio::Options::Serialize(SerializeStream& stream, const void* otherObj) -{ - SERIALIZE_GET_OTHER_OBJ(ImportAudio::Options); - - SERIALIZE(Format); - SERIALIZE(DisableStreaming); - SERIALIZE(Is3D); - SERIALIZE(Quality); - SERIALIZE(BitDepth); -} - -void ImportAudio::Options::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) -{ - DESERIALIZE(Format); - DESERIALIZE(DisableStreaming); - DESERIALIZE(Is3D); - DESERIALIZE(Quality); - DESERIALIZE(BitDepth); -} bool ImportAudio::TryGetImportOptions(const StringView& path, Options& options) { @@ -112,16 +86,14 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder& sampleBuffer.Link(audioData.Get()); // Convert bit depth if need to - if (options.BitDepth != static_cast(info.BitDepth)) + uint32 outputBitDepth = (uint32)options.BitDepth; + if (outputBitDepth != info.BitDepth) { - const uint32 outBufferSize = info.NumSamples * (options.BitDepth / 8); + const uint32 outBufferSize = info.NumSamples * (outputBitDepth / 8); sampleBuffer.Allocate(outBufferSize); - - AudioTool::ConvertBitDepth(audioData.Get(), info.BitDepth, sampleBuffer.Get(), options.BitDepth, info.NumSamples); - - info.BitDepth = options.BitDepth; + AudioTool::ConvertBitDepth(audioData.Get(), info.BitDepth, sampleBuffer.Get(), outputBitDepth, info.NumSamples); + info.BitDepth = outputBitDepth; bytesPerSample = info.BitDepth / 8; - bufferSize = outBufferSize; } @@ -149,7 +121,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder& context.Data.Header.Chunks[chunkIndex]->Data.Copy(dataPtr, dataSize); #define WRITE_DATA(chunkIndex, dataPtr, dataSize) \ - samplesPerChunk[chunkIndex] = (dataSize) / (options.BitDepth / 8); \ + samplesPerChunk[chunkIndex] = (dataSize) / (outputBitDepth / 8); \ switch (options.Format) \ { \ case AudioFormat::Raw: \ diff --git a/Source/Engine/ContentImporters/ImportAudio.h b/Source/Engine/ContentImporters/ImportAudio.h index fb4ee64e3..3f3eda5ed 100644 --- a/Source/Engine/ContentImporters/ImportAudio.h +++ b/Source/Engine/ContentImporters/ImportAudio.h @@ -3,12 +3,12 @@ #pragma once #include "Types.h" -#include "Engine/Tools/AudioTool/AudioDecoder.h" -#include "Engine/Core/ISerializable.h" -#include "Engine/Audio/Config.h" #if COMPILE_WITH_ASSETS_IMPORTER +#include "Engine/Tools/AudioTool/AudioTool.h" +#include "Engine/Tools/AudioTool/AudioDecoder.h" + /// /// Enable/disable caching audio import options /// @@ -20,23 +20,7 @@ class ImportAudio { public: - /// - /// Importing audio options - /// - struct Options : public ISerializable - { - AudioFormat Format = AudioFormat::Vorbis; - bool DisableStreaming = false; - bool Is3D = false; - int32 BitDepth = 16; - float Quality = 0.4f; - - String ToString() const; - - // [ISerializable] - void Serialize(SerializeStream& stream, const void* otherObj) override; - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; - }; + typedef AudioTool::Options Options; public: /// diff --git a/Source/Engine/Tools/AudioTool/AudioTool.cpp b/Source/Engine/Tools/AudioTool/AudioTool.cpp index 89d613116..3136a5a0d 100644 --- a/Source/Engine/Tools/AudioTool/AudioTool.cpp +++ b/Source/Engine/Tools/AudioTool/AudioTool.cpp @@ -1,14 +1,49 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +#if COMPILE_WITH_AUDIO_TOOL + #include "AudioTool.h" #include "Engine/Core/Core.h" #include "Engine/Core/Memory/Allocation.h" +#if USE_EDITOR +#include "Engine/Serialization/Serialization.h" +#include "Engine/Scripting/Enums.h" +#endif #define CONVERT_TO_MONO_AVG 1 #if !CONVERT_TO_MONO_AVG #include "Engine/Core/Math/Math.h" #endif +#if USE_EDITOR + +String AudioTool::Options::ToString() const +{ + return String::Format(TEXT("Format:{}, DisableStreaming:{}, Is3D:{}, Quality:{}, BitDepth:{}"), ScriptingEnum::ToString(Format), DisableStreaming, Is3D, Quality, (int32)BitDepth); +} + +void AudioTool::Options::Serialize(SerializeStream& stream, const void* otherObj) +{ + SERIALIZE_GET_OTHER_OBJ(AudioTool::Options); + + SERIALIZE(Format); + SERIALIZE(DisableStreaming); + SERIALIZE(Is3D); + SERIALIZE(Quality); + SERIALIZE(BitDepth); +} + +void AudioTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + DESERIALIZE(Format); + DESERIALIZE(DisableStreaming); + DESERIALIZE(Is3D); + DESERIALIZE(Quality); + DESERIALIZE(BitDepth); +} + +#endif + void ConvertToMono8(const int8* input, uint8* output, uint32 numSamples, uint32 numChannels) { for (uint32 i = 0; i < numSamples; i++) @@ -277,3 +312,5 @@ void AudioTool::ConvertFromFloat(const float* input, int32* output, uint32 numSa input++; } } + +#endif diff --git a/Source/Engine/Tools/AudioTool/AudioTool.h b/Source/Engine/Tools/AudioTool/AudioTool.h index b9525ffa7..cd61ee37a 100644 --- a/Source/Engine/Tools/AudioTool/AudioTool.h +++ b/Source/Engine/Tools/AudioTool/AudioTool.h @@ -2,16 +2,85 @@ #pragma once +#if COMPILE_WITH_AUDIO_TOOL + #include "Engine/Core/Config.h" #include "Engine/Core/Types/BaseTypes.h" +#if USE_EDITOR +#include "Engine/Core/ISerializable.h" +#endif +#include "Engine/Audio/Types.h" /// /// Audio data importing and processing utilities. /// -class FLAXENGINE_API AudioTool +API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API AudioTool { -public: + DECLARE_SCRIPTING_TYPE_MINIMAL(AudioTool); +#if USE_EDITOR + +public: + /// + /// Declares the imported audio clip bit depth. + /// + API_ENUM(Attributes="HideInEditor") enum class BitDepth : int32 + { + // 8-bits per sample. + _8 = 8, + // 16-bits per sample. + _16 = 16, + // 24-bits per sample. + _24 = 24, + // 32-bits per sample. + _32 = 32, + }; + + /// + /// Audio import options. + /// + API_STRUCT(Attributes="HideInEditor") struct FLAXENGINE_API Options : public ISerializable + { + DECLARE_SCRIPTING_TYPE_MINIMAL(Options); + + /// + /// The audio data format to import the audio clip as. + /// + API_FIELD(Attributes="EditorOrder(10)") + AudioFormat Format = AudioFormat::Vorbis; + + /// + /// The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality. + /// + API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Compression Quality\"), Limit(0, 1, 0.01f)") + float Quality = 0.4f; + + /// + /// Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds). + /// + API_FIELD(Attributes="EditorOrder(30)") + bool DisableStreaming = false; + + /// + /// Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format. + /// + API_FIELD(Attributes="EditorOrder(40), EditorDisplay(null, \"Is 3D\")") + bool Is3D = false; + + /// + /// The size of a single sample in bits. The clip will be converted to this bit depth on import. + /// + API_FIELD(Attributes="EditorOrder(50), VisibleIf(nameof(ShowBtiDepth))") + BitDepth BitDepth = BitDepth::_16; + + String ToString() const; + + // [ISerializable] + void Serialize(SerializeStream& stream, const void* otherObj) override; + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + }; + +public: /// /// Converts a set of audio samples using multiple channels into a set of mono samples. /// @@ -58,4 +127,7 @@ public: { return (input[2] << 24) | (input[1] << 16) | (input[0] << 8); } +#endif }; + +#endif From 92e28f66affd5ad03482a7bb283f0ab04a0cb145 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Oct 2023 14:19:22 +0200 Subject: [PATCH 28/33] Fix various issues with audio clip data buffers to reduce artifacts (especially when using 24-bit data) --- Source/Editor/Windows/Assets/AudioClipWindow.cs | 5 +++++ Source/Engine/Audio/AudioSource.cpp | 2 +- Source/Engine/ContentImporters/ImportAudio.cpp | 9 +++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Windows/Assets/AudioClipWindow.cs b/Source/Editor/Windows/Assets/AudioClipWindow.cs index e6fabc165..8cf31f416 100644 --- a/Source/Editor/Windows/Assets/AudioClipWindow.cs +++ b/Source/Editor/Windows/Assets/AudioClipWindow.cs @@ -134,6 +134,11 @@ namespace FlaxEditor.Windows.Assets /// public void Reimport() { + if (_window?._previewSource != null) + { + _window._previewSource.Stop(); + _window.UpdateToolstrip(); + } Editor.Instance.ContentImporting.Reimport((BinaryAssetItem)_window.Item, ImportSettings, true); } diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index 2cee52a8a..93c2e18df 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -183,7 +183,7 @@ void AudioSource::Stop() float AudioSource::GetTime() const { - if (_state == States::Stopped || SourceIDs.IsEmpty()) + if (_state == States::Stopped || SourceIDs.IsEmpty() || !Clip->IsLoaded()) return 0.0f; float time = AudioBackend::Source::GetCurrentBufferTime(this); diff --git a/Source/Engine/ContentImporters/ImportAudio.cpp b/Source/Engine/ContentImporters/ImportAudio.cpp index 7a8c952e4..803bf58a8 100644 --- a/Source/Engine/ContentImporters/ImportAudio.cpp +++ b/Source/Engine/ContentImporters/ImportAudio.cpp @@ -64,6 +64,10 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder& } } + // Vorbis uses fixed 16-bit depth + if (options.Format == AudioFormat::Vorbis) + options.BitDepth = AudioTool::BitDepth::_16; + LOG_STR(Info, options.ToString()); // Open the file @@ -155,8 +159,9 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder& else { // Split audio data into a several chunks (uniform data spread) - const int32 MinChunkSize = 1 * 1024 * 1024; // 1 MB - const int32 chunkSize = Math::Max(MinChunkSize, (int32)Math::AlignUp(bufferSize / ASSET_FILE_DATA_CHUNKS, 256)); + const int32 minChunkSize = 1 * 1024 * 1024; // 1 MB + const int32 dataAlignment = info.NumChannels * bytesPerSample; // Ensure to never split samples in-between (eg. 24-bit that uses 3 bytes) + const int32 chunkSize = Math::Max(minChunkSize, (int32)Math::AlignUp(bufferSize / ASSET_FILE_DATA_CHUNKS, dataAlignment)); const int32 chunksCount = Math::CeilToInt((float)bufferSize / chunkSize); ASSERT(chunksCount > 0 && chunksCount <= ASSET_FILE_DATA_CHUNKS); From a698095bdd647867b571fea566010ad05454fce8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Oct 2023 14:34:08 +0200 Subject: [PATCH 29/33] Fix spatial audio playback when clip is set after the audio source was enabled #1622 --- Source/Engine/Audio/AudioSource.cpp | 8 ++++++++ Source/Engine/Audio/AudioSource.h | 1 + 2 files changed, 9 insertions(+) diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index 93c2e18df..afe5c4b49 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -265,6 +265,7 @@ void AudioSource::Cleanup() void AudioSource::OnClipChanged() { Stop(); + _clipChanged = true; } void AudioSource::OnClipLoaded() @@ -318,6 +319,12 @@ void AudioSource::SetNonStreamingBuffer() void AudioSource::PlayInternal() { + if (_clipChanged && SourceIDs.HasItems()) + { + // If clip was changed between source setup (OnEnable) and actual playback start then ensure to flush any runtime properties with the audio backend + _clipChanged = false; + AudioBackend::Source::SpatialSetupChanged(this); + } AudioBackend::Source::Play(this); _isActuallyPlayingSth = true; @@ -482,6 +489,7 @@ void AudioSource::OnEnable() { _prevPos = GetPosition(); _velocity = Vector3::Zero; + _clipChanged = false; Audio::OnAddSource(this); GetScene()->Ticking.Update.AddTick(this); diff --git a/Source/Engine/Audio/AudioSource.h b/Source/Engine/Audio/AudioSource.h index 3b6f32153..70cdb4180 100644 --- a/Source/Engine/Audio/AudioSource.h +++ b/Source/Engine/Audio/AudioSource.h @@ -53,6 +53,7 @@ private: bool _loop; bool _playOnStart; bool _allowSpatialization; + bool _clipChanged = false; bool _isActuallyPlayingSth = false; bool _needToUpdateStreamingBuffers = false; From ae308ab27a0d587f7a17a3630ff2a9e6083b02d5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Oct 2023 15:25:25 +0200 Subject: [PATCH 30/33] Minor code cleanup #1619 --- Source/Editor/Surface/Archetypes/Tools.cs | 4 +-- Source/Editor/Surface/Elements/Box.cs | 30 +++++++++-------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index 201a29418..68bea9051 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -846,7 +846,7 @@ namespace FlaxEditor.Surface.Archetypes { _picker.Value = type; } - + /// public override void OnDestroy() { @@ -1016,7 +1016,7 @@ namespace FlaxEditor.Surface.Archetypes { _picker.Value = type; } - + /// public override void OnDestroy() { diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index 87cafd64b..ba2009b2a 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -827,59 +827,51 @@ namespace FlaxEditor.Surface.Elements Surface.Undo.Enabled = false; SurfaceNode node = Surface.Context.SpawnNode(7, 22, Float2.Zero); // 22 AsNode, 25 CastNode Surface.Undo.Enabled = undoEnabled; - - if(node is not Archetypes.Tools.AsNode castNode) + if (node is not Archetypes.Tools.AsNode castNode) throw new Exception("Node is not a casting node!"); - + // Set the type of the casting node undoEnabled = castNode.Surface.Undo.Enabled; castNode.Surface.Undo.Enabled = false; castNode.SetPickerValue(iB.CurrentType); castNode.Surface.Undo.Enabled = undoEnabled; - if (node.GetBox(0) is not OutputBox castOutputBox || node.GetBox(1) is not InputBox castInputBox) - { throw new NullReferenceException("Casting failed. Cast node is invalid!"); - } - + // We set the position of the cast node here to set it relative to the target nodes input box undoEnabled = castNode.Surface.Undo.Enabled; castNode.Surface.Undo.Enabled = false; var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY)); castNode.Location = Surface.Root.PointFromParent(ref wantedOffset); castNode.Surface.Undo.Enabled = undoEnabled; - + var spawnNodeAction = new AddRemoveNodeAction(castNode, true); - + var connectToCastNodeAction = new ConnectBoxesAction(castInputBox, oB, true); castInputBox.CreateConnection(oB); connectToCastNodeAction.End(); - + var connectCastToTargetNodeAction = new ConnectBoxesAction(iB, castOutputBox, true); iB.CreateConnection(castOutputBox); connectCastToTargetNodeAction.End(); - + Surface.AddBatchedUndoAction(new MultiUndoAction(spawnNodeAction, connectToCastNodeAction, connectCastToTargetNodeAction)); } else { SurfaceNode node = Surface.Context.SpawnNode(7, 22, Float2.Zero); // 22 AsNode, 25 CastNode - - if(node is not Archetypes.Tools.AsNode castNode) + if (node is not Archetypes.Tools.AsNode castNode) throw new Exception("Node is not a casting node!"); - + // Set the type of the casting node castNode.SetPickerValue(iB.CurrentType); - if (node.GetBox(0) is not OutputBox castOutputBox || node.GetBox(1) is not InputBox castInputBox) - { throw new NullReferenceException("Casting failed. Cast node is invalid!"); - } - + // We set the position of the cast node here to set it relative to the target nodes input box var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY)); castNode.Location = Surface.Root.PointFromParent(ref wantedOffset); - + castInputBox.CreateConnection(oB); iB.CreateConnection(castOutputBox); } From 2cac149741601cb4ae68ba4bd5747599f7189ced Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Oct 2023 15:58:23 +0200 Subject: [PATCH 31/33] Code cleanup for #1377 --- .../GUI/Timeline/Tracks/CameraCutTrack.cs | 2 + Source/Editor/Viewport/EditorViewport.cs | 58 ++++--------------- Source/Engine/Graphics/RenderTask.cs | 14 +++++ Source/Engine/Graphics/RenderView.cpp | 4 +- Source/Engine/Graphics/RenderView.cs | 2 +- Source/Engine/Graphics/RenderView.h | 10 ++-- Source/Engine/Level/Actors/Camera.cpp | 4 +- Source/Engine/Level/Actors/Camera.h | 2 +- 8 files changed, 40 insertions(+), 56 deletions(-) diff --git a/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs index f6a542faa..5ca9ff7ce 100644 --- a/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs @@ -125,6 +125,8 @@ namespace FlaxEditor.GUI.Timeline.Tracks fov = cam.FieldOfView; customAspectRatio = cam.CustomAspectRatio; view.RenderLayersMask = cam.RenderLayersMask; + view.Flags = cam.RenderFlags; + view.Mode = cam.RenderMode; } // Try to evaluate camera properties based on the animated tracks diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 43ec4ee78..0c8bfc07d 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -10,7 +10,6 @@ using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Widgets; using FlaxEngine; using FlaxEngine.GUI; -using FlaxEngine.Utilities; using Newtonsoft.Json; using JsonSerializer = FlaxEngine.Json.JsonSerializer; @@ -496,34 +495,16 @@ namespace FlaxEditor.Viewport viewLayers.AddButton("Copy layers", () => Clipboard.Text = JsonSerializer.Serialize(Task.View.RenderLayersMask)); viewLayers.AddButton("Paste layers", () => { - object obj; try { - obj = JsonConvert.DeserializeObject(Clipboard.Text, typeof(LayersMask), JsonSerializer.Settings); + Task.ViewLayersMask = JsonSerializer.Deserialize(Clipboard.Text); } catch { - obj = null; - } - if (obj != null && obj is LayersMask layer) - { - RenderView view = Task.View; - view.RenderLayersMask = layer; - Task.View = view; } }); - viewLayers.AddButton("Reset layers", () => - { - RenderView view = Task.View; - view.RenderLayersMask = LayersMask.Default; - Task.View = view; - }).Icon = Editor.Instance.Icons.Rotate32; - viewLayers.AddButton("Disable layers", () => - { - RenderView view = Task.View; - view.RenderLayersMask = new LayersMask(0); - Task.View = view; - }).Icon = Editor.Instance.Icons.Rotate32; + viewLayers.AddButton("Reset layers", () => Task.ViewLayersMask = LayersMask.Default).Icon = Editor.Instance.Icons.Rotate32; + viewLayers.AddButton("Disable layers", () => Task.ViewLayersMask = new LayersMask(0)).Icon = Editor.Instance.Icons.Rotate32; viewLayers.AddSeparator(); var layers = LayersAndTagsSettings.GetCurrentLayers(); if (layers != null && layers.Length > 0) @@ -542,10 +523,8 @@ namespace FlaxEditor.Viewport { int layerIndex = (int)button.Tag; LayersMask mask = new LayersMask(layerIndex); - RenderView view = Task.View; - view.RenderLayersMask ^= mask; - Task.View = view; - button.Icon = (Task.View.RenderLayersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + Task.ViewLayersMask ^= mask; + button.Icon = (Task.ViewLayersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } }; viewLayers.VisibleChanged += WidgetViewLayersShowHide; @@ -557,18 +536,12 @@ namespace FlaxEditor.Viewport viewFlags.AddButton("Copy flags", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewFlags)); viewFlags.AddButton("Paste flags", () => { - object obj; try { - obj = JsonConvert.DeserializeObject(Clipboard.Text, typeof(ViewFlags), JsonSerializer.Settings); + Task.ViewFlags = JsonSerializer.Deserialize(Clipboard.Text); } catch { - obj = null; - } - if (obj != null && obj is ViewFlags flags) - { - Task.ViewFlags = flags; } }); viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32; @@ -587,7 +560,7 @@ namespace FlaxEditor.Viewport { var v = (ViewFlags)button.Tag; Task.ViewFlags ^= v; - button.Icon = (Task.View.Flags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + button.Icon = (Task.ViewFlags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } }; viewFlags.VisibleChanged += WidgetViewFlagsShowHide; @@ -599,18 +572,12 @@ namespace FlaxEditor.Viewport debugView.AddButton("Copy view", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewMode)); debugView.AddButton("Paste view", () => { - object obj; try { - obj = JsonConvert.DeserializeObject(Clipboard.Text, typeof(ViewMode), JsonSerializer.Settings); + Task.ViewMode = JsonSerializer.Deserialize(Clipboard.Text); } catch { - obj = null; - } - if (obj != null && obj is ViewMode mode) - { - Task.ViewMode = mode; } }); debugView.AddSeparator(); @@ -1185,9 +1152,9 @@ namespace FlaxEditor.Viewport _isVirtualMouseRightDown = false; // Cancel when mouse right or escape is pressed if (_wasVirtualMouseRightDown) wasControllingMouse = true; - if (_isVirtualMouseRightDown) + if (_isVirtualMouseRightDown) _isControllingMouse = _isVirtualMouseRightDown; - + if (wasControllingMouse != _isControllingMouse) { if (_isControllingMouse) @@ -1698,15 +1665,14 @@ namespace FlaxEditor.Viewport return; var ccm = (ContextMenu)cm; + var layersMask = Task.ViewLayersMask; foreach (var e in ccm.Items) { if (e is ContextMenuButton b && b != null && b.Tag != null) { int layerIndex = (int)b.Tag; LayersMask mask = new LayersMask(layerIndex); - b.Icon = (Task.View.RenderLayersMask & mask) != 0 - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = (layersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } diff --git a/Source/Engine/Graphics/RenderTask.cs b/Source/Engine/Graphics/RenderTask.cs index a1f90a9c5..fd42c754c 100644 --- a/Source/Engine/Graphics/RenderTask.cs +++ b/Source/Engine/Graphics/RenderTask.cs @@ -39,5 +39,19 @@ namespace FlaxEngine View = view; } } + + /// + /// The rendering mask for layers. Used to exclude objects from rendering (via property). + /// + public LayersMask ViewLayersMask + { + get => View.RenderLayersMask; + set + { + var view = View; + view.RenderLayersMask = value; + View = view; + } + } } } diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp index c84351bdc..efcbb1513 100644 --- a/Source/Engine/Graphics/RenderView.cpp +++ b/Source/Engine/Graphics/RenderView.cpp @@ -176,7 +176,7 @@ void RenderView::SetProjector(float nearPlane, float farPlane, const Float3& pos CullingFrustum = Frustum; } -void RenderView::CopyFrom(Camera* camera, Viewport* viewport) +void RenderView::CopyFrom(const Camera* camera, const Viewport* viewport) { const Vector3 cameraPos = camera->GetPosition(); LargeWorlds::UpdateOrigin(Origin, cameraPos); @@ -193,7 +193,7 @@ void RenderView::CopyFrom(Camera* camera, Viewport* viewport) CullingFrustum = Frustum; RenderLayersMask = camera->RenderLayersMask; Flags = camera->RenderFlags; - Mode = camera->RenderView; + Mode = camera->RenderMode; } void RenderView::GetWorldMatrix(const Transform& transform, Matrix& world) const diff --git a/Source/Engine/Graphics/RenderView.cs b/Source/Engine/Graphics/RenderView.cs index 0d35bd360..b770037a3 100644 --- a/Source/Engine/Graphics/RenderView.cs +++ b/Source/Engine/Graphics/RenderView.cs @@ -103,7 +103,7 @@ namespace FlaxEngine TemporalAAJitter = Float4.Zero; RenderLayersMask = camera.RenderLayersMask; Flags = camera.RenderFlags; - Mode = camera.RenderView; + Mode = camera.RenderMode; UpdateCachedData(); } diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h index 5cfe06a8d..e72ebaa45 100644 --- a/Source/Engine/Graphics/RenderView.h +++ b/Source/Engine/Graphics/RenderView.h @@ -295,10 +295,12 @@ public: /// Camera's FOV angle (in degrees) void SetProjector(float nearPlane, float farPlane, const Float3& position, const Float3& direction, const Float3& up, float angle); - // Copy view data from camera - // @param camera Camera to copy its data - // @param camera The custom viewport to use for view/projection matrices override. - void CopyFrom(Camera* camera, Viewport* viewport = nullptr); + /// + /// Copies view data from camera to the view. + /// + /// The camera to copy its data. + /// The custom viewport to use for view/projection matrices override. + void CopyFrom(const Camera* camera, const Viewport* viewport = nullptr); public: FORCE_INLINE DrawPass GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp index 2a5c44a7e..5c5107803 100644 --- a/Source/Engine/Level/Actors/Camera.cpp +++ b/Source/Engine/Level/Actors/Camera.cpp @@ -416,7 +416,7 @@ void Camera::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_MEMBER(OrthoScale, _orthoScale); SERIALIZE(RenderLayersMask); SERIALIZE(RenderFlags); - SERIALIZE(RenderView); + SERIALIZE(RenderMode); } void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) @@ -432,7 +432,7 @@ void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier DESERIALIZE_MEMBER(OrthoScale, _orthoScale); DESERIALIZE(RenderLayersMask); DESERIALIZE(RenderFlags); - DESERIALIZE(RenderView); + DESERIALIZE(RenderMode); } void Camera::OnEnable() diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h index 21f13665f..22d950dd4 100644 --- a/Source/Engine/Level/Actors/Camera.h +++ b/Source/Engine/Level/Actors/Camera.h @@ -145,7 +145,7 @@ public: /// Describes frame rendering modes for this camera. /// API_FIELD(Attributes = "EditorOrder(120), EditorDisplay(\"Camera\")") - ViewMode RenderView = ViewMode::Default; + ViewMode RenderMode = ViewMode::Default; public: /// From cd889604e180031edf8aa7eff1acf291b2faef92 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Oct 2023 16:02:31 +0200 Subject: [PATCH 32/33] Fix build regression --- Source/Engine/Tools/AudioTool/AudioTool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Tools/AudioTool/AudioTool.h b/Source/Engine/Tools/AudioTool/AudioTool.h index cd61ee37a..d7e28df57 100644 --- a/Source/Engine/Tools/AudioTool/AudioTool.h +++ b/Source/Engine/Tools/AudioTool/AudioTool.h @@ -79,6 +79,7 @@ public: void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; }; +#endif public: /// @@ -127,7 +128,6 @@ public: { return (input[2] << 24) | (input[1] << 16) | (input[0] << 8); } -#endif }; #endif From 5cf1d2b46e1dd5bc546f60045797ffb34349bfb3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Oct 2023 17:06:17 +0200 Subject: [PATCH 33/33] Code format --- Source/Editor/SceneGraph/Actors/CameraNode.cs | 2 +- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/CameraNode.cs b/Source/Editor/SceneGraph/Actors/CameraNode.cs index 4b05ca430..1e01b5c50 100644 --- a/Source/Editor/SceneGraph/Actors/CameraNode.cs +++ b/Source/Editor/SceneGraph/Actors/CameraNode.cs @@ -28,7 +28,7 @@ namespace FlaxEditor.SceneGraph.Actors /// public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window) { - base.OnContextMenu(contextMenu,window); + base.OnContextMenu(contextMenu, window); if (window is not SceneTreeWindow win) return; var button = new ContextMenuButton(contextMenu, "Move Camera to View"); diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index c4edda65e..d392e8309 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -599,7 +599,7 @@ namespace FlaxEditor.SceneGraph.GUI // Drag scripts else if (_dragScripts != null && _dragScripts.HasValidDrag) { - foreach(var script in _dragScripts.Objects) + foreach (var script in _dragScripts.Objects) { var customAction = script.HasPrefabLink ? new ReparentAction(script) : null; using (new UndoBlock(ActorNode.Root.Undo, script, "Change script parent", customAction)) @@ -616,7 +616,7 @@ namespace FlaxEditor.SceneGraph.GUI var spawnParent = myActor; if (DragOverMode == DragItemPositioning.Above || DragOverMode == DragItemPositioning.Below) spawnParent = newParent; - + for (int i = 0; i < _dragAssets.Objects.Count; i++) { var item = _dragAssets.Objects[i]; @@ -720,7 +720,7 @@ namespace FlaxEditor.SceneGraph.GUI for (var i = 0; i < tree.Selection.Count; i++) { var e = tree.Selection[i]; - + // Skip if parent is already selected to keep correct parenting if (tree.Selection.Contains(e.Parent)) continue;