From d389348260f3b3b56a18bb82de188ed14aa7c9a3 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Tue, 12 Sep 2023 21:38:26 +0300 Subject: [PATCH 01/61] 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 478c946c1c65f04272dc09f429daf44b3f34169f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 17 Sep 2023 17:42:18 -0500 Subject: [PATCH 02/61] Add automatic module creation. --- .../Windows/ContentWindow.ContextMenu.cs | 153 ++++++++++++++++++ Source/Editor/Windows/ContentWindow.cs | 78 ++++++++- 2 files changed, 230 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index 283133688..2a398cfb9 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -1,11 +1,13 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.IO; using FlaxEditor.Content; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.Assertions; +using FlaxEngine.GUI; using FlaxEngine.Json; namespace FlaxEditor.Windows @@ -146,6 +148,157 @@ namespace FlaxEditor.Windows } cm.AddSeparator(); + + // Check if is source folder to add new module + if (item is ContentFolder sourceFolder && sourceFolder.ParentFolder.Node is ProjectTreeNode node) + { + if (sourceFolder.Node == node.Source) + { + var button = cm.AddButton("New Module"); + button.CloseMenuOnClick = false; + button.Clicked += () => + { + var popup = new ContextMenuBase + { + Size = new Float2(230, 125), + ClipChildren = false, + CullChildren = false, + }; + popup.Show(button, new Float2(button.Width, 0)); + + var nameLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Name", + HorizontalAlignment = TextAlignment.Near, + }; + nameLabel.LocalX += 10; + nameLabel.LocalY += 10; + + var nameTextBox = new TextBox + { + Parent = popup, + WatermarkText = "Module Name", + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + nameTextBox.LocalX += 100; + nameTextBox.LocalY += 10; + var defaultTextBoxBorderColor = nameTextBox.BorderColor; + var defaultTextBoxBorderSelectedColor = nameTextBox.BorderSelectedColor; + nameTextBox.TextChanged += () => + { + if (string.IsNullOrEmpty(nameTextBox.Text)) + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + return; + } + + var pluginPath = Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text); + if (Directory.Exists(pluginPath)) + { + nameTextBox.BorderColor = Color.Red; + nameTextBox.BorderSelectedColor = Color.Red; + } + else + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + } + }; + + var editorLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Editor", + HorizontalAlignment = TextAlignment.Near, + }; + editorLabel.LocalX += 10; + editorLabel.LocalY += 35; + + var editorCheckBox = new CheckBox() + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + }; + editorCheckBox.LocalY += 35; + editorCheckBox.LocalX += 100; + + var cppLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "C++", + HorizontalAlignment = TextAlignment.Near, + }; + cppLabel.LocalX += 10; + cppLabel.LocalY += 60; + + var cppCheckBox = new CheckBox() + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + }; + cppCheckBox.LocalY += 60; + cppCheckBox.LocalX += 100; + + var submitButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Create", + Width = 70, + }; + submitButton.LocalX += 40; + submitButton.LocalY += 90; + + submitButton.Clicked += () => + { + // TODO: Check all modules in project including plugins + if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text))) + { + Editor.LogWarning("Cannot create plugin due to name conflict."); + return; + } + CreateModule(node.Source.Path, nameTextBox.Text, editorCheckBox.Checked, cppCheckBox.Checked); + nameTextBox.Clear(); + editorCheckBox.Checked = false; + cppCheckBox.Checked = false; + popup.Hide(); + }; + + var cancelButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Cancel", + Width = 70, + }; + cancelButton.LocalX += 120; + cancelButton.LocalY += 90; + + cancelButton.Clicked += () => + { + nameTextBox.Clear(); + editorCheckBox.Checked = false; + cppCheckBox.Checked = false; + popup.Hide(); + }; + }; + /* + button.Size = new Float2(200, 100); + var nameLabel = new Label() + { + Text = "Name:", + Parent = button, + AutoWidth = true, + }; + */ + } + } if (!isRootFolder) { diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 0f3c12286..8dc91d711 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Xml; using FlaxEditor.Content; using FlaxEditor.Content.GUI; @@ -730,7 +731,82 @@ namespace FlaxEditor.Windows } /// - /// Stars creating the folder. + /// Starts creating a new module + /// + private async void CreateModule(string path, string moduleName, bool editorModule, bool cpp) + { + if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(path)) + { + Editor.LogWarning("Failed to create module due to no name"); + return; + } + + var sourceFolder = SelectedNode.Folder; + var sourcePath = sourceFolder.Path; + + // Create folder + var moduleFolderPath = Path.Combine(path, moduleName); + Directory.CreateDirectory(moduleFolderPath); + + // Create module + var moduleText = "using Flax.Build;\n" + + "using Flax.Build.NativeCpp;\n" + + $"\npublic class {moduleName} : Game{(editorModule ? "Editor" : "")}Module\n" + + "{\n " + + "/// \n" + + " public override void Init()\n" + + " {\n" + + " base.Init();\n" + + "\n" + + " // C#-only scripting if false\n" + + $" BuildNativeCode = {(cpp ? "true" : "false")};\n" + + " }\n" + + "\n" + + " /// \n" + + " public override void Setup(BuildOptions options)\n" + + " {" + + "\n" + + " base.Setup(options);\n" + + "\n" + + " options.ScriptingAPI.IgnoreMissingDocumentationWarnings = true;\n" + + "\n" + + " // Here you can modify the build options for your game module\n" + + " // To reference another module use: options.PublicDependencies.Add(\"Audio\");\n" + + " // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n" + + " // To learn more see scripting documentation.\n" + + " }\n" + + "}"; + + var modulePath = Path.Combine(moduleFolderPath, $"{moduleName}.Build.cs"); + await File.WriteAllTextAsync(modulePath, moduleText, new UTF8Encoding()); + Editor.Log($"Module created at {modulePath}"); + + // Get editor target and target files and add module + var files = Directory.GetFiles(sourceFolder.Path); + var targetModuleText = $"Modules.Add(\"{moduleName}\");\n "; + foreach (var file in files) + { + if (!file.Contains(".Build.cs", StringComparison.OrdinalIgnoreCase)) + continue; + + var targetText = await File.ReadAllTextAsync(file); + + if (!editorModule && targetText.Contains("GameProjectTarget", StringComparison.Ordinal)) + continue; + + // TODO: Handle edge case when there are no modules in a target + var index = targetText.IndexOf("Modules.Add"); + if (index != -1) + { + var newText = targetText.Insert(index, targetModuleText); + await File.WriteAllTextAsync(file, newText); + Editor.Log($"Module added to Target: {file}"); + } + } + } + + /// + /// Starts creating the folder. /// public void NewFolder() { From c1104e803d0557bdb399631f28aae9aebc01fd7e Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 17 Sep 2023 17:48:09 -0500 Subject: [PATCH 03/61] Code cleanup --- Source/Editor/Windows/ContentWindow.ContextMenu.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index 2a398cfb9..ab00340a5 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -288,15 +288,6 @@ namespace FlaxEditor.Windows popup.Hide(); }; }; - /* - button.Size = new Float2(200, 100); - var nameLabel = new Label() - { - Text = "Name:", - Parent = button, - AutoWidth = true, - }; - */ } } From 17383fe0b639f0599e28100224baf039ac698ed1 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 17 Sep 2023 17:49:38 -0500 Subject: [PATCH 04/61] Small fix to error message --- Source/Editor/Windows/ContentWindow.ContextMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index ab00340a5..bf457ab02 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -260,7 +260,7 @@ namespace FlaxEditor.Windows // TODO: Check all modules in project including plugins if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text))) { - Editor.LogWarning("Cannot create plugin due to name conflict."); + Editor.LogWarning("Cannot create module due to name conflict."); return; } CreateModule(node.Source.Path, nameTextBox.Text, editorCheckBox.Checked, cppCheckBox.Checked); From 6e94b21452efbe3ba85ec80c46d164bb6329c1fe Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 17 Sep 2023 20:02:22 -0500 Subject: [PATCH 05/61] Fix build issue --- Source/Editor/Windows/ContentWindow.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 8dc91d711..4169f60d3 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -776,9 +776,9 @@ namespace FlaxEditor.Windows " // To learn more see scripting documentation.\n" + " }\n" + "}"; - + moduleText = Encoding.UTF8.GetString(Encoding.Default.GetBytes(moduleText)); var modulePath = Path.Combine(moduleFolderPath, $"{moduleName}.Build.cs"); - await File.WriteAllTextAsync(modulePath, moduleText, new UTF8Encoding()); + await File.WriteAllTextAsync(modulePath, moduleText); Editor.Log($"Module created at {modulePath}"); // Get editor target and target files and add module From 409b17df2d1e0febfec3ec0c2132b7c8938a430e Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Sun, 24 Sep 2023 11:28:07 -0400 Subject: [PATCH 06/61] Remake --- Source/Engine/Level/Actor.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 394d5c9dc..5d4cd9495 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -876,6 +876,15 @@ void Actor::EndPlay() CHECK(IsDuringPlay()); #endif + // Fire event for scripting + for (auto* script : Scripts) + { + CHECK_EXECUTE_IN_EDITOR + { + script->OnDestroy(); + } + } + // Fire event for scripting if (IsActiveInHierarchy() && GetScene()) { @@ -895,15 +904,6 @@ void Actor::EndPlay() Children[i]->EndPlay(); } - // Fire event for scripting - for (auto* script : Scripts) - { - CHECK_EXECUTE_IN_EDITOR - { - script->OnDestroy(); - } - } - // Inform attached scripts for (int32 i = 0; i < Scripts.Count(); i++) { From 5eff51d47eb8881433cfa56282347ea71642da71 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 25 Sep 2023 11:46:37 -0500 Subject: [PATCH 07/61] 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 08/61] 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 09/61] 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 10/61] 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 11/61] 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 12/61] 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 13/61] 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 14/61] 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 15/61] 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 16/61] 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 17/61] 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 ba204029009c04f31f862975e8c31d5d2a77ec5e Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Thu, 28 Sep 2023 19:05:26 -0400 Subject: [PATCH 18/61] Change OnDestroy order --- Source/Engine/Level/Actor.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 5d4cd9495..f89083177 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -877,6 +877,12 @@ void Actor::EndPlay() #endif // Fire event for scripting + if (IsActiveInHierarchy() && GetScene()) + { + ASSERT(GetScene()); + OnDisable(); + } + for (auto* script : Scripts) { CHECK_EXECUTE_IN_EDITOR @@ -885,13 +891,6 @@ void Actor::EndPlay() } } - // Fire event for scripting - if (IsActiveInHierarchy() && GetScene()) - { - ASSERT(GetScene()); - OnDisable(); - } - OnEndPlay(); // Clear flag From 8a34ae3ece1a0c30a4755e3ebb3eda0901f33973 Mon Sep 17 00:00:00 2001 From: MineBill Date: Sun, 1 Oct 2023 18:41:32 +0300 Subject: [PATCH 19/61] 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 bd32619016bc58dd7eb06d08ac61f52fb4c1e6d2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 1 Oct 2023 21:11:08 -0500 Subject: [PATCH 20/61] Small fix --- Source/Editor/Windows/ContentWindow.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 4169f60d3..890f4f770 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -740,9 +740,6 @@ namespace FlaxEditor.Windows Editor.LogWarning("Failed to create module due to no name"); return; } - - var sourceFolder = SelectedNode.Folder; - var sourcePath = sourceFolder.Path; // Create folder var moduleFolderPath = Path.Combine(path, moduleName); @@ -782,7 +779,7 @@ namespace FlaxEditor.Windows Editor.Log($"Module created at {modulePath}"); // Get editor target and target files and add module - var files = Directory.GetFiles(sourceFolder.Path); + var files = Directory.GetFiles(path); var targetModuleText = $"Modules.Add(\"{moduleName}\");\n "; foreach (var file in files) { @@ -791,9 +788,10 @@ namespace FlaxEditor.Windows var targetText = await File.ReadAllTextAsync(file); - if (!editorModule && targetText.Contains("GameProjectTarget", StringComparison.Ordinal)) + // Skip game project if it is suppose to be an editor module + if (editorModule && targetText.Contains("GameProjectTarget", StringComparison.Ordinal)) continue; - + // TODO: Handle edge case when there are no modules in a target var index = targetText.IndexOf("Modules.Add"); if (index != -1) From bc872ebff50808bd824a97f968667d136ee23e9c Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Thu, 5 Oct 2023 00:06:51 +0200 Subject: [PATCH 21/61] 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 9af6048bec7398973d1eca2cf998d7282518fb1e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Oct 2023 11:35:52 +0200 Subject: [PATCH 22/61] 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 23/61] 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 24/61] - 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 25/61] - 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 26/61] 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 27/61] 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 28/61] 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 29/61] 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 30/61] 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 31/61] 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 32/61] 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 33/61] 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 34/61] 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 35/61] 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 36/61] 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 37/61] 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 38/61] 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 39/61] 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; From 78c0e1dd925edb8733ffd9457f94f0e45378c9c9 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Fri, 6 Oct 2023 18:16:04 +0200 Subject: [PATCH 40/61] - Visual fix to preview connection when pulling out of an input box or a reroute node --- Source/Editor/Surface/VisjectSurface.Draw.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index 8db64b476..6180cfa12 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using FlaxEditor.Surface.Elements; using FlaxEngine; namespace FlaxEditor.Surface @@ -135,8 +136,25 @@ namespace FlaxEditor.Surface endPos = _lastInstigatorUnderMouse.ConnectionOrigin; } + Float2 actualStartPos = startPos; + Float2 actualEndPos = endPos; + + if (_connectionInstigator is Archetypes.Tools.RerouteNode) + { + if (endPos.X < startPos.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true}) + { + actualStartPos = endPos; + actualEndPos = startPos; + } + } + else if (_connectionInstigator is Box { IsOutput: false }) + { + actualStartPos = endPos; + actualEndPos = startPos; + } + // Draw connection - _connectionInstigator.DrawConnectingLine(ref startPos, ref endPos, ref lineColor); + _connectionInstigator.DrawConnectingLine(ref actualStartPos, ref actualEndPos, ref lineColor); } /// From e4bd84bd6ae8c7e6e73d5d504f6d2d1d6483942a Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Fri, 6 Oct 2023 23:02:00 +0200 Subject: [PATCH 41/61] - Fixed items in Surface Paramter Group getting duplicated over and over again --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index aa2069ee2..df530c3d5 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -618,7 +618,7 @@ namespace FlaxEditor.Surface.ContextMenu Archetypes = archetypes }; - var group = CreateGroup(groupArchetype); + var group = CreateGroup(groupArchetype, false); group.ArrowImageOpened = new SpriteBrush(Style.Current.ArrowDown); group.ArrowImageClosed = new SpriteBrush(Style.Current.ArrowRight); group.Close(false); From 1f4343d664dbd673e17311bafc34667b34c8da14 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 7 Oct 2023 14:23:06 +0300 Subject: [PATCH 42/61] Fix IES profile light computation formula --- Source/Shaders/IESProfile.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Shaders/IESProfile.hlsl b/Source/Shaders/IESProfile.hlsl index 710ae8484..2b101f176 100644 --- a/Source/Shaders/IESProfile.hlsl +++ b/Source/Shaders/IESProfile.hlsl @@ -7,7 +7,7 @@ float ComputeLightProfileMultiplier(Texture2D tex, float3 worldPosition, float3 lightPosition, float3 lightDirection) { float3 l = normalize(worldPosition - lightPosition); - float d = dot(lightPosition, lightDirection); + float d = dot(l, lightDirection); float angle = asin(d) / PI + 0.5f; return tex.SampleLevel(SamplerLinearClamp, float2(angle, 0), 0).r; } From bc658bbfbac51b634d3e608103c719373d487a5c Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 7 Oct 2023 14:24:31 +0300 Subject: [PATCH 43/61] Fix `Dictionary` iterator comparison Missing change from 9291295a4d9997e3257037db7135d3a5e52d3962 --- Source/Engine/Core/Collections/Dictionary.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Collections/Dictionary.h b/Source/Engine/Core/Collections/Dictionary.h index f5ffb1bfa..67b90a444 100644 --- a/Source/Engine/Core/Collections/Dictionary.h +++ b/Source/Engine/Core/Collections/Dictionary.h @@ -309,7 +309,7 @@ public: FORCE_INLINE bool operator==(const Iterator& v) const { - return _index == v._index && &_collection == &v._collection; + return _index == v._index && _collection == v._collection; } FORCE_INLINE bool operator!=(const Iterator& v) const From 2eb49dba59de666008676771f61635775cfea8f7 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 7 Oct 2023 14:38:58 +0300 Subject: [PATCH 44/61] Move `Use Inverse Squared Falloff` above `Falloff Exponent` in lights Prevents the checkbox in UI from moving around when toggling the value. --- Source/Engine/Level/Actors/PointLight.h | 14 +++++++------- Source/Engine/Level/Actors/SpotLight.h | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Level/Actors/PointLight.h b/Source/Engine/Level/Actors/PointLight.h index 50df19232..f40d4a0b0 100644 --- a/Source/Engine/Level/Actors/PointLight.h +++ b/Source/Engine/Level/Actors/PointLight.h @@ -30,18 +30,18 @@ public: API_FIELD(Attributes="EditorOrder(3), DefaultValue(0.0f), EditorDisplay(\"Light\"), Limit(0, 1000, 0.01f)") float SourceLength = 0.0f; - /// - /// Controls the radial falloff of light when UseInverseSquaredFalloff is disabled. - /// - API_FIELD(Attributes="EditorOrder(13), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f), VisibleIf(nameof(UseInverseSquaredFalloff), true)") - float FallOffExponent = 8.0f; - /// /// Whether to use physically based inverse squared distance falloff, where Radius is only clamping the light's contribution. /// - API_FIELD(Attributes="EditorOrder(14), DefaultValue(false), EditorDisplay(\"Light\")") + API_FIELD(Attributes = "EditorOrder(13), DefaultValue(false), EditorDisplay(\"Light\")") bool UseInverseSquaredFalloff = false; + /// + /// Controls the radial falloff of light when UseInverseSquaredFalloff is disabled. + /// + API_FIELD(Attributes="EditorOrder(14), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f), VisibleIf(nameof(UseInverseSquaredFalloff), true)") + float FallOffExponent = 8.0f; + /// /// IES texture (light profiles from real world measured data) /// diff --git a/Source/Engine/Level/Actors/SpotLight.h b/Source/Engine/Level/Actors/SpotLight.h index e6179a522..37735692a 100644 --- a/Source/Engine/Level/Actors/SpotLight.h +++ b/Source/Engine/Level/Actors/SpotLight.h @@ -29,18 +29,18 @@ public: API_FIELD(Attributes="EditorOrder(2), DefaultValue(0.0f), EditorDisplay(\"Light\"), Limit(0, 1000, 0.01f)") float SourceRadius = 0.0f; - /// - /// Controls the radial falloff of light when UseInverseSquaredFalloff is disabled. - /// - API_FIELD(Attributes="EditorOrder(13), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f), VisibleIf(nameof(UseInverseSquaredFalloff), true)") - float FallOffExponent = 8.0f; - /// /// Whether to use physically based inverse squared distance falloff, where Radius is only clamping the light's contribution. /// - API_FIELD(Attributes="EditorOrder(14), DefaultValue(false), EditorDisplay(\"Light\")") + API_FIELD(Attributes = "EditorOrder(13), DefaultValue(false), EditorDisplay(\"Light\")") bool UseInverseSquaredFalloff = false; + /// + /// Controls the radial falloff of light when UseInverseSquaredFalloff is disabled. + /// + API_FIELD(Attributes="EditorOrder(14), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f), VisibleIf(nameof(UseInverseSquaredFalloff), true)") + float FallOffExponent = 8.0f; + /// /// IES texture (light profiles from real world measured data) /// From d41ec1560668c2e648654ec1a52a2e54f480b025 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 7 Oct 2023 13:57:05 +0200 Subject: [PATCH 45/61] - Implemented support for surface parameters --- Source/Editor/Surface/Archetypes/Function.cs | 24 +-- Source/Editor/Surface/Archetypes/Packing.cs | 8 +- .../Editor/Surface/Archetypes/Parameters.cs | 137 ++++++++++++++++++ .../Surface/ContextMenu/VisjectCMItem.cs | 4 +- Source/Editor/Surface/NodeArchetype.cs | 2 +- 5 files changed, 156 insertions(+), 19 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index e9d73ae68..5c3ee7217 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -745,12 +745,12 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } - internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) { return false; } - internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) { return inputType.IsVoid; } @@ -1162,7 +1162,7 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } - internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) { if (nodeArch.Tag is not ScriptMemberInfo memberInfo) return false; @@ -1188,7 +1188,7 @@ namespace FlaxEditor.Surface.Archetypes return false; } - internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) { if (nodeArch.Tag is not ScriptMemberInfo memberInfo) return false; @@ -1836,12 +1836,12 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } - internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) { return false; } - internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) { return inputType.IsVoid; } @@ -1982,7 +1982,7 @@ namespace FlaxEditor.Surface.Archetypes UpdateSignature(); } - internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) { var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); if (scriptType == ScriptType.Null) @@ -2011,7 +2011,7 @@ namespace FlaxEditor.Surface.Archetypes return false; } - internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) { var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); if (scriptType == ScriptType.Null) @@ -2093,7 +2093,7 @@ namespace FlaxEditor.Surface.Archetypes UpdateSignature(); } - internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) { if (outputType.IsVoid) return true; @@ -2130,7 +2130,7 @@ namespace FlaxEditor.Surface.Archetypes return false; } - internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) { return inputType.IsVoid; } @@ -2353,7 +2353,7 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } - internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) { // Event based nodes always have a pulse input, so it's always compatible with void if (outputType.IsVoid) @@ -2373,7 +2373,7 @@ namespace FlaxEditor.Surface.Archetypes return false; } - internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) { // Event based nodes always have a pulse output, so it's always compatible with void if (inputType.IsVoid) diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs index 6b9e72e7e..d5102dbbc 100644 --- a/Source/Editor/Surface/Archetypes/Packing.cs +++ b/Source/Editor/Surface/Archetypes/Packing.cs @@ -217,7 +217,7 @@ namespace FlaxEditor.Surface.Archetypes { } - internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) { var typeName = (string)nodeArch.DefaultValues[0]; var type = TypeUtils.GetType(typeName); @@ -234,7 +234,7 @@ namespace FlaxEditor.Surface.Archetypes return false; } - internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) { var typeName = (string)nodeArch.DefaultValues[0]; var type = TypeUtils.GetType(typeName); @@ -255,7 +255,7 @@ namespace FlaxEditor.Surface.Archetypes { } - internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) { var typeName = (string)nodeArch.DefaultValues[0]; var type = TypeUtils.GetType(typeName); @@ -267,7 +267,7 @@ namespace FlaxEditor.Surface.Archetypes return false; } - internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) { var typeName = (string)nodeArch.DefaultValues[0]; var type = TypeUtils.GetType(typeName); diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs index c7f343cfb..0e382a68e 100644 --- a/Source/Editor/Surface/Archetypes/Parameters.cs +++ b/Source/Editor/Surface/Archetypes/Parameters.cs @@ -490,6 +490,56 @@ namespace FlaxEditor.Surface.Archetypes _combobox.Width = Width - 30; } } + + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) + { + if (inputType == ScriptType.Object) + return true; + + SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]); + ScriptType type = parameter?.Type ?? ScriptType.Null; + + if (parameter == null || type == ScriptType.Null || parameter.Type.Type == null) + return false; + if (DefaultPrototypes == null || !DefaultPrototypes.TryGetValue(parameter.Type.Type, out var elements)) + { + return VisjectSurface.FullCastCheck(inputType, type, hint); + } + if (elements == null) + return false; + + for (var i = 0; i < elements.Length; i++) + { + if(elements[i].Type != NodeElementType.Output) + continue; + + if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint)) + return true; + } + return false; + } + + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) + { + SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]); + ScriptType type = parameter?.Type ?? ScriptType.Null; + + if (parameter == null || type == ScriptType.Null) + return false; + if (parameter.Type.Type == null || DefaultPrototypes == null || !DefaultPrototypes.TryGetValue(parameter.Type.Type, out var elements)) + return false; + if (elements == null) + return false; + + for (var i = 0; i < elements.Length; i++) + { + if(elements[i].Type != NodeElementType.Input) + continue; + if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint)) + return true; + } + return false; + } } /// @@ -675,6 +725,53 @@ namespace FlaxEditor.Surface.Archetypes /// protected override bool UseNormalMaps => false; + + internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) + { + if (inputType == ScriptType.Object) + return true; + + SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]); + ScriptType type = parameter?.Type ?? ScriptType.Null; + + if (parameter == null || type == ScriptType.Null) + return false; + if (parameter.Type.Type == null || DefaultPrototypesParticleEmitter == null || !DefaultPrototypesParticleEmitter.TryGetValue(parameter.Type.Type, out var elements)) + return false; + if (elements == null) + return false; + + for (var i = 0; i < elements.Length; i++) + { + if(elements[i].Type != NodeElementType.Output) + continue; + if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint)) + return true; + } + return false; + } + + internal new static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) + { + SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]); + ScriptType type = parameter?.Type ?? ScriptType.Null; + + if (parameter == null || type == ScriptType.Null) + return false; + if (parameter.Type.Type == null || DefaultPrototypesParticleEmitter == null || !DefaultPrototypesParticleEmitter.TryGetValue(parameter.Type.Type, out var elements)) + return false; + if (elements == null) + return false; + + for (var i = 0; i < elements.Length; i++) + { + if(elements[i].Type != NodeElementType.Input) + continue; + if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint)) + return true; + } + return false; + } } /// @@ -692,6 +789,22 @@ namespace FlaxEditor.Surface.Archetypes /// protected override bool UseNormalMaps => false; + + internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) + { + if (inputType == ScriptType.Object) + return true; + + SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]); + ScriptType type = parameter?.Type ?? ScriptType.Null; + + return VisjectSurface.FullCastCheck(inputType, type, hint); + } + + internal new static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) + { + return false; + } } /// @@ -874,6 +987,22 @@ namespace FlaxEditor.Surface.Archetypes _combobox.Width = Width - 50; } } + + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) + { + return inputType == ScriptType.Void; + } + + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context) + { + if (outputType == ScriptType.Void) + return true; + + SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]); + ScriptType type = parameter?.Type ?? ScriptType.Null; + + return VisjectSurface.FullCastCheck(outputType, type, hint); + } } /// @@ -885,6 +1014,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 1, Create = (id, context, arch, groupArch) => new SurfaceNodeParamsGet(id, context, arch, groupArch), + IsInputCompatible = SurfaceNodeParamsGet.IsInputCompatible, + IsOutputCompatible = SurfaceNodeParamsGet.IsOutputCompatible, Title = "Get Parameter", Description = "Parameter value getter", Flags = NodeFlags.MaterialGraph | NodeFlags.AnimGraph, @@ -902,6 +1033,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 2, Create = (id, context, arch, groupArch) => new SurfaceNodeParamsGetParticleEmitter(id, context, arch, groupArch), + IsInputCompatible = SurfaceNodeParamsGetParticleEmitter.IsInputCompatible, + IsOutputCompatible = SurfaceNodeParamsGetParticleEmitter.IsOutputCompatible, Title = "Get Parameter", Description = "Parameter value getter", Flags = NodeFlags.ParticleEmitterGraph, @@ -919,6 +1052,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 3, Create = (id, context, arch, groupArch) => new SurfaceNodeParamsGetVisualScript(id, context, arch, groupArch), + IsInputCompatible = SurfaceNodeParamsGetVisualScript.IsInputCompatible, + IsOutputCompatible = SurfaceNodeParamsGetVisualScript.IsOutputCompatible, Title = "Get Parameter", Description = "Parameter value getter", Flags = NodeFlags.VisualScriptGraph, @@ -936,6 +1071,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 4, Create = (id, context, arch, groupArch) => new SurfaceNodeParamsSet(id, context, arch, groupArch), + IsInputCompatible = SurfaceNodeParamsSet.IsInputCompatible, + IsOutputCompatible = SurfaceNodeParamsSet.IsOutputCompatible, Title = "Set Parameter", Description = "Parameter value setter", Flags = NodeFlags.VisualScriptGraph, diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 7fd583202..37da7d74d 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -110,11 +110,11 @@ namespace FlaxEditor.Surface.ContextMenu bool isCompatible = false; if (startBox.IsOutput && _archetype.IsInputCompatible != null) { - isCompatible |= _archetype.IsInputCompatible.Invoke(_archetype, startBox.CurrentType, _archetype.ConnectionsHints); + isCompatible |= _archetype.IsInputCompatible.Invoke(_archetype, startBox.CurrentType, _archetype.ConnectionsHints, startBox.ParentNode.Context); } else if (!startBox.IsOutput && _archetype.IsOutputCompatible != null) { - isCompatible |= _archetype.IsOutputCompatible.Invoke(_archetype, startBox.CurrentType, startBox.ParentNode.Archetype.ConnectionsHints); + isCompatible |= _archetype.IsOutputCompatible.Invoke(_archetype, startBox.CurrentType, startBox.ParentNode.Archetype.ConnectionsHints, startBox.ParentNode.Context); } else if (_archetype.Elements != null) { diff --git a/Source/Editor/Surface/NodeArchetype.cs b/Source/Editor/Surface/NodeArchetype.cs index fe54268e2..0284fc76d 100644 --- a/Source/Editor/Surface/NodeArchetype.cs +++ b/Source/Editor/Surface/NodeArchetype.cs @@ -92,7 +92,7 @@ namespace FlaxEditor.Surface /// /// Checks if the given type is compatible with the given node archetype. Used for custom nodes /// - public delegate bool IsCompatible(NodeArchetype nodeArch, ScriptType portType, ConnectionsHint hint); + public delegate bool IsCompatible(NodeArchetype nodeArch, ScriptType portType, ConnectionsHint hint, VisjectSurfaceContext context); /// /// Unique node type ID within a single group. From 56c3080b10ab44a585ede8d257c718cc38090173 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Sat, 7 Oct 2023 11:06:23 -0400 Subject: [PATCH 46/61] fix edit text box --- Source/Engine/UI/GUI/Common/TextBoxBase.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 05183d57a..93cdaa917 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -769,13 +769,13 @@ namespace FlaxEngine.GUI { SetSelection(SelectionLeft); } - else if (SelectionLeft > 0) + else if (SelectionLeft >= 0) { int position; if (ctrl) position = FindPrevWordBegin(); else - position = _selectionEnd - 1; + position = Mathf.Max(_selectionEnd - 1, 0); if (shift) { @@ -1302,7 +1302,7 @@ namespace FlaxEngine.GUI MoveRight(shiftDown, ctrDown); return true; case KeyboardKeys.ArrowLeft: - MoveLeft(shiftDown, ctrDown); + MoveLeft(shiftDown, ctrDown); return true; case KeyboardKeys.ArrowUp: MoveUp(shiftDown, ctrDown); From e903348d716c7c3908aff6002f62e886afeac3c3 Mon Sep 17 00:00:00 2001 From: minebill Date: Sat, 7 Oct 2023 22:28:39 +0300 Subject: [PATCH 47/61] Fix incorrect mapping of Alt key on linux --- Source/Engine/Platform/Linux/LinuxPlatform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index 6195215e3..af2c02ca6 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -945,7 +945,7 @@ const char* ButtonCodeToKeyName(KeyboardKeys code) // Row #6 case KeyboardKeys::Control: return "LCTL"; // Left Control case KeyboardKeys::LeftWindows: return "LWIN"; - case KeyboardKeys::LeftMenu: return "LALT"; + case KeyboardKeys::Alt: return "LALT"; case KeyboardKeys::Spacebar: return "SPCE"; case KeyboardKeys::RightMenu: return "RALT"; case KeyboardKeys::RightWindows: return "RWIN"; From 95f40ecc0c62d16fc1e19b5ca83cb6272efb9180 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Sat, 7 Oct 2023 16:27:14 -0400 Subject: [PATCH 48/61] clean code --- Source/Engine/UI/GUI/Common/TextBoxBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 93cdaa917..c08b59f48 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -1302,7 +1302,7 @@ namespace FlaxEngine.GUI MoveRight(shiftDown, ctrDown); return true; case KeyboardKeys.ArrowLeft: - MoveLeft(shiftDown, ctrDown); + MoveLeft(shiftDown, ctrDown); return true; case KeyboardKeys.ArrowUp: MoveUp(shiftDown, ctrDown); From b8f094e00787f82f14ba702c67490949376c9362 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 8 Oct 2023 11:09:12 +0200 Subject: [PATCH 49/61] Cleanup `Iterator` in `ChunkedArray` --- Source/Engine/Core/Collections/ChunkedArray.h | 47 +------------------ 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/Source/Engine/Core/Collections/ChunkedArray.h b/Source/Engine/Core/Collections/ChunkedArray.h index d01711e38..38bf92fb8 100644 --- a/Source/Engine/Core/Collections/ChunkedArray.h +++ b/Source/Engine/Core/Collections/ChunkedArray.h @@ -136,13 +136,11 @@ public: public: bool IsEnd() const { - ASSERT(_collection); return Index() == _collection->Count(); } bool IsNotEnd() const { - ASSERT(_collection); return Index() != _collection->Count(); } @@ -171,8 +169,6 @@ public: public: Iterator& operator++() { - ASSERT(_collection); - // Check if it is not at end const int32 end = _collection->Count(); if (Index() != end) @@ -188,38 +184,18 @@ public: _index = 0; } } - return *this; } Iterator operator++(int) { - ASSERT(_collection); Iterator temp = *this; - - // Check if it is not at end - const int32 end = _collection->Count(); - if (Index() != end) - { - // Move forward within chunk - _index++; - - // Check if need to change chunk - if (_index == ChunkSize && _chunkIndex < _collection->_chunks.Count() - 1) - { - // Move to next chunk - _chunkIndex++; - _index = 0; - } - } - + ++temp; return temp; } Iterator& operator--() { - ASSERT(_collection); - // Check if it's not at beginning if (_index != 0 || _chunkIndex != 0) { @@ -236,32 +212,13 @@ public: _index--; } } - return *this; } Iterator operator--(int) { - ASSERT(_collection); Iterator temp = *this; - - // Check if it's not at beginning - if (_index != 0 || _chunkIndex != 0) - { - // Check if need to change chunk - if (_index == 0) - { - // Move to previous chunk - _chunkIndex--; - _index = ChunkSize - 1; - } - else - { - // Move backward within chunk - _index--; - } - } - + --temp; return temp; } }; From b56447bae43f1abd158644d33b0edbbdbaaffeec Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 8 Oct 2023 15:14:35 +0200 Subject: [PATCH 50/61] Refactor code in #1423 to be cleaner --- Source/Editor/Editor.cs | 7 +- .../SourceCodeEditing/CodeEditingModule.cs | 74 +++++ .../Windows/ContentWindow.ContextMenu.cs | 275 +++++++++--------- Source/Editor/Windows/ContentWindow.cs | 76 +---- 4 files changed, 215 insertions(+), 217 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index e0c460d0c..b6eb4ca31 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -8,7 +8,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using FlaxEditor.Content; -using FlaxEditor.Content.Import; using FlaxEditor.Content.Settings; using FlaxEditor.Content.Thumbnails; using FlaxEditor.Modules; @@ -154,12 +153,12 @@ namespace FlaxEditor public ContentFindingModule ContentFinding; /// - /// The scripts editing + /// The scripts editing. /// public CodeEditingModule CodeEditing; /// - /// The scripts documentation + /// The scripts documentation. /// public CodeDocsModule CodeDocs; @@ -179,7 +178,7 @@ namespace FlaxEditor public ProjectCacheModule ProjectCache; /// - /// The undo/redo + /// The undo/redo. /// public EditorUndo Undo; diff --git a/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs b/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs index c61fad713..d7a638598 100644 --- a/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs +++ b/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs @@ -2,8 +2,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; +using System.Text; using FlaxEditor.Options; using FlaxEditor.Scripting; using FlaxEngine; @@ -325,6 +327,78 @@ namespace FlaxEditor.Modules.SourceCodeEditing Editor.Instance.CodeEditing.SelectedEditor = editor; } + /// + /// Starts creating a new module + /// + internal void CreateModule(string path, string moduleName, bool editorModule, bool cpp) + { + if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(path)) + { + Editor.LogWarning("Failed to create module due to no name"); + return; + } + + // Create folder + var moduleFolderPath = Path.Combine(path, moduleName); + Directory.CreateDirectory(moduleFolderPath); + + // Create module + var moduleText = "using Flax.Build;\n" + + "using Flax.Build.NativeCpp;\n" + + $"\npublic class {moduleName} : Game{(editorModule ? "Editor" : "")}Module\n" + + "{\n " + + "/// \n" + + " public override void Init()\n" + + " {\n" + + " base.Init();\n" + + "\n" + + " // C#-only scripting if false\n" + + $" BuildNativeCode = {(cpp ? "true" : "false")};\n" + + " }\n" + + "\n" + + " /// \n" + + " public override void Setup(BuildOptions options)\n" + + " {" + + "\n" + + " base.Setup(options);\n" + + "\n" + + " options.ScriptingAPI.IgnoreMissingDocumentationWarnings = true;\n" + + "\n" + + " // Here you can modify the build options for your game module\n" + + " // To reference another module use: options.PublicDependencies.Add(\"Audio\");\n" + + " // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n" + + " // To learn more see scripting documentation.\n" + + " }\n" + + "}"; + moduleText = Encoding.UTF8.GetString(Encoding.Default.GetBytes(moduleText)); + var modulePath = Path.Combine(moduleFolderPath, $"{moduleName}.Build.cs"); + File.WriteAllText(modulePath, moduleText); + Editor.Log($"Module created at {modulePath}"); + + // Get editor target and target files and add module + var files = Directory.GetFiles(path); + var targetModuleText = $"Modules.Add(\"{moduleName}\");\n "; + foreach (var file in files) + { + if (!file.Contains(".Build.cs", StringComparison.OrdinalIgnoreCase)) + continue; + var targetText = File.ReadAllText(file); + + // Skip game project if it is suppose to be an editor module + if (editorModule && targetText.Contains("GameProjectTarget", StringComparison.Ordinal)) + continue; + + // TODO: Handle edge case when there are no modules in a target + var index = targetText.IndexOf("Modules.Add"); + if (index != -1) + { + var newText = targetText.Insert(index, targetModuleText); + File.WriteAllText(file, newText); + Editor.Log($"Module added to Target: {file}"); + } + } + } + /// public override void OnUpdate() { diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index f10175cce..03873df57 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -150,145 +150,11 @@ namespace FlaxEditor.Windows cm.AddSeparator(); // Check if is source folder to add new module - if (item is ContentFolder sourceFolder && sourceFolder.ParentFolder.Node is ProjectTreeNode node) + if (folder?.ParentFolder?.Node is ProjectTreeNode parentFolderNode && folder.Node == parentFolderNode.Source) { - if (sourceFolder.Node == node.Source) - { - var button = cm.AddButton("New Module"); - button.CloseMenuOnClick = false; - button.Clicked += () => - { - var popup = new ContextMenuBase - { - Size = new Float2(230, 125), - ClipChildren = false, - CullChildren = false, - }; - popup.Show(button, new Float2(button.Width, 0)); - - var nameLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Name", - HorizontalAlignment = TextAlignment.Near, - }; - nameLabel.LocalX += 10; - nameLabel.LocalY += 10; - - var nameTextBox = new TextBox - { - Parent = popup, - WatermarkText = "Module Name", - AnchorPreset = AnchorPresets.TopLeft, - IsMultiline = false, - }; - nameTextBox.LocalX += 100; - nameTextBox.LocalY += 10; - var defaultTextBoxBorderColor = nameTextBox.BorderColor; - var defaultTextBoxBorderSelectedColor = nameTextBox.BorderSelectedColor; - nameTextBox.TextChanged += () => - { - if (string.IsNullOrEmpty(nameTextBox.Text)) - { - nameTextBox.BorderColor = defaultTextBoxBorderColor; - nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; - return; - } - - var pluginPath = Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text); - if (Directory.Exists(pluginPath)) - { - nameTextBox.BorderColor = Color.Red; - nameTextBox.BorderSelectedColor = Color.Red; - } - else - { - nameTextBox.BorderColor = defaultTextBoxBorderColor; - nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; - } - }; - - var editorLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Editor", - HorizontalAlignment = TextAlignment.Near, - }; - editorLabel.LocalX += 10; - editorLabel.LocalY += 35; - - var editorCheckBox = new CheckBox() - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - }; - editorCheckBox.LocalY += 35; - editorCheckBox.LocalX += 100; - - var cppLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "C++", - HorizontalAlignment = TextAlignment.Near, - }; - cppLabel.LocalX += 10; - cppLabel.LocalY += 60; - - var cppCheckBox = new CheckBox() - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - }; - cppCheckBox.LocalY += 60; - cppCheckBox.LocalX += 100; - - var submitButton = new Button - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Create", - Width = 70, - }; - submitButton.LocalX += 40; - submitButton.LocalY += 90; - - submitButton.Clicked += () => - { - // TODO: Check all modules in project including plugins - if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text))) - { - Editor.LogWarning("Cannot create module due to name conflict."); - return; - } - CreateModule(node.Source.Path, nameTextBox.Text, editorCheckBox.Checked, cppCheckBox.Checked); - nameTextBox.Clear(); - editorCheckBox.Checked = false; - cppCheckBox.Checked = false; - popup.Hide(); - }; - - var cancelButton = new Button - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Cancel", - Width = 70, - }; - cancelButton.LocalX += 120; - cancelButton.LocalY += 90; - - cancelButton.Clicked += () => - { - nameTextBox.Clear(); - editorCheckBox.Checked = false; - cppCheckBox.Checked = false; - popup.Hide(); - }; - }; - } + var button = cm.AddButton("New module"); + button.CloseMenuOnClick = false; + button.Clicked += () => NewModule(button, parentFolderNode.Source.Path); } if (!isRootFolder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectTreeNode)) @@ -456,5 +322,138 @@ namespace FlaxEditor.Windows return; } } + + private void NewModule(ContextMenuButton button, string path) + { + var popup = new ContextMenuBase + { + Size = new Float2(230, 125), + ClipChildren = false, + CullChildren = false, + }; + popup.Show(button, new Float2(button.Width, 0)); + + var nameLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Name", + HorizontalAlignment = TextAlignment.Near, + }; + nameLabel.LocalX += 10; + nameLabel.LocalY += 10; + + var nameTextBox = new TextBox + { + Parent = popup, + WatermarkText = "Module Name", + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + nameTextBox.LocalX += 100; + nameTextBox.LocalY += 10; + var defaultTextBoxBorderColor = nameTextBox.BorderColor; + var defaultTextBoxBorderSelectedColor = nameTextBox.BorderSelectedColor; + nameTextBox.TextChanged += () => + { + if (string.IsNullOrEmpty(nameTextBox.Text)) + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + return; + } + + var pluginPath = Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text); + if (Directory.Exists(pluginPath)) + { + nameTextBox.BorderColor = Color.Red; + nameTextBox.BorderSelectedColor = Color.Red; + } + else + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + } + }; + + var editorLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Editor", + HorizontalAlignment = TextAlignment.Near, + }; + editorLabel.LocalX += 10; + editorLabel.LocalY += 35; + + var editorCheckBox = new CheckBox + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + }; + editorCheckBox.LocalY += 35; + editorCheckBox.LocalX += 100; + + var cppLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "C++", + HorizontalAlignment = TextAlignment.Near, + }; + cppLabel.LocalX += 10; + cppLabel.LocalY += 60; + + var cppCheckBox = new CheckBox + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + }; + cppCheckBox.LocalY += 60; + cppCheckBox.LocalX += 100; + + var submitButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Create", + Width = 70, + }; + submitButton.LocalX += 40; + submitButton.LocalY += 90; + submitButton.Clicked += () => + { + // TODO: Check all modules in project including plugins + if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text))) + { + Editor.LogWarning("Cannot create module due to name conflict."); + return; + } + Editor.CodeEditing.CreateModule(path, nameTextBox.Text, editorCheckBox.Checked, cppCheckBox.Checked); + nameTextBox.Clear(); + editorCheckBox.Checked = false; + cppCheckBox.Checked = false; + popup.Hide(); + button.ParentContextMenu.Hide(); + }; + + var cancelButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Cancel", + Width = 70, + }; + cancelButton.LocalX += 120; + cancelButton.LocalY += 90; + cancelButton.Clicked += () => + { + nameTextBox.Clear(); + editorCheckBox.Checked = false; + cppCheckBox.Checked = false; + popup.Hide(); + button.ParentContextMenu.Hide(); + }; + } } } diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 1210b021c..6b5855238 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Text; using System.Xml; using FlaxEditor.Content; using FlaxEditor.Content.GUI; @@ -330,7 +329,7 @@ namespace FlaxEditor.Windows b.Checked = ShowPluginsFiles; b.CloseMenuOnClick = false; b.AutoCheck = true; - + b = show.ContextMenu.AddButton("Generated files", () => ShowGeneratedFiles = !ShowGeneratedFiles); b.TooltipText = "Shows generated files"; b.Checked = ShowGeneratedFiles; @@ -756,79 +755,6 @@ namespace FlaxEditor.Windows Editor.ContentImporting.Import(importFiles, CurrentViewFolder); } - /// - /// Starts creating a new module - /// - private async void CreateModule(string path, string moduleName, bool editorModule, bool cpp) - { - if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(path)) - { - Editor.LogWarning("Failed to create module due to no name"); - return; - } - - // Create folder - var moduleFolderPath = Path.Combine(path, moduleName); - Directory.CreateDirectory(moduleFolderPath); - - // Create module - var moduleText = "using Flax.Build;\n" + - "using Flax.Build.NativeCpp;\n" + - $"\npublic class {moduleName} : Game{(editorModule ? "Editor" : "")}Module\n" + - "{\n " + - "/// \n" + - " public override void Init()\n" + - " {\n" + - " base.Init();\n" + - "\n" + - " // C#-only scripting if false\n" + - $" BuildNativeCode = {(cpp ? "true" : "false")};\n" + - " }\n" + - "\n" + - " /// \n" + - " public override void Setup(BuildOptions options)\n" + - " {" + - "\n" + - " base.Setup(options);\n" + - "\n" + - " options.ScriptingAPI.IgnoreMissingDocumentationWarnings = true;\n" + - "\n" + - " // Here you can modify the build options for your game module\n" + - " // To reference another module use: options.PublicDependencies.Add(\"Audio\");\n" + - " // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n" + - " // To learn more see scripting documentation.\n" + - " }\n" + - "}"; - moduleText = Encoding.UTF8.GetString(Encoding.Default.GetBytes(moduleText)); - var modulePath = Path.Combine(moduleFolderPath, $"{moduleName}.Build.cs"); - await File.WriteAllTextAsync(modulePath, moduleText); - Editor.Log($"Module created at {modulePath}"); - - // Get editor target and target files and add module - var files = Directory.GetFiles(path); - var targetModuleText = $"Modules.Add(\"{moduleName}\");\n "; - foreach (var file in files) - { - if (!file.Contains(".Build.cs", StringComparison.OrdinalIgnoreCase)) - continue; - - var targetText = await File.ReadAllTextAsync(file); - - // Skip game project if it is suppose to be an editor module - if (editorModule && targetText.Contains("GameProjectTarget", StringComparison.Ordinal)) - continue; - - // TODO: Handle edge case when there are no modules in a target - var index = targetText.IndexOf("Modules.Add"); - if (index != -1) - { - var newText = targetText.Insert(index, targetModuleText); - await File.WriteAllTextAsync(file, newText); - Editor.Log($"Module added to Target: {file}"); - } - } - } - /// /// Starts creating the folder. /// From 734f8bcaf4694558ac82addb714976d434f2b4ac Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 8 Oct 2023 15:52:36 +0200 Subject: [PATCH 51/61] Fix shader compilation tracking to check for directory existence --- Source/Engine/ShadersCompilation/ShadersCompilation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/ShadersCompilation/ShadersCompilation.cpp b/Source/Engine/ShadersCompilation/ShadersCompilation.cpp index 0d16e4f7c..ce4116134 100644 --- a/Source/Engine/ShadersCompilation/ShadersCompilation.cpp +++ b/Source/Engine/ShadersCompilation/ShadersCompilation.cpp @@ -301,7 +301,7 @@ void ShadersCompilation::RegisterForShaderReloads(Asset* asset, const String& in { // Create a directory watcher to track the included file changes const String directory = StringUtils::GetDirectoryName(includedPath); - if (!ShaderIncludesWatcher.ContainsKey(directory)) + if (FileSystem::DirectoryExists(directory) && !ShaderIncludesWatcher.ContainsKey(directory)) { auto watcher = New(directory, false); watcher->OnEvent.Bind(); From 13853d5fb54d041eca7ea3322d234f0d93a18b5a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 8 Oct 2023 16:01:34 +0200 Subject: [PATCH 52/61] Fix model asset previews to wait for materials to be loaded --- Source/Editor/Content/Proxy/ModelProxy.cs | 6 ++++++ Source/Editor/Content/Proxy/SkinnedModelProxy.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/Source/Editor/Content/Proxy/ModelProxy.cs b/Source/Editor/Content/Proxy/ModelProxy.cs index 845cbc80b..c78863e5e 100644 --- a/Source/Editor/Content/Proxy/ModelProxy.cs +++ b/Source/Editor/Content/Proxy/ModelProxy.cs @@ -87,6 +87,12 @@ namespace FlaxEditor.Content // Check if asset is streamed enough var asset = (Model)request.Asset; + var slots = asset.MaterialSlots; + foreach (var slot in slots) + { + if (slot.Material && !slot.Material.IsLoaded) + return false; + } return asset.LoadedLODs >= Mathf.Max(1, (int)(asset.LODs.Length * ThumbnailsModule.MinimumRequiredResourcesQuality)); } diff --git a/Source/Editor/Content/Proxy/SkinnedModelProxy.cs b/Source/Editor/Content/Proxy/SkinnedModelProxy.cs index f0193d0d0..4fd71e933 100644 --- a/Source/Editor/Content/Proxy/SkinnedModelProxy.cs +++ b/Source/Editor/Content/Proxy/SkinnedModelProxy.cs @@ -62,6 +62,12 @@ namespace FlaxEditor.Content var lods = asset.LODs.Length; if (asset.IsLoaded && lods == 0) return true; // Skeleton-only model + var slots = asset.MaterialSlots; + foreach (var slot in slots) + { + if (slot.Material && !slot.Material.IsLoaded) + return false; + } return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * ThumbnailsModule.MinimumRequiredResourcesQuality)); } From 4db1975f0291d1ce10234165ec62555ebc9b83f6 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 8 Oct 2023 17:28:28 +0300 Subject: [PATCH 53/61] Fix Editor viewport camera drifting with odd viewport sizes --- Source/Editor/Viewport/EditorViewport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 0c8bfc07d..13c06157a 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1022,7 +1022,7 @@ namespace FlaxEditor.Viewport // Center mouse position if it's too close to the edge var size = Size; - var center = size * 0.5f; + var center = Float2.Round(size * 0.5f); if (Mathf.Abs(_viewMousePos.X - center.X) > center.X * 0.8f || Mathf.Abs(_viewMousePos.Y - center.Y) > center.Y * 0.8f) { _viewMousePos = center; From 38664e9d43db220b5de623cf8cd732345839e25c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 8 Oct 2023 16:34:41 +0200 Subject: [PATCH 54/61] More improvements to 13853d5fb54d041eca7ea3322d234f0d93a18b5a --- .../Editor/Content/Proxy/CubeTextureProxy.cs | 7 +- .../Content/Proxy/MaterialInstanceProxy.cs | 2 +- Source/Editor/Content/Proxy/MaterialProxy.cs | 2 +- Source/Editor/Content/Proxy/ModelProxy.cs | 13 +--- .../Editor/Content/Proxy/SkinnedModelProxy.cs | 16 +---- Source/Editor/Content/Proxy/TextureProxy.cs | 6 +- .../Content/Thumbnails/ThumbnailsModule.cs | 68 +++++++++++++++++++ 7 files changed, 74 insertions(+), 40 deletions(-) diff --git a/Source/Editor/Content/Proxy/CubeTextureProxy.cs b/Source/Editor/Content/Proxy/CubeTextureProxy.cs index 691e4ac53..0477ad18e 100644 --- a/Source/Editor/Content/Proxy/CubeTextureProxy.cs +++ b/Source/Editor/Content/Proxy/CubeTextureProxy.cs @@ -54,12 +54,7 @@ namespace FlaxEditor.Content /// public override bool CanDrawThumbnail(ThumbnailRequest request) { - if (!_preview.HasLoadedAssets) - return false; - - // Check if all mip maps are streamed - var asset = (CubeTexture)request.Asset; - return asset.ResidentMipLevels >= Mathf.Max(1, (int)(asset.MipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality)); + return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Texture)request.Asset); } /// diff --git a/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs b/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs index dcc3e3ec4..331ff81c3 100644 --- a/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs +++ b/Source/Editor/Content/Proxy/MaterialInstanceProxy.cs @@ -62,7 +62,7 @@ namespace FlaxEditor.Content /// public override bool CanDrawThumbnail(ThumbnailRequest request) { - return _preview.HasLoadedAssets; + return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((MaterialInstance)request.Asset); } /// diff --git a/Source/Editor/Content/Proxy/MaterialProxy.cs b/Source/Editor/Content/Proxy/MaterialProxy.cs index cf7c8b77e..a7fcfecc8 100644 --- a/Source/Editor/Content/Proxy/MaterialProxy.cs +++ b/Source/Editor/Content/Proxy/MaterialProxy.cs @@ -106,7 +106,7 @@ namespace FlaxEditor.Content /// public override bool CanDrawThumbnail(ThumbnailRequest request) { - return _preview.HasLoadedAssets; + return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Material)request.Asset); } /// diff --git a/Source/Editor/Content/Proxy/ModelProxy.cs b/Source/Editor/Content/Proxy/ModelProxy.cs index c78863e5e..0cf16850d 100644 --- a/Source/Editor/Content/Proxy/ModelProxy.cs +++ b/Source/Editor/Content/Proxy/ModelProxy.cs @@ -82,18 +82,7 @@ namespace FlaxEditor.Content /// public override bool CanDrawThumbnail(ThumbnailRequest request) { - if (!_preview.HasLoadedAssets) - return false; - - // Check if asset is streamed enough - var asset = (Model)request.Asset; - var slots = asset.MaterialSlots; - foreach (var slot in slots) - { - if (slot.Material && !slot.Material.IsLoaded) - return false; - } - return asset.LoadedLODs >= Mathf.Max(1, (int)(asset.LODs.Length * ThumbnailsModule.MinimumRequiredResourcesQuality)); + return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Model)request.Asset); } /// diff --git a/Source/Editor/Content/Proxy/SkinnedModelProxy.cs b/Source/Editor/Content/Proxy/SkinnedModelProxy.cs index 4fd71e933..6e228aa86 100644 --- a/Source/Editor/Content/Proxy/SkinnedModelProxy.cs +++ b/Source/Editor/Content/Proxy/SkinnedModelProxy.cs @@ -54,21 +54,7 @@ namespace FlaxEditor.Content /// public override bool CanDrawThumbnail(ThumbnailRequest request) { - if (!_preview.HasLoadedAssets) - return false; - - // Check if asset is streamed enough - var asset = (SkinnedModel)request.Asset; - var lods = asset.LODs.Length; - if (asset.IsLoaded && lods == 0) - return true; // Skeleton-only model - var slots = asset.MaterialSlots; - foreach (var slot in slots) - { - if (slot.Material && !slot.Material.IsLoaded) - return false; - } - return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * ThumbnailsModule.MinimumRequiredResourcesQuality)); + return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((SkinnedModel)request.Asset); } /// diff --git a/Source/Editor/Content/Proxy/TextureProxy.cs b/Source/Editor/Content/Proxy/TextureProxy.cs index 48bfcc9e9..709ca4dbf 100644 --- a/Source/Editor/Content/Proxy/TextureProxy.cs +++ b/Source/Editor/Content/Proxy/TextureProxy.cs @@ -57,11 +57,7 @@ namespace FlaxEditor.Content /// public override bool CanDrawThumbnail(ThumbnailRequest request) { - // Check if asset is streamed enough - var asset = (Texture)request.Asset; - var mipLevels = asset.MipLevels; - var minMipLevels = Mathf.Min(mipLevels, 7); - return asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality)); + return ThumbnailsModule.HasMinimumQuality((Texture)request.Asset); } /// diff --git a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs index a6996310e..f1889d9ed 100644 --- a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs +++ b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs @@ -125,6 +125,74 @@ namespace FlaxEditor.Content.Thumbnails } } + internal static bool HasMinimumQuality(TextureBase asset) + { + var mipLevels = asset.MipLevels; + var minMipLevels = Mathf.Min(mipLevels, 7); + return asset.IsLoaded && asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * MinimumRequiredResourcesQuality)); + } + + internal static bool HasMinimumQuality(Model asset) + { + if (!asset.IsLoaded) + return false; + var lods = asset.LODs.Length; + var slots = asset.MaterialSlots; + foreach (var slot in slots) + { + if (slot.Material && !HasMinimumQuality(slot.Material)) + return false; + } + return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * MinimumRequiredResourcesQuality)); + } + + internal static bool HasMinimumQuality(SkinnedModel asset) + { + var lods = asset.LODs.Length; + if (asset.IsLoaded && lods == 0) + return true; // Skeleton-only model + var slots = asset.MaterialSlots; + foreach (var slot in slots) + { + if (slot.Material && !HasMinimumQuality(slot.Material)) + return false; + } + return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * MinimumRequiredResourcesQuality)); + } + + internal static bool HasMinimumQuality(MaterialBase asset) + { + if (asset is MaterialInstance asInstance) + return HasMinimumQuality(asInstance); + return HasMinimumQualityInternal(asset); + } + + internal static bool HasMinimumQuality(Material asset) + { + return HasMinimumQualityInternal(asset); + } + + internal static bool HasMinimumQuality(MaterialInstance asset) + { + if (!HasMinimumQualityInternal(asset)) + return false; + var baseMaterial = asset.BaseMaterial; + return baseMaterial == null || HasMinimumQualityInternal(baseMaterial); + } + + private static bool HasMinimumQualityInternal(MaterialBase asset) + { + if (!asset.IsLoaded) + return false; + var parameters = asset.Parameters; + foreach (var parameter in parameters) + { + if (parameter.Value is TextureBase asTexture && !HasMinimumQuality(asTexture)) + return false; + } + return true; + } + #region IContentItemOwner /// From 552641c51a80b0ab30c7049c9f6ebf82b22fb47e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 8 Oct 2023 17:02:26 +0200 Subject: [PATCH 55/61] Fix potential crashes if uses calls terrain tools with invalid params --- Source/Editor/Tools/Terrain/TerrainTools.cpp | 15 ++++++++++++--- Source/Editor/Tools/Terrain/TerrainTools.h | 6 +++--- Source/Engine/Terrain/TerrainPatch.cpp | 12 +----------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Source/Editor/Tools/Terrain/TerrainTools.cpp b/Source/Editor/Tools/Terrain/TerrainTools.cpp index 545e60d54..89d0aa17d 100644 --- a/Source/Editor/Tools/Terrain/TerrainTools.cpp +++ b/Source/Editor/Tools/Terrain/TerrainTools.cpp @@ -285,6 +285,7 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches StringAnsi TerrainTools::SerializePatch(Terrain* terrain, const Int2& patchCoord) { + CHECK_RETURN(terrain, StringAnsi::Empty); auto patch = terrain->GetPatch(patchCoord); CHECK_RETURN(patch, StringAnsi::Empty); @@ -300,6 +301,7 @@ StringAnsi TerrainTools::SerializePatch(Terrain* terrain, const Int2& patchCoord void TerrainTools::DeserializePatch(Terrain* terrain, const Int2& patchCoord, const StringAnsiView& value) { + CHECK(terrain); auto patch = terrain->GetPatch(patchCoord); CHECK(patch); @@ -317,27 +319,31 @@ void TerrainTools::DeserializePatch(Terrain* terrain, const Int2& patchCoord, co bool TerrainTools::InitializePatch(Terrain* terrain, const Int2& patchCoord) { + CHECK_RETURN(terrain, true); auto patch = terrain->GetPatch(patchCoord); CHECK_RETURN(patch, true); return patch->InitializeHeightMap(); } -bool TerrainTools::ModifyHeightMap(Terrain* terrain, const Int2& patchCoord, float* samples, const Int2& offset, const Int2& size) +bool TerrainTools::ModifyHeightMap(Terrain* terrain, const Int2& patchCoord, const float* samples, const Int2& offset, const Int2& size) { + CHECK_RETURN(terrain, true); auto patch = terrain->GetPatch(patchCoord); CHECK_RETURN(patch, true); return patch->ModifyHeightMap(samples, offset, size); } -bool TerrainTools::ModifyHolesMask(Terrain* terrain, const Int2& patchCoord, byte* samples, const Int2& offset, const Int2& size) +bool TerrainTools::ModifyHolesMask(Terrain* terrain, const Int2& patchCoord, const byte* samples, const Int2& offset, const Int2& size) { + CHECK_RETURN(terrain, true); auto patch = terrain->GetPatch(patchCoord); CHECK_RETURN(patch, true); return patch->ModifyHolesMask(samples, offset, size); } -bool TerrainTools::ModifySplatMap(Terrain* terrain, const Int2& patchCoord, int32 index, Color32* samples, const Int2& offset, const Int2& size) +bool TerrainTools::ModifySplatMap(Terrain* terrain, const Int2& patchCoord, int32 index, const Color32* samples, const Int2& offset, const Int2& size) { + CHECK_RETURN(terrain, true); auto patch = terrain->GetPatch(patchCoord); CHECK_RETURN(patch, true); CHECK_RETURN(index >= 0 && index < TERRAIN_MAX_SPLATMAPS_COUNT, true); @@ -346,6 +352,7 @@ bool TerrainTools::ModifySplatMap(Terrain* terrain, const Int2& patchCoord, int3 float* TerrainTools::GetHeightmapData(Terrain* terrain, const Int2& patchCoord) { + CHECK_RETURN(terrain, nullptr); auto patch = terrain->GetPatch(patchCoord); CHECK_RETURN(patch, nullptr); return patch->GetHeightmapData(); @@ -353,6 +360,7 @@ float* TerrainTools::GetHeightmapData(Terrain* terrain, const Int2& patchCoord) byte* TerrainTools::GetHolesMaskData(Terrain* terrain, const Int2& patchCoord) { + CHECK_RETURN(terrain, nullptr); auto patch = terrain->GetPatch(patchCoord); CHECK_RETURN(patch, nullptr); return patch->GetHolesMaskData(); @@ -360,6 +368,7 @@ byte* TerrainTools::GetHolesMaskData(Terrain* terrain, const Int2& patchCoord) Color32* TerrainTools::GetSplatMapData(Terrain* terrain, const Int2& patchCoord, int32 index) { + CHECK_RETURN(terrain, nullptr); auto patch = terrain->GetPatch(patchCoord); CHECK_RETURN(patch, nullptr); return patch->GetSplatMapData(index); diff --git a/Source/Editor/Tools/Terrain/TerrainTools.h b/Source/Editor/Tools/Terrain/TerrainTools.h index 6f4682bf6..bb35c45c9 100644 --- a/Source/Editor/Tools/Terrain/TerrainTools.h +++ b/Source/Editor/Tools/Terrain/TerrainTools.h @@ -68,7 +68,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(TerrainTools); /// The offset from the first row and column of the heightmap data (offset destination x and z start position). /// The size of the heightmap to modify (x and z). Amount of samples in each direction. /// True if failed, otherwise false. - API_FUNCTION() static bool ModifyHeightMap(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, float* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size); + API_FUNCTION() static bool ModifyHeightMap(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, const float* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size); /// /// Modifies the terrain patch holes mask with the given samples. @@ -79,7 +79,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(TerrainTools); /// The offset from the first row and column of the mask data (offset destination x and z start position). /// The size of the mask to modify (x and z). Amount of samples in each direction. /// True if failed, otherwise false. - API_FUNCTION() static bool ModifyHolesMask(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, byte* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size); + API_FUNCTION() static bool ModifyHolesMask(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, const byte* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size); /// /// Modifies the terrain patch splat map (layers mask) with the given samples. @@ -91,7 +91,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(TerrainTools); /// The offset from the first row and column of the splatmap data (offset destination x and z start position). /// The size of the splatmap to modify (x and z). Amount of samples in each direction. /// True if failed, otherwise false. - API_FUNCTION() static bool ModifySplatMap(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, int32 index, Color32* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size); + API_FUNCTION() static bool ModifySplatMap(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, int32 index, const Color32* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size); /// /// Gets the raw pointer to the heightmap data (cached internally by the c++ core in editor). diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 92a8c09d3..8e05062c8 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -1126,12 +1126,9 @@ bool TerrainPatch::InitializeHeightMap() float* TerrainPatch::GetHeightmapData() { PROFILE_CPU_NAMED("Terrain.GetHeightmapData"); - if (_cachedHeightMap.HasItems()) return _cachedHeightMap.Get(); - CacheHeightData(); - return _cachedHeightMap.Get(); } @@ -1144,12 +1141,9 @@ void TerrainPatch::ClearHeightmapCache() byte* TerrainPatch::GetHolesMaskData() { PROFILE_CPU_NAMED("Terrain.GetHolesMaskData"); - if (_cachedHolesMask.HasItems()) return _cachedHolesMask.Get(); - CacheHeightData(); - return _cachedHolesMask.Get(); } @@ -1161,15 +1155,11 @@ void TerrainPatch::ClearHolesMaskCache() Color32* TerrainPatch::GetSplatMapData(int32 index) { - ASSERT(index >= 0 && index < TERRAIN_MAX_SPLATMAPS_COUNT); - + CHECK_RETURN(index >= 0 && index < TERRAIN_MAX_SPLATMAPS_COUNT, nullptr); PROFILE_CPU_NAMED("Terrain.GetSplatMapData"); - if (_cachedSplatMap[index].HasItems()) return _cachedSplatMap[index].Get(); - CacheSplatData(); - return _cachedSplatMap[index].Get(); } From 03642632f55f1d265bf0d02f8e2a430316a96426 Mon Sep 17 00:00:00 2001 From: minebill Date: Sun, 8 Oct 2023 21:45:34 +0300 Subject: [PATCH 56/61] Use a maximum distance between clicks, when detecting a double click --- Source/Engine/Platform/Linux/LinuxWindow.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Linux/LinuxWindow.cpp b/Source/Engine/Platform/Linux/LinuxWindow.cpp index e6fe63f9c..5f8a471b8 100644 --- a/Source/Engine/Platform/Linux/LinuxWindow.cpp +++ b/Source/Engine/Platform/Linux/LinuxWindow.cpp @@ -41,8 +41,10 @@ extern Dictionary KeyNameMap; extern Array KeyCodeMap; extern X11::Cursor Cursors[(int32)CursorType::MAX]; -static const uint32 MouseDoubleClickTime = 500; +static constexpr uint32 MouseDoubleClickTime = 500; +static constexpr uint32 MaxDoubleClickDistanceSquared = 10; static X11::Time MouseLastButtonPressTime = 0; +static Float2 OldMouseClickPosition; LinuxWindow::LinuxWindow(const CreateWindowSettings& settings) : WindowBase(settings) @@ -597,15 +599,19 @@ void LinuxWindow::OnButtonPress(void* event) // Handle double-click if (buttonEvent->button == Button1) { - if (buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime)) + if ( + buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime) && + Float2::DistanceSquared(mousePos, OldMouseClickPosition) < MaxDoubleClickDistanceSquared) { Input::Mouse->OnMouseDoubleClick(ClientToScreen(mousePos), mouseButton, this); MouseLastButtonPressTime = 0; + OldMouseClickPosition = mousePos; return; } else { MouseLastButtonPressTime = buttonEvent->time; + OldMouseClickPosition = mousePos; } } From 5b866b643be559c41662f5e67ee4fecbbcc1f2f8 Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Sun, 8 Oct 2023 22:05:53 +0200 Subject: [PATCH 57/61] fix GetDesktopSize for Linux --- .../Engine/Platform/Linux/LinuxPlatform.cpp | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index af2c02ca6..4787b4549 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -2700,6 +2700,8 @@ Float2 LinuxPlatform::GetDesktopSize() if (screenIdx >= count) return Float2::Zero; + // this function is used as a fallback to place a window at the center of + // a screen so we report only one screen instead of the real desktop Float2 size((float)xsi[screenIdx].width, (float)xsi[screenIdx].height); X11::XFree(xsi); return size; @@ -2739,8 +2741,40 @@ Rectangle LinuxPlatform::GetMonitorBounds(const Float2& screenPos) Rectangle LinuxPlatform::GetVirtualDesktopBounds() { - // 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; + X11::XineramaScreenInfo* xsi = X11::XineramaQueryScreens(xDisplay, &count); + if (count <= 0) + return Rectangle::Empty; + // get all screen dimensions and assume the monitors form a rectangle + // as you can arrange monitors to your liking this is not necessarily the case + int minX = INT32_MAX, minY = INT32_MAX; + int maxX = 0, maxY = 0; + for (int i = 0; i < count; i++) + { + int maxScreenX = xsi[i].x_org + xsi[i].width; + int maxScreenY = xsi[i].y_org + xsi[i].height; + if (maxScreenX > maxX) + maxX = maxScreenX; + if (maxScreenY > maxY) + maxY = maxScreenY; + if (minX > xsi[i].x_org) + minX = xsi[i].x_org; + if (minY > xsi[i].y_org) + minY = xsi[i].y_org; + } + + Float2 org(static_cast(minX), static_cast(minY)); + Float2 size(static_cast(maxX - minX), static_cast(maxY - minY)); + X11::XFree(xsi); + return Rectangle(org, size); } String LinuxPlatform::GetMainDirectory() From 481a3cfba57bb39dc4eb2a415d2243409e37fd2b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 8 Oct 2023 23:33:51 +0200 Subject: [PATCH 58/61] Minor tweaks --- .../Vulkan/GPUContextVulkan.cpp | 30 ++++++------------- .../GraphicsDevice/Vulkan/GPUContextVulkan.h | 1 - 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index b0bb1c733..6bf6ecf81 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -649,22 +649,6 @@ void GPUContextVulkan::UpdateDescriptorSets(ComputePipelineStateVulkan* pipeline } } -void GPUContextVulkan::BindPipeline() -{ - if (_psDirtyFlag && _currentState && (_rtDepth || _rtCount)) - { - // Clear flag - _psDirtyFlag = false; - - // Change state - const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); - const auto pipeline = _currentState->GetState(_renderPass); - vkCmdBindPipeline(cmdBuffer->GetHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - - RENDER_STAT_PS_STATE_CHANGE(); - } -} - void GPUContextVulkan::OnDrawCall() { GPUPipelineStateVulkan* pipelineState = _currentState; @@ -704,7 +688,15 @@ void GPUContextVulkan::OnDrawCall() BeginRenderPass(); } - BindPipeline(); + // Bind pipeline + if (_psDirtyFlag && _currentState && (_rtDepth || _rtCount)) + { + _psDirtyFlag = false; + const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); + const auto pipeline = _currentState->GetState(_renderPass); + vkCmdBindPipeline(cmdBuffer->GetHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + RENDER_STAT_PS_STATE_CHANGE(); + } //UpdateDynamicStates(); @@ -1104,9 +1096,7 @@ void GPUContextVulkan::Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCo EndRenderPass(); auto pipelineState = shaderVulkan->GetOrCreateState(); - UpdateDescriptorSets(pipelineState); - FlushBarriers(); // Bind pipeline @@ -1141,10 +1131,8 @@ void GPUContextVulkan::DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* b EndRenderPass(); auto pipelineState = shaderVulkan->GetOrCreateState(); - UpdateDescriptorSets(pipelineState); AddBufferBarrier(bufferForArgsVulkan, VK_ACCESS_INDIRECT_COMMAND_READ_BIT); - FlushBarriers(); // Bind pipeline diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h index 63224b2d0..4d63e5854 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h @@ -160,7 +160,6 @@ private: void UpdateDescriptorSets(const struct SpirvShaderDescriptorInfo& descriptorInfo, class DescriptorSetWriterVulkan& dsWriter, bool& needsWrite); void UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineState); void UpdateDescriptorSets(ComputePipelineStateVulkan* pipelineState); - void BindPipeline(); void OnDrawCall(); public: From d7e9e2ed1679c527790a149120c6a7a33b993824 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 9 Oct 2023 11:33:02 +0200 Subject: [PATCH 59/61] Fix `AssetsCache` to include project path and reject cache when project gets duplicated with cache #1631 --- Flax.flaxproj | 2 +- Source/Engine/Content/Cache/AssetsCache.cpp | 49 ++++++++------------- Source/Engine/Content/Cache/AssetsCache.h | 11 +---- 3 files changed, 21 insertions(+), 41 deletions(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index fbf48cc2b..263ba739f 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -3,7 +3,7 @@ "Version": { "Major": 1, "Minor": 6, - "Build": 6345 + "Build": 6346 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.", diff --git a/Source/Engine/Content/Cache/AssetsCache.cpp b/Source/Engine/Content/Cache/AssetsCache.cpp index ddb7159f9..c9a10f721 100644 --- a/Source/Engine/Content/Cache/AssetsCache.cpp +++ b/Source/Engine/Content/Cache/AssetsCache.cpp @@ -15,15 +15,8 @@ #include "Engine/Engine/Globals.h" #include "FlaxEngine.Gen.h" -AssetsCache::AssetsCache() - : _isDirty(false) - , _registry(4096) -{ -} - void AssetsCache::Init() { - // Cache data Entry e; int32 count; const DateTime loadStartTime = DateTime::Now(); @@ -32,13 +25,11 @@ void AssetsCache::Init() #else _path = Globals::ProjectContentFolder / TEXT("AssetsCache.dat"); #endif - LOG(Info, "Loading Asset Cache {0}...", _path); // Check if assets registry exists if (!FileSystem::FileExists(_path)) { - // Back _isDirty = true; LOG(Warning, "Cannot find assets cache file"); return; @@ -49,7 +40,6 @@ void AssetsCache::Init() DeleteMe deleteStream(stream); // Load version - // Note: every Engine build is using different assets cache int32 version; stream->ReadInt32(&version); if (version != FLAXENGINE_VERSION_BUILD) @@ -58,24 +48,28 @@ void AssetsCache::Init() return; } - // Load Engine workspace path - String workspacePath; - stream->ReadString(&workspacePath, -410); + // Load paths + String enginePath, projectPath; + stream->ReadString(&enginePath, -410); + stream->ReadString(&projectPath, -410); // Flags AssetsCacheFlags flags; stream->ReadInt32((int32*)&flags); - // Check if other engine instance used this cache (cache depends on engine build and install location) - // Skip it for relative paths mode - if (!(flags & AssetsCacheFlags::RelativePaths) && workspacePath != Globals::StartupFolder) + // Check if other workspace instance used this cache + if (EnumHasNoneFlags(flags, AssetsCacheFlags::RelativePaths) && enginePath != Globals::StartupFolder) { - LOG(Warning, "Assets cache generated by the different engine installation in \'{0}\'", workspacePath); + LOG(Warning, "Assets cache generated by the different {1} installation in \'{0}\'", enginePath, TEXT("engine")); + return; + } + if (EnumHasNoneFlags(flags, AssetsCacheFlags::RelativePaths) && projectPath != Globals::ProjectFolder) + { + LOG(Warning, "Assets cache generated by the different {1} installation in \'{0}\'", projectPath, TEXT("project")); return; } ScopeLock lock(_locker); - _isDirty = false; // Load elements count @@ -103,15 +97,11 @@ void AssetsCache::Init() e.Info.Path = Globals::StartupFolder / e.Info.Path; } - // Validate entry - if (!IsEntryValid(e)) - { - // Reject + // Use only valid entries + if (IsEntryValid(e)) + _registry.Add(e.Info.ID, e); + else rejectedCount++; - continue; - } - - _registry.Add(e.Info.ID, e); } // Paths mapping @@ -148,7 +138,6 @@ void AssetsCache::Init() } } - // End const int32 loadTimeInMs = static_cast((DateTime::Now() - loadStartTime).GetTotalMilliseconds()); LOG(Info, "Asset Cache loaded {0} entries in {1} ms ({2} rejected)", _registry.Count(), loadTimeInMs, rejectedCount); } @@ -188,8 +177,9 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa // Version stream->WriteInt32(FLAXENGINE_VERSION_BUILD); - // Engine workspace path + // Paths stream->WriteString(Globals::StartupFolder, -410); + stream->WriteString(Globals::ProjectFolder, -410); // Flags stream->WriteInt32((int32)flags); @@ -202,7 +192,6 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa for (auto i = entries.Begin(); i.IsNotEnd(); ++i) { auto& e = i->Value; - stream->Write(e.Info.ID); stream->WriteString(e.Info.TypeName, index - 13); stream->WriteString(e.Info.Path, index); @@ -211,7 +200,6 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa #else stream->WriteInt64(0); #endif - index++; } @@ -222,7 +210,6 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa { stream->Write(i->Value); stream->WriteString(i->Key, index + 73); - index++; } diff --git a/Source/Engine/Content/Cache/AssetsCache.h b/Source/Engine/Content/Cache/AssetsCache.h index 4f12c0d22..6dd9471e3 100644 --- a/Source/Engine/Content/Cache/AssetsCache.h +++ b/Source/Engine/Content/Cache/AssetsCache.h @@ -74,7 +74,7 @@ public: typedef Dictionary PathsMapping; private: - bool _isDirty; + bool _isDirty = false; CriticalSection _locker; Registry _registry; PathsMapping _pathsMapping; @@ -82,15 +82,8 @@ private: public: /// - /// Initializes a new instance of the class. + /// Gets amount of registered assets. /// - AssetsCache(); - -public: - /// - /// Gets amount of registered assets - /// - /// Registry size int32 Size() const { _locker.Lock(); From 64d4076615c859c234fce619d9b7b99fc189e815 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 9 Oct 2023 12:06:55 +0200 Subject: [PATCH 60/61] Minor tweaks --- Source/Editor/Content/GUI/ContentView.cs | 4 ++-- .../Editor/Windows/Assets/JsonAssetWindow.cs | 7 ++---- Source/Engine/Content/Content.cpp | 22 +++++-------------- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 5e054e5c6..e1a3acdf6 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -520,8 +520,8 @@ namespace FlaxEditor.Content.GUI { int min = _selection.Min(x => x.IndexInParent); int max = _selection.Max(x => x.IndexInParent); - min = Mathf.Min(min, item.IndexInParent); - max = Mathf.Max(max, item.IndexInParent); + min = Mathf.Max(Mathf.Min(min, item.IndexInParent), 0); + max = Mathf.Min(Mathf.Max(max, item.IndexInParent), _children.Count - 1); var selection = new List(_selection); for (int i = min; i <= max; i++) { diff --git a/Source/Editor/Windows/Assets/JsonAssetWindow.cs b/Source/Editor/Windows/Assets/JsonAssetWindow.cs index 47ce09274..097d4992a 100644 --- a/Source/Editor/Windows/Assets/JsonAssetWindow.cs +++ b/Source/Editor/Windows/Assets/JsonAssetWindow.cs @@ -80,11 +80,8 @@ namespace FlaxEditor.Windows.Assets { if (!IsEdited) return; - if (_asset.WaitForLoaded()) - { return; - } if (Editor.SaveJsonAsset(_item.Path, _object)) { @@ -137,7 +134,7 @@ namespace FlaxEditor.Windows.Assets } } _presenter.Select(_object); - + if (_typeText != null) _typeText.Dispose(); var typeText = new ClickableLabel @@ -152,7 +149,7 @@ namespace FlaxEditor.Windows.Assets typeText.LocalY += (_toolstrip.Height - typeText.Height) * 0.5f; typeText.RightClick = () => Clipboard.Text = Asset.DataTypeName; _typeText = typeText; - + _undo.Clear(); ClearEditedFlag(); diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index 6aecd1778..ea627f2c7 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -125,16 +125,12 @@ void ContentService::LateUpdate() if (timeNow - LastUnloadCheckTime < Content::AssetsUpdateInterval) return; LastUnloadCheckTime = timeNow; - - Asset* asset; - ScopeLock lock(AssetsLocker); - - // TODO: maybe it would be better to link for asset remove ref event and cache only assets with no references - test it with millions of assets? + AssetsLocker.Lock(); // Verify all assets for (auto i = Assets.Begin(); i.IsNotEnd(); ++i) { - asset = i->Value; + Asset* asset = i->Value; // Check if has no references and is not during unloading if (asset->GetReferencesCount() <= 0 && !UnloadQueue.ContainsKey(asset)) @@ -158,7 +154,7 @@ void ContentService::LateUpdate() // Unload marked assets for (int32 i = 0; i < ToUnload.Count(); i++) { - asset = ToUnload[i]; + Asset* asset = ToUnload[i]; // Check if has no references if (asset->GetReferencesCount() <= 0) @@ -170,6 +166,8 @@ void ContentService::LateUpdate() UnloadQueue.Remove(asset); } + AssetsLocker.Unlock(); + // Update cache (for longer sessions it will help to reduce cache misses) Cache.Save(); } @@ -212,7 +210,6 @@ bool FindAssets(const ProjectInfo* project, HashSet& project { if (projects.Contains(project)) return false; - projects.Add(project); bool found = findAsset(id, project->ProjectFolderPath / TEXT("Content"), tmpCache, info); for (const auto& reference : project->References) @@ -220,7 +217,6 @@ bool FindAssets(const ProjectInfo* project, HashSet& project if (reference.Project) found |= FindAssets(reference.Project, projects, id, tmpCache, info); } - return found; } @@ -232,7 +228,6 @@ bool Content::GetAssetInfo(const Guid& id, AssetInfo& info) return false; #if ENABLE_ASSETS_DISCOVERY - // Find asset in registry if (Cache.FindAsset(id, info)) return true; @@ -270,19 +265,15 @@ bool Content::GetAssetInfo(const Guid& id, AssetInfo& info) //LOG(Warning, "Cannot find {0}.", id); return false; - #else - // Find asset in registry return Cache.FindAsset(id, info); - #endif } bool Content::GetAssetInfo(const StringView& path, AssetInfo& info) { #if ENABLE_ASSETS_DISCOVERY - // Find asset in registry if (Cache.FindAsset(path, info)) return true; @@ -326,12 +317,9 @@ bool Content::GetAssetInfo(const StringView& path, AssetInfo& info) } return false; - #else - // Find asset in registry return Cache.FindAsset(path, info); - #endif } From 8e23781153894bea467c30b634e1af1082516897 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 9 Oct 2023 12:13:09 +0200 Subject: [PATCH 61/61] Fix updating time left when destroying large amount of objects #1584 --- Source/Engine/Core/ObjectsRemovalService.cpp | 30 ++++++++------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/Source/Engine/Core/ObjectsRemovalService.cpp b/Source/Engine/Core/ObjectsRemovalService.cpp index 35458595e..bb3fd8bf1 100644 --- a/Source/Engine/Core/ObjectsRemovalService.cpp +++ b/Source/Engine/Core/ObjectsRemovalService.cpp @@ -73,28 +73,22 @@ void ObjectsRemovalService::Flush(float dt, float gameDelta) PoolLocker.Lock(); - int32 itemsLeft; - do + // Update timeouts and delete objects that timed out + for (auto i = Pool.Begin(); i.IsNotEnd(); ++i) { - // Update timeouts and delete objects that timed out - itemsLeft = Pool.Count(); - for (auto i = Pool.Begin(); i.IsNotEnd(); ++i) + auto& bucket = *i; + Object* obj = bucket.Key; + const float ttl = bucket.Value - ((obj->Flags & ObjectFlags::UseGameTimeForDelete) != ObjectFlags::None ? gameDelta : dt); + if (ttl <= 0.0f) { - Object* obj = i->Key; - const float ttl = i->Value - ((obj->Flags & ObjectFlags::UseGameTimeForDelete) != ObjectFlags::None ? gameDelta : dt); - if (ttl <= 0.0f) - { - Pool.Remove(i); - obj->OnDeleteObject(); - itemsLeft--; - } - else - { - i->Value = ttl; - } + Pool.Remove(i); + obj->OnDeleteObject(); + } + else + { + bucket.Value = ttl; } } - while (itemsLeft != Pool.Count()); // Continue removing if any new item was added during removing (eg. sub-object delete with 0 timeout) PoolLocker.Unlock(); }