From 93a0facd9331b13cc44affb33e4d13ba5858f927 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 29 Nov 2022 21:30:17 -0600 Subject: [PATCH 01/45] Allows user to left click to end editing text and right click to change text back to its original and end editing. --- Source/Editor/GUI/Input/SliderControl.cs | 1 + Source/Editor/GUI/Input/ValueBox.cs | 3 +++ Source/Engine/UI/GUI/Common/TextBoxBase.cs | 26 ++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/Source/Editor/GUI/Input/SliderControl.cs b/Source/Editor/GUI/Input/SliderControl.cs index 7a8b82622..8dc129d0a 100644 --- a/Source/Editor/GUI/Input/SliderControl.cs +++ b/Source/Editor/GUI/Input/SliderControl.cs @@ -371,6 +371,7 @@ namespace FlaxEditor.GUI.Input Parent = this, Location = new Float2(split, 0), Size = new Float2(Height, TextBoxSize), + CanEndEditByClick = true, }; _textBox.EditEnd += OnTextBoxEditEnd; } diff --git a/Source/Editor/GUI/Input/ValueBox.cs b/Source/Editor/GUI/Input/ValueBox.cs index 7f4fb51e0..6cfbbddfb 100644 --- a/Source/Editor/GUI/Input/ValueBox.cs +++ b/Source/Editor/GUI/Input/ValueBox.cs @@ -125,6 +125,7 @@ namespace FlaxEditor.GUI.Input _min = min; _max = max; _slideSpeed = sliderSpeed; + CanEndEditByClick = true; } /// @@ -173,6 +174,7 @@ namespace FlaxEditor.GUI.Input private void EndSliding() { _isSliding = false; + CanEndEditByClick = true; EndMouseCapture(); if (_cursorChanged) { @@ -245,6 +247,7 @@ namespace FlaxEditor.GUI.Input _startSlideLocation = location; _startSlideValue = _value; StartMouseCapture(true); + CanEndEditByClick = false; // Hide cursor and cache location Cursor = CursorType.Hidden; diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 69b4fa123..78ab040a2 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -139,6 +139,12 @@ namespace FlaxEngine.GUI /// public event Action KeyUp; + /// + /// Gets or sets a value indicating whether the text box can end edit via left click outside of the control + /// + [HideInEditor] + public bool CanEndEditByClick { get; set; } = false; + /// /// Gets or sets a value indicating whether this is a multiline text box control. /// @@ -1041,6 +1047,26 @@ namespace FlaxEngine.GUI // Animate view offset _viewOffset = isDeltaSlow ? _targetViewOffset : Float2.Lerp(_viewOffset, _targetViewOffset, deltaTime * 20.0f); + + // Clicking outside of the text box will end text editing. Left will keep the value, right will restore original value + if (_isEditing && CanEndEditByClick) + { + if (!IsMouseOver && RootWindow.ContainsFocus) + { + if (Input.GetMouseButtonDown(MouseButton.Left)) + { + RemoveFocus(); + } + else if (Input.GetMouseButtonDown(MouseButton.Right)) + { + // Restore text from start + SetSelection(-1); + _text = _onStartEditValue; + OnTextChanged(); + RemoveFocus(); + } + } + } base.Update(deltaTime); } From 5e5d88ed8eacbe5dc1117380658a8081344df40f Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 3 Dec 2022 15:43:52 +0200 Subject: [PATCH 02/45] Fix input actions/axis lookups to not use StringView The stored strings might point to temporary strings, causing garbage data to be present in the dictionary at times. --- Source/Engine/Input/Input.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 91bde0625..51f1831f0 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -55,8 +55,8 @@ struct AxisData namespace InputImpl { - Dictionary Actions; - Dictionary Axes; + Dictionary Actions; + Dictionary Axes; bool GamepadsChanged = true; Array AxesValues; InputDevice::EventQueue InputEvents; From 9b828601542b98de73f5715ebd97413d2f8486ee Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 29 Nov 2022 21:30:17 -0600 Subject: [PATCH 03/45] Allows user to left click to end editing text and right click to change text back to its original and end editing. --- Source/Editor/GUI/Input/SliderControl.cs | 1 + Source/Editor/GUI/Input/ValueBox.cs | 3 +++ Source/Engine/UI/GUI/Common/TextBoxBase.cs | 26 ++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/Source/Editor/GUI/Input/SliderControl.cs b/Source/Editor/GUI/Input/SliderControl.cs index 7a8b82622..8dc129d0a 100644 --- a/Source/Editor/GUI/Input/SliderControl.cs +++ b/Source/Editor/GUI/Input/SliderControl.cs @@ -371,6 +371,7 @@ namespace FlaxEditor.GUI.Input Parent = this, Location = new Float2(split, 0), Size = new Float2(Height, TextBoxSize), + CanEndEditByClick = true, }; _textBox.EditEnd += OnTextBoxEditEnd; } diff --git a/Source/Editor/GUI/Input/ValueBox.cs b/Source/Editor/GUI/Input/ValueBox.cs index 7f4fb51e0..6cfbbddfb 100644 --- a/Source/Editor/GUI/Input/ValueBox.cs +++ b/Source/Editor/GUI/Input/ValueBox.cs @@ -125,6 +125,7 @@ namespace FlaxEditor.GUI.Input _min = min; _max = max; _slideSpeed = sliderSpeed; + CanEndEditByClick = true; } /// @@ -173,6 +174,7 @@ namespace FlaxEditor.GUI.Input private void EndSliding() { _isSliding = false; + CanEndEditByClick = true; EndMouseCapture(); if (_cursorChanged) { @@ -245,6 +247,7 @@ namespace FlaxEditor.GUI.Input _startSlideLocation = location; _startSlideValue = _value; StartMouseCapture(true); + CanEndEditByClick = false; // Hide cursor and cache location Cursor = CursorType.Hidden; diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 69b4fa123..78ab040a2 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -139,6 +139,12 @@ namespace FlaxEngine.GUI /// public event Action KeyUp; + /// + /// Gets or sets a value indicating whether the text box can end edit via left click outside of the control + /// + [HideInEditor] + public bool CanEndEditByClick { get; set; } = false; + /// /// Gets or sets a value indicating whether this is a multiline text box control. /// @@ -1041,6 +1047,26 @@ namespace FlaxEngine.GUI // Animate view offset _viewOffset = isDeltaSlow ? _targetViewOffset : Float2.Lerp(_viewOffset, _targetViewOffset, deltaTime * 20.0f); + + // Clicking outside of the text box will end text editing. Left will keep the value, right will restore original value + if (_isEditing && CanEndEditByClick) + { + if (!IsMouseOver && RootWindow.ContainsFocus) + { + if (Input.GetMouseButtonDown(MouseButton.Left)) + { + RemoveFocus(); + } + else if (Input.GetMouseButtonDown(MouseButton.Right)) + { + // Restore text from start + SetSelection(-1); + _text = _onStartEditValue; + OnTextChanged(); + RemoveFocus(); + } + } + } base.Update(deltaTime); } From eab775b9a22cae553de8f4bf600d6e15d0457fed Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 8 Dec 2022 08:02:43 -0600 Subject: [PATCH 04/45] Added RstoreTextFromStart function and changed the calls. Renamed bool. --- Source/Editor/GUI/Input/SliderControl.cs | 2 +- Source/Editor/GUI/Input/ValueBox.cs | 6 +++--- Source/Engine/UI/GUI/Common/TextBoxBase.cs | 25 +++++++++++++--------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Source/Editor/GUI/Input/SliderControl.cs b/Source/Editor/GUI/Input/SliderControl.cs index 8dc129d0a..6fe9a246e 100644 --- a/Source/Editor/GUI/Input/SliderControl.cs +++ b/Source/Editor/GUI/Input/SliderControl.cs @@ -371,7 +371,7 @@ namespace FlaxEditor.GUI.Input Parent = this, Location = new Float2(split, 0), Size = new Float2(Height, TextBoxSize), - CanEndEditByClick = true, + EndEditOnClick = true, }; _textBox.EditEnd += OnTextBoxEditEnd; } diff --git a/Source/Editor/GUI/Input/ValueBox.cs b/Source/Editor/GUI/Input/ValueBox.cs index 6cfbbddfb..c25455612 100644 --- a/Source/Editor/GUI/Input/ValueBox.cs +++ b/Source/Editor/GUI/Input/ValueBox.cs @@ -125,7 +125,7 @@ namespace FlaxEditor.GUI.Input _min = min; _max = max; _slideSpeed = sliderSpeed; - CanEndEditByClick = true; + EndEditOnClick = true; } /// @@ -174,7 +174,7 @@ namespace FlaxEditor.GUI.Input private void EndSliding() { _isSliding = false; - CanEndEditByClick = true; + EndEditOnClick = true; EndMouseCapture(); if (_cursorChanged) { @@ -247,7 +247,7 @@ namespace FlaxEditor.GUI.Input _startSlideLocation = location; _startSlideValue = _value; StartMouseCapture(true); - CanEndEditByClick = false; + EndEditOnClick = false; // Hide cursor and cache location Cursor = CursorType.Hidden; diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 78ab040a2..85b5c537c 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -143,7 +143,7 @@ namespace FlaxEngine.GUI /// Gets or sets a value indicating whether the text box can end edit via left click outside of the control /// [HideInEditor] - public bool CanEndEditByClick { get; set; } = false; + public bool EndEditOnClick { get; set; } = false; /// /// Gets or sets a value indicating whether this is a multiline text box control. @@ -1049,7 +1049,7 @@ namespace FlaxEngine.GUI _viewOffset = isDeltaSlow ? _targetViewOffset : Float2.Lerp(_viewOffset, _targetViewOffset, deltaTime * 20.0f); // Clicking outside of the text box will end text editing. Left will keep the value, right will restore original value - if (_isEditing && CanEndEditByClick) + if (_isEditing && EndEditOnClick) { if (!IsMouseOver && RootWindow.ContainsFocus) { @@ -1059,10 +1059,7 @@ namespace FlaxEngine.GUI } else if (Input.GetMouseButtonDown(MouseButton.Right)) { - // Restore text from start - SetSelection(-1); - _text = _onStartEditValue; - OnTextChanged(); + RestoreTextFromStart(); RemoveFocus(); } } @@ -1071,6 +1068,17 @@ namespace FlaxEngine.GUI base.Update(deltaTime); } + /// + /// Restores the Text from the start. + /// + public void RestoreTextFromStart() + { + // Restore text from start + SetSelection(-1); + _text = _onStartEditValue; + OnTextChanged(); + } + /// public override void OnGotFocus() { @@ -1326,13 +1334,10 @@ namespace FlaxEngine.GUI } case KeyboardKeys.Escape: { - // Restore text from start - SetSelection(-1); - _text = _onStartEditValue; + RestoreTextFromStart(); if (!IsNavFocused) RemoveFocus(); - OnTextChanged(); return true; } From 95db33e614537fe1a851e098b172adb0da8cbc2b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 8 Dec 2022 09:00:37 -0600 Subject: [PATCH 05/45] Added string text box to end on click --- Source/Editor/CustomEditors/Editors/StringEditor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/CustomEditors/Editors/StringEditor.cs b/Source/Editor/CustomEditors/Editors/StringEditor.cs index d2d214eb5..22e6b2e71 100644 --- a/Source/Editor/CustomEditors/Editors/StringEditor.cs +++ b/Source/Editor/CustomEditors/Editors/StringEditor.cs @@ -30,6 +30,7 @@ namespace FlaxEditor.CustomEditors.Editors } _element = layout.TextBox(isMultiLine); + _element.TextBox.EndEditOnClick = true; _element.TextBox.EditEnd += () => SetValue(_element.Text); } From ad0aadbf29cd1f60dac915223860cc61ea752125 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 8 Dec 2022 16:06:39 -0600 Subject: [PATCH 06/45] Changed location of renaming box for ActorTreeNodes to start at the label for better UX. --- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index ca0aaee77..10857f685 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -282,7 +282,7 @@ namespace FlaxEditor.SceneGraph.GUI (window as PrefabWindow).ScrollingOnTreeView(false); // Start renaming the actor - var dialog = RenamePopup.Show(this, HeaderRect, _actorNode.Name, false); + var dialog = RenamePopup.Show(this, TextRect, _actorNode.Name, false); dialog.Renamed += OnRenamed; dialog.Closed += popup => { From 1a3bcc5d2710415405c49063f7a337a9d915565f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 8 Dec 2022 16:21:10 -0600 Subject: [PATCH 07/45] Changed default to true. --- Source/Editor/CustomEditors/Editors/StringEditor.cs | 1 - Source/Editor/GUI/Input/SliderControl.cs | 1 - Source/Editor/GUI/Input/ValueBox.cs | 1 - Source/Engine/UI/GUI/Common/TextBoxBase.cs | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/StringEditor.cs b/Source/Editor/CustomEditors/Editors/StringEditor.cs index 22e6b2e71..d2d214eb5 100644 --- a/Source/Editor/CustomEditors/Editors/StringEditor.cs +++ b/Source/Editor/CustomEditors/Editors/StringEditor.cs @@ -30,7 +30,6 @@ namespace FlaxEditor.CustomEditors.Editors } _element = layout.TextBox(isMultiLine); - _element.TextBox.EndEditOnClick = true; _element.TextBox.EditEnd += () => SetValue(_element.Text); } diff --git a/Source/Editor/GUI/Input/SliderControl.cs b/Source/Editor/GUI/Input/SliderControl.cs index 6fe9a246e..7a8b82622 100644 --- a/Source/Editor/GUI/Input/SliderControl.cs +++ b/Source/Editor/GUI/Input/SliderControl.cs @@ -371,7 +371,6 @@ namespace FlaxEditor.GUI.Input Parent = this, Location = new Float2(split, 0), Size = new Float2(Height, TextBoxSize), - EndEditOnClick = true, }; _textBox.EditEnd += OnTextBoxEditEnd; } diff --git a/Source/Editor/GUI/Input/ValueBox.cs b/Source/Editor/GUI/Input/ValueBox.cs index c25455612..ac253b79d 100644 --- a/Source/Editor/GUI/Input/ValueBox.cs +++ b/Source/Editor/GUI/Input/ValueBox.cs @@ -125,7 +125,6 @@ namespace FlaxEditor.GUI.Input _min = min; _max = max; _slideSpeed = sliderSpeed; - EndEditOnClick = true; } /// diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index addf967dc..0e9c34954 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -143,7 +143,7 @@ namespace FlaxEngine.GUI /// Gets or sets a value indicating whether the text box can end edit via left click outside of the control /// [HideInEditor] - public bool EndEditOnClick { get; set; } = false; + public bool EndEditOnClick { get; set; } = true; /// /// Gets or sets a value indicating whether this is a multiline text box control. From 2e055d190918e8312e95595d706b8656d0887500 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 8 Dec 2022 20:40:21 -0600 Subject: [PATCH 08/45] Changed saved window layouts to be accessable from any project. added apply and delete buttons as well. --- Source/Editor/Modules/UIModule.cs | 12 +++++++++--- Source/Editor/Modules/WindowsModule.cs | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 7c71c5628..5d0b6ff09 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -738,7 +738,11 @@ namespace FlaxEditor.Modules return; // Find layout to use - var searchFolder = Globals.ProjectCacheFolder; + var searchFolder = StringUtils.CombinePaths(Editor.LocalCachePath, "LayoutsCache") ; + if (!Directory.Exists(searchFolder)) + { + Directory.CreateDirectory(searchFolder); + } var files = Directory.GetFiles(searchFolder, "Layout_*.xml", SearchOption.TopDirectoryOnly); var layouts = _menuWindowApplyWindowLayout.ContextMenu; layouts.DisposeAllItems(); @@ -746,8 +750,10 @@ namespace FlaxEditor.Modules { var file = files[i]; var name = file.Substring(searchFolder.Length + 8, file.Length - searchFolder.Length - 12); - var button = layouts.AddButton(name, OnApplyLayoutButtonClicked); - button.Tag = file; + var nameCM = layouts.AddChildMenu(name); + var applyButton = nameCM.ContextMenu.AddButton("Apply", OnApplyLayoutButtonClicked); + nameCM.ContextMenu.AddButton("Delete", () => File.Delete(file)); + applyButton.Tag = file; } _menuWindowApplyWindowLayout.Enabled = files.Length > 0; } diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 0e341581f..04182f81d 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -555,8 +555,8 @@ namespace FlaxEditor.Modules } base.OnSubmit(); - - var path = StringUtils.CombinePaths(Globals.ProjectCacheFolder, "Layout_" + name + ".xml"); + + var path = StringUtils.CombinePaths(Editor.LocalCachePath, "LayoutsCache", "Layout_" + name + ".xml"); Editor.Instance.Windows.SaveLayout(path); } } From ffc5a4d24fc660dd38d42631e89676ba0e47698b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 8 Dec 2022 20:42:39 -0600 Subject: [PATCH 09/45] Style fix --- Source/Editor/Modules/UIModule.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 5d0b6ff09..83ff4a74b 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -740,9 +740,7 @@ namespace FlaxEditor.Modules // Find layout to use var searchFolder = StringUtils.CombinePaths(Editor.LocalCachePath, "LayoutsCache") ; if (!Directory.Exists(searchFolder)) - { Directory.CreateDirectory(searchFolder); - } var files = Directory.GetFiles(searchFolder, "Layout_*.xml", SearchOption.TopDirectoryOnly); var layouts = _menuWindowApplyWindowLayout.ContextMenu; layouts.DisposeAllItems(); From a45f7a86f23ee74351d919620ae9eecae617ff09 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 8 Dec 2022 21:21:03 -0600 Subject: [PATCH 10/45] Added Tooltips --- Source/Editor/Modules/UIModule.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 83ff4a74b..80f0bb97c 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -750,7 +750,8 @@ namespace FlaxEditor.Modules var name = file.Substring(searchFolder.Length + 8, file.Length - searchFolder.Length - 12); var nameCM = layouts.AddChildMenu(name); var applyButton = nameCM.ContextMenu.AddButton("Apply", OnApplyLayoutButtonClicked); - nameCM.ContextMenu.AddButton("Delete", () => File.Delete(file)); + applyButton.TooltipText = "Applies the selected layout."; + nameCM.ContextMenu.AddButton("Delete", () => File.Delete(file)).TooltipText = "Permanently deletes the selected layout."; applyButton.Tag = file; } _menuWindowApplyWindowLayout.Enabled = files.Length > 0; From 8d9e3ded7459008f215f4755efa0cdb3d74108a2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 12 Dec 2022 16:55:12 -0600 Subject: [PATCH 11/45] Remove sorting children in convtent view treeand make it so the game is on top, plugins in the middle, and engine at the bottom of the tree. This is for better UX. --- Source/Editor/Windows/ContentWindow.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 22808a1f1..bb179f9e9 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -837,13 +837,19 @@ namespace FlaxEditor.Windows }; _root.Expand(true); + // Add game project on top, plugins in the middle and engine at bottom + _root.AddChild(Editor.ContentDatabase.Game); foreach (var project in Editor.ContentDatabase.Projects) + { + if (project == Editor.ContentDatabase.Game || project == Editor.ContentDatabase.Engine) + continue; _root.AddChild(project); + } + _root.AddChild(Editor.ContentDatabase.Engine); Editor.ContentDatabase.Game?.Expand(true); _tree.Margin = new Margin(0.0f, 0.0f, -16.0f, 2.0f); // Hide root node _tree.AddChild(_root); - _root.SortChildrenRecursive(); // Setup navigation _navigationUnlocked = true; From 7eab3fe5b367575b474a83f2ccddb6fc66e9766c Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 12 Dec 2022 18:18:48 -0600 Subject: [PATCH 12/45] Added back sorting sub project directories. --- Source/Editor/Windows/ContentWindow.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index bb179f9e9..3b1d0a11f 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -841,6 +841,7 @@ namespace FlaxEditor.Windows _root.AddChild(Editor.ContentDatabase.Game); foreach (var project in Editor.ContentDatabase.Projects) { + project.SortChildrenRecursive(); if (project == Editor.ContentDatabase.Game || project == Editor.ContentDatabase.Engine) continue; _root.AddChild(project); From de79e43fb19925da9fda8837674602beb97ea197 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 14 Dec 2022 08:30:44 -0600 Subject: [PATCH 13/45] Code Style Fix --- Source/Editor/Modules/WindowsModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 04182f81d..993d51778 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -555,7 +555,7 @@ namespace FlaxEditor.Modules } base.OnSubmit(); - + var path = StringUtils.CombinePaths(Editor.LocalCachePath, "LayoutsCache", "Layout_" + name + ".xml"); Editor.Instance.Windows.SaveLayout(path); } From 86be1acd7a0e36dd6b52b3e7604388643a1b1d6b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 15 Dec 2022 07:45:08 -0600 Subject: [PATCH 14/45] Cached sate of anchor drop down closed. --- Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 602c6d0ee..1050139be 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -407,6 +407,7 @@ namespace FlaxEditor.CustomEditors.Dedicated public class UIControlControlEditor : GenericEditor { private Type _cachedType; + private bool _anchorDropDownClosed = true; /// public override void Initialize(LayoutElementsContainer layout) @@ -498,7 +499,8 @@ namespace FlaxEditor.CustomEditors.Dedicated ItemInfo maxItem = new ItemInfo(maxInfo); GroupElement ng = main.Group("Anchors", true); - ng.Panel.Close(false); + ng.Panel.IsClosed = _anchorDropDownClosed; + ng.Panel.IsClosedChanged += panel => _anchorDropDownClosed = panel.IsClosed; ng.Property("Min", minItem.GetValues(Values)); ng.Property("Max", maxItem.GetValues(Values)); } From af5f3cbd95e8c46762fac91cf8632f4b1de717ed Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 15 Dec 2022 08:18:09 -0600 Subject: [PATCH 15/45] Return null if type is abstract but trying to be added as an actor or script --- Source/Engine/Level/Actor.cs | 15 +++++++++++++++ Source/Engine/Level/Actor.h | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/Source/Engine/Level/Actor.cs b/Source/Engine/Level/Actor.cs index 936d38565..cd774fca6 100644 --- a/Source/Engine/Level/Actor.cs +++ b/Source/Engine/Level/Actor.cs @@ -123,6 +123,9 @@ namespace FlaxEngine /// The child actor. public Actor AddChild(Type type) { + if (type.IsAbstract) + return null; + var result = (Actor)New(type); result.SetParent(this, false, false); return result; @@ -135,6 +138,9 @@ namespace FlaxEngine /// The child actor. public T AddChild() where T : Actor { + if (typeof(T).IsAbstract) + return null; + var result = New(); result.SetParent(this, false, false); return result; @@ -172,6 +178,9 @@ namespace FlaxEngine var result = GetChild(); if (result == null) { + if (typeof(T).IsAbstract) + return null; + result = New(); result.SetParent(this, false, false); } @@ -185,6 +194,9 @@ namespace FlaxEngine /// The created script instance, null otherwise. public Script AddScript(Type type) { + if (type.IsAbstract) + return null; + var script = (Script)New(type); script.Parent = this; return script; @@ -197,6 +209,9 @@ namespace FlaxEngine /// The created script instance, null otherwise. public T AddScript() where T : Script { + if (typeof(T).IsAbstract) + return null; + var script = New(); script.Parent = this; return script; diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index e9ad7793c..e108b0756 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -227,6 +227,10 @@ public: T* result = (T*)GetChild(T::GetStaticClass()); if (!result) { + const MClass* type = T::GetStaticClass(); + if (type->IsAbstract()) + return nullptr; + result = New(); result->SetParent(this, false, false); } From c3f23d13902581ad5ae7c8268660e72c1f453393 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 15 Dec 2022 08:49:52 -0600 Subject: [PATCH 16/45] Added abstract check to spawning actor --- Source/Engine/Level/Level.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 98ad5a694..485b1a3b6 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -177,6 +177,12 @@ bool LevelImpl::spawnActor(Actor* actor, Actor* parent) return true; } + if (actor->GetType().ManagedClass->IsAbstract()) + { + Log::Exception(TEXT("Cannot spawn abstract actor type.")); + return true; + } + if (actor->Is()) { // Spawn scene From 66771775341b94d90080da6c70f4cc233d39032e Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 15 Dec 2022 08:52:37 -0600 Subject: [PATCH 17/45] Removed variable MClass --- Source/Engine/Level/Actor.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index e108b0756..ec52a8c74 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -227,8 +227,7 @@ public: T* result = (T*)GetChild(T::GetStaticClass()); if (!result) { - const MClass* type = T::GetStaticClass(); - if (type->IsAbstract()) + if (T::GetStaticClass()->IsAbstract()) return nullptr; result = New(); From 1b919b9fae086d8ef3df9e32a3935d81edb2afdf Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 15 Dec 2022 20:45:51 -0600 Subject: [PATCH 18/45] Created new SearchBox class with button to clear the search. --- Source/Editor/GUI/Input/SearchBox.cs | 50 ++++++++++++++++++++++++ Source/Editor/Windows/ContentWindow.cs | 5 +-- Source/Editor/Windows/OutputLogWindow.cs | 4 +- Source/Editor/Windows/SceneTreeWindow.cs | 4 +- Source/Editor/Windows/ToolboxWindow.cs | 4 +- 5 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 Source/Editor/GUI/Input/SearchBox.cs diff --git a/Source/Editor/GUI/Input/SearchBox.cs b/Source/Editor/GUI/Input/SearchBox.cs new file mode 100644 index 000000000..485c7f99a --- /dev/null +++ b/Source/Editor/GUI/Input/SearchBox.cs @@ -0,0 +1,50 @@ +using FlaxEngine.GUI; + +namespace FlaxEditor.GUI.Input +{ + /// + /// Search box control which can gather text search input from the user. + /// + public class SearchBox : TextBox + { + /// + /// A button that clears the search bar. + /// + public Button ClearSearchButton { get; } + + /// + /// Init search box + /// + public SearchBox() + : this(false, 0, 0) + { + } + + /// + /// Init search box + /// + public SearchBox(bool isMultiline, float x, float y, float width = 120) + : base(isMultiline, x, y, width) + { + WatermarkText = "Search..."; + + ClearSearchButton = new Button + { + Parent = this, + Width = 15.0f, + Height = 14.0f, + AnchorPreset = AnchorPresets.TopRight, + Text = "X", + TooltipText = "Cancel Search.", + BackgroundColor = Style.Current.TextBoxBackground, + BorderColor = Style.Current.TextBoxBackground, + Visible = false, + }; + ClearSearchButton.LocalY += 2; + ClearSearchButton.LocalX -= 2; + ClearSearchButton.Clicked += Clear; + + TextChanged += () => ClearSearchButton.Visible = !string.IsNullOrEmpty(Text); + } + } +} diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 22808a1f1..703dc8c81 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -113,10 +113,9 @@ namespace FlaxEditor.Windows IsScrollable = false, Offsets = new Margin(0, 0, 0, 18 + 6), }; - _foldersSearchBox = new TextBox + _foldersSearchBox = new SearchBox { AnchorPreset = AnchorPresets.HorizontalStretchMiddle, - WatermarkText = "Search...", Parent = headerPanel, Bounds = new Rectangle(4, 4, headerPanel.Width - 8, 18), }; @@ -149,7 +148,7 @@ namespace FlaxEditor.Windows Parent = _split.Panel2, }; const float viewDropdownWidth = 50.0f; - _itemsSearchBox = new TextBox + _itemsSearchBox = new SearchBox { AnchorPreset = AnchorPresets.HorizontalStretchMiddle, WatermarkText = "Search...", diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index c9f3ac6e2..22989124f 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -7,6 +7,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Xml; using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.GUI.Input; using FlaxEditor.Options; using FlaxEngine; using FlaxEngine.GUI; @@ -157,9 +158,8 @@ namespace FlaxEditor.Windows Parent = this, }; _viewDropdown.Clicked += OnViewButtonClicked; - _searchBox = new TextBox(false, _viewDropdown.Right + 2, 2, Width - _viewDropdown.Right - 2 - _scrollSize) + _searchBox = new SearchBox(false, _viewDropdown.Right + 2, 2, Width - _viewDropdown.Right - 2 - _scrollSize) { - WatermarkText = "Search...", Parent = this, }; _searchBox.TextChanged += Refresh; diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 8fd6e33ee..28ab83ff5 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -6,6 +6,7 @@ using FlaxEditor.Gizmo; using FlaxEditor.Content; using FlaxEditor.GUI.Tree; using FlaxEditor.GUI.Drag; +using FlaxEditor.GUI.Input; using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph.GUI; using FlaxEditor.Scripting; @@ -49,10 +50,9 @@ namespace FlaxEditor.Windows IsScrollable = false, Offsets = new Margin(0, 0, 0, 18 + 6), }; - _searchBox = new TextBox + _searchBox = new SearchBox { AnchorPreset = AnchorPresets.HorizontalStretchMiddle, - WatermarkText = "Search...", Parent = headerPanel, Bounds = new Rectangle(4, 4, headerPanel.Width - 8, 18), }; diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index ff03d8e0d..6c1b3d9cc 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using FlaxEditor.GUI.Input; using FlaxEditor.GUI.Tabs; using FlaxEditor.GUI.Tree; using FlaxEditor.Scripting; @@ -130,10 +131,9 @@ namespace FlaxEditor.Windows }; _groupSearch = CreateGroupWithList(_actorGroups, "Search", 26); - _searchBox = new TextBox + _searchBox = new SearchBox { AnchorPreset = AnchorPresets.HorizontalStretchTop, - WatermarkText = "Search...", Parent = _groupSearch.Parent.Parent, Bounds = new Rectangle(4, 4, _actorGroups.Width - 8, 18), }; From 1c85b30e9fb3fa1aede43e2bfabb686ea131629f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 15 Dec 2022 20:53:47 -0600 Subject: [PATCH 19/45] Change color of button on hover. --- Source/Editor/GUI/Input/SearchBox.cs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/Input/SearchBox.cs b/Source/Editor/GUI/Input/SearchBox.cs index 485c7f99a..04ae0c2d5 100644 --- a/Source/Editor/GUI/Input/SearchBox.cs +++ b/Source/Editor/GUI/Input/SearchBox.cs @@ -1,3 +1,4 @@ +using FlaxEngine; using FlaxEngine.GUI; namespace FlaxEditor.GUI.Input @@ -11,6 +12,8 @@ namespace FlaxEditor.GUI.Input /// A button that clears the search bar. /// public Button ClearSearchButton { get; } + + private Color _defaultButtonTextColor; /// /// Init search box @@ -36,15 +39,28 @@ namespace FlaxEditor.GUI.Input AnchorPreset = AnchorPresets.TopRight, Text = "X", TooltipText = "Cancel Search.", - BackgroundColor = Style.Current.TextBoxBackground, - BorderColor = Style.Current.TextBoxBackground, + BackgroundColor = Color.Transparent, + BorderColor = Color.Transparent, + BackgroundColorHighlighted = Color.Transparent, + BorderColorHighlighted = Color.Transparent, + BackgroundColorSelected = Color.Transparent, + BorderColorSelected = Color.Transparent, Visible = false, }; ClearSearchButton.LocalY += 2; ClearSearchButton.LocalX -= 2; ClearSearchButton.Clicked += Clear; + _defaultButtonTextColor = ClearSearchButton.TextColor; TextChanged += () => ClearSearchButton.Visible = !string.IsNullOrEmpty(Text); } + + /// + public override void Update(float deltaTime) + { + base.Update(deltaTime); + + ClearSearchButton.TextColor = ClearSearchButton.IsMouseOver ? Style.Current.ForegroundGrey : _defaultButtonTextColor; + } } } From a468053f4674d256e1626bb17cb075f2bfe62351 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 15 Dec 2022 21:09:34 -0600 Subject: [PATCH 20/45] Added hover begin and end event to button --- Source/Engine/UI/GUI/Common/Button.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index f9bc54655..190ba8eff 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -71,6 +71,16 @@ namespace FlaxEngine.GUI /// public event Action /// The search box. /// The tree control. + /// Amount of additional space above the search box to put custom UI. /// The created menu to setup and show. - public static ContextMenuBase CreateSearchPopup(out TextBox searchBox, out Tree tree) + public static ContextMenuBase CreateSearchPopup(out TextBox searchBox, out Tree tree, float headerHeight = 0) { var menu = new ContextMenuBase { - Size = new Float2(320, 220), + Size = new Float2(320, 220 + headerHeight), }; - searchBox = new TextBox(false, 1, 1) + searchBox = new TextBox(false, 1, headerHeight + 1) { Width = menu.Width - 3, WatermarkText = "Search...", @@ -980,6 +981,33 @@ namespace FlaxEditor.Utilities return menu; } + /// + /// Updates (recursivly) search popup tree structures based on the filter text. + /// + public static void UpdateSearchPopupFilter(TreeNode node, string filterText) + { + // Update children + bool isAnyChildVisible = false; + for (int i = 0; i < node.Children.Count; i++) + { + if (node.Children[i] is TreeNode child) + { + UpdateSearchPopupFilter(child, filterText); + isAnyChildVisible |= child.Visible; + } + } + + // Update itself + bool noFilter = string.IsNullOrWhiteSpace(filterText); + bool isThisVisible = noFilter || QueryFilterHelper.Match(filterText, node.Text); + bool isExpanded = isAnyChildVisible; + if (isExpanded) + node.Expand(true); + else + node.Collapse(true); + node.Visible = isThisVisible | isAnyChildVisible; + } + /// /// Gets the asset name relative to the project root folder (without asset file extension) /// diff --git a/Source/Engine/Core/Config/LayersAndTagsSettings.cs b/Source/Engine/Core/Config/LayersAndTagsSettings.cs index 0396b7588..2f39d4595 100644 --- a/Source/Engine/Core/Config/LayersAndTagsSettings.cs +++ b/Source/Engine/Core/Config/LayersAndTagsSettings.cs @@ -20,13 +20,6 @@ namespace FlaxEditor.Content.Settings [EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(ReadOnly = true)] public string[] Layers = new string[32]; - /// - /// Gets the current tags collection. - /// - /// The tags collection. - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern string[] GetCurrentTags(); - /// /// Gets the current layer names (max 32 items but trims last empty items). /// diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 1c4f4a2e1..d96bd005d 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -73,13 +73,12 @@ Actor::Actor(const SpawnParams& params) , _isPrefabRoot(false) , _isEnabled(false) , _layer(0) - , _tag(ACTOR_TAG_INVALID) - , _scene(nullptr) , _staticFlags(StaticFlags::FullyStatic) , _localTransform(Transform::Identity) , _transform(Transform::Identity) , _sphere(BoundingSphere::Empty) , _box(BoundingBox::Zero) + , _scene(nullptr) , _physicsScene(nullptr) , HideFlags(HideFlags::None) { @@ -439,23 +438,24 @@ void Actor::DestroyChildren(float timeLeft) } } -bool Actor::HasTag(const StringView& tag) const -{ - return HasTag() && tag == Level::Tags[_tag]; -} - const String& Actor::GetLayerName() const { return Level::Layers[_layer]; } -const String& Actor::GetTag() const +bool Actor::HasTag() const { - if (HasTag()) - { - return Level::Tags[_tag]; - } - return String::Empty; + return Tags.Count() != 0; +} + +bool Actor::HasTag(const Tag& tag) const +{ + return Tags.Contains(tag); +} + +bool Actor::HasTag(const StringView& tag) const +{ + return Tags.Contains(tag); } void Actor::SetLayer(int32 layerIndex) @@ -463,51 +463,10 @@ void Actor::SetLayer(int32 layerIndex) layerIndex = Math::Clamp(layerIndex, 0, 31); if (layerIndex == _layer) return; - _layer = layerIndex; OnLayerChanged(); } -void Actor::SetTagIndex(int32 tagIndex) -{ - if (tagIndex == ACTOR_TAG_INVALID) - { - } - else if (Level::Tags.IsEmpty()) - { - tagIndex = ACTOR_TAG_INVALID; - } - else - { - tagIndex = tagIndex < 0 ? ACTOR_TAG_INVALID : Math::Min(tagIndex, Level::Tags.Count() - 1); - } - if (tagIndex == _tag) - return; - - _tag = tagIndex; - OnTagChanged(); -} - -void Actor::SetTag(const StringView& tagName) -{ - int32 tagIndex; - if (tagName.IsEmpty()) - { - tagIndex = ACTOR_TAG_INVALID; - } - else - { - tagIndex = Level::Tags.Find(tagName); - if (tagIndex == -1) - { - LOG(Error, "Cannot change actor tag. Given value is invalid."); - return; - } - } - - SetTagIndex(tagIndex); -} - void Actor::SetName(const StringView& value) { if (_name == value) @@ -897,10 +856,21 @@ void Actor::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_MEMBER(StaticFlags, _staticFlags); SERIALIZE(HideFlags); SERIALIZE_MEMBER(Layer, _layer); - if (!other || _tag != other->_tag) + if (!other || Tags != other->Tags) { - stream.JKEY("Tag"); - stream.String(GetTag()); + if (Tags.Count() == 1) + { + stream.JKEY("Tag"); + stream.String(Tags.Get()->ToString()); + } + else + { + stream.JKEY("Tags"); + stream.StartArray(); + for (auto& tag : Tags) + stream.String(tag.ToString()); + stream.EndArray(); + } } if (isPrefabDiff) @@ -996,14 +966,27 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) _staticFlags |= StaticFlags::Navigation; } - // Resolve tag (it may be missing in the current configuration const auto tag = stream.FindMember("Tag"); if (tag != stream.MemberEnd()) { if (tag->value.IsString() && tag->value.GetStringLength()) { - const String tagName = tag->value.GetText(); - _tag = Level::GetOrAddTag(tagName); + Tags.Clear(); + Tags.Add(Tags::Get(tag->value.GetText())); + } + } + else + { + const auto tags = stream.FindMember("Tags"); + if (tags != stream.MemberEnd() && tags->value.IsArray()) + { + Tags.Clear(); + for (rapidjson::SizeType i = 0; i < tags->value.Size(); i++) + { + auto& e = tags->value[i]; + if (e.IsString() && e.GetStringLength()) + Tags.Add(Tags::Get(e.GetText())); + } } } diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 266feda70..1133eceae 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -3,6 +3,7 @@ #pragma once #include "SceneObject.h" +#include "Tags.h" #include "Engine/Core/Types/Span.h" #include "Engine/Core/Math/Transform.h" #include "Engine/Core/Math/BoundingBox.h" @@ -34,7 +35,6 @@ API_CLASS(Abstract) class FLAXENGINE_API Actor : public SceneObject friend SceneRendering; friend Prefab; friend PrefabInstanceData; - protected: int16 _isActive : 1; int16 _isActiveInHierarchy : 1; @@ -43,7 +43,6 @@ protected: int16 _drawNoCulling : 1; int16 _drawCategory : 4; byte _layer; - byte _tag; StaticFlags _staticFlags; Transform _localTransform; Transform _transform; @@ -77,6 +76,11 @@ public: API_FIELD(Attributes="HideInEditor, NoSerialize") HideFlags HideFlags; + /// + /// Actor tags collection. + /// + API_FIELD(Attributes="NoAnimate, EditorDisplay(\"General\"), EditorOrder(-68)") Array Tags; + public: /// /// Gets the object layer (index). Can be used for selective rendering or ignoring raycasts. @@ -96,26 +100,10 @@ public: } /// - /// Gets the actor tag (index). Can be used to identify the objects. + /// Sets the layer. /// - FORCE_INLINE int32 GetTagIndex() const - { - return _tag; - } - - /// - /// Determines whether this actor has tag assigned. - /// - API_FUNCTION() FORCE_INLINE bool HasTag() const - { - return _tag != ACTOR_TAG_INVALID; - } - - /// - /// Determines whether this actor has given tag assigned. - /// - /// The tag to check. - API_FUNCTION() bool HasTag(const StringView& tag) const; + /// The index of the layer. + API_PROPERTY() void SetLayer(int32 layerIndex); /// /// Gets the name of the layer. @@ -123,28 +111,21 @@ public: API_PROPERTY() const String& GetLayerName() const; /// - /// Gets the name of the tag. + /// Determines whether this actor has any tag assigned. /// - API_PROPERTY(Attributes="NoAnimate, EditorDisplay(\"General\"), EditorOrder(-68), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTagEditor\")") - const String& GetTag() const; + API_FUNCTION() bool HasTag() const; /// - /// Sets the layer. + /// Determines whether this actor has given tag assigned. /// - /// The index of the layer. - API_PROPERTY() void SetLayer(int32 layerIndex); + /// The tag to check. + API_FUNCTION() bool HasTag(const Tag& tag) const; /// - /// Sets the tag. + /// Determines whether this actor has given tag assigned. /// - /// The index of the tag. - void SetTagIndex(int32 tagIndex); - - /// - /// Sets the tag. - /// - /// Name of the tag. - API_PROPERTY() void SetTag(const StringView& tagName); + /// The tag to check. + API_FUNCTION() bool HasTag(const StringView& tag) const; /// /// Gets the actor name. @@ -955,13 +936,6 @@ public: { } - /// - /// Called when tag gets changed. - /// - virtual void OnTagChanged() - { - } - /// /// Called when adding object to the game. /// diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index b5001a730..338598541 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -166,7 +166,6 @@ Action Level::ScriptsReload; Action Level::ScriptsReloaded; Action Level::ScriptsReloadEnd; #endif -Array Level::Tags; String Level::Layers[32]; bool LevelImpl::spawnActor(Actor* actor, Actor* parent) @@ -226,8 +225,7 @@ void LayersAndTagsSettings::Apply() // Tags/Layers are stored as index in actors so collection change would break the linkage for (auto& tag : Tags) { - if (!Level::Tags.Contains(tag)) - Level::Tags.Add(tag); + Tags::Get(tag); } for (int32 i = 0; i < ARRAY_COUNT(Level::Layers); i++) { @@ -735,17 +733,6 @@ void LevelImpl::CallSceneEvent(SceneEventType eventType, Scene* scene, Guid scen } } -int32 Level::GetOrAddTag(const StringView& tag) -{ - int32 index = Tags.Find(tag); - if (index == INVALID_INDEX) - { - index = Tags.Count(); - Tags.AddOne() = tag; - } - return index; -} - int32 Level::GetNonEmptyLayerNamesCount() { int32 result = 31; diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h index 96202c830..8039464c5 100644 --- a/Source/Engine/Level/Level.h +++ b/Source/Engine/Level/Level.h @@ -445,23 +445,11 @@ public: static void ConstructParentActorsTreeList(const Array& input, Array& output); public: - /// - /// The tags names. - /// - static Array Tags; - /// /// The layers names. /// static String Layers[32]; - /// - /// Gets or adds the tag (returns the tag index). - /// - /// The tag. - /// The tag index. - static int32 GetOrAddTag(const StringView& tag); - /// /// Gets the amount of non empty layer names (from the beginning, trims the last ones). /// diff --git a/Source/Engine/Level/Tags.cpp b/Source/Engine/Level/Tags.cpp new file mode 100644 index 000000000..83bb2e13e --- /dev/null +++ b/Source/Engine/Level/Tags.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. + +#include "Tags.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/StringView.h" + +Array Tags::List; +#if !BUILD_RELEASE +FLAXENGINE_API String* TagsListDebug = nullptr; +#endif + +const String& Tag::ToString() const +{ + return Index >= 0 && Index < Tags::List.Count() ? Tags::List.Get()[Index] : String::Empty; +} + +bool Tag::operator==(const StringView& other) const +{ + return ToString() == other; +} + +bool Tag::operator!=(const StringView& other) const +{ + return ToString() != other; +} + +Tag Tags::Get(const StringView& tagName) +{ + Tag tag = List.Find(tagName); + if (tag.Index == -1 && tagName.HasChars()) + { + tag.Index = List.Count(); + List.AddOne() = tagName; +#if !BUILD_RELEASE + TagsListDebug = List.Get(); +#endif + } + return tag; +} + +const String& Tags::GetTagName(int32 tag) +{ + return Tag(tag).ToString(); +} diff --git a/Source/Engine/Level/Tags.cs b/Source/Engine/Level/Tags.cs new file mode 100644 index 000000000..0fda5feab --- /dev/null +++ b/Source/Engine/Level/Tags.cs @@ -0,0 +1,116 @@ +// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. + +using System; +using System.Runtime.CompilerServices; + +namespace FlaxEngine +{ + partial struct Tag : IEquatable, IEquatable, IComparable, IComparable, IComparable + { + /// + /// The default . + /// + public static Tag Default => new Tag(-1); + + /// + /// Initializes a new instance of the struct. + /// + /// The tag index. + public Tag(int index) + { + Index = index; + } + + [System.Runtime.Serialization.OnDeserializing] + internal void OnDeserializing(System.Runtime.Serialization.StreamingContext context) + { + // Initialize structure with default values to replicate C++ deserialization behavior + this.Index = -1; + } + + /// + /// Compares two tags. + /// + /// The lft tag. + /// The right tag. + /// True if both values are equal, otherwise false. + public static bool operator ==(Tag left, Tag right) + { + return left.Index == right.Index; + } + + /// + /// Compares two tags. + /// + /// The lft tag. + /// The right tag. + /// True if both values are not equal, otherwise false. + public static bool operator !=(Tag left, Tag right) + { + return left.Index == right.Index; + } + + /// + /// Checks if tag is valid. + /// + /// The tag to check. + /// True if tag is valid, otherwise false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator bool(Tag tag) + { + return tag.Index != -1; + } + + /// + public int CompareTo(object obj) + { + if (obj is string asString) + return CompareTo(asString); + if (obj is Tag asTag) + return CompareTo(asTag); + return 0; + } + + /// + public bool Equals(Tag other) + { + return Index == other.Index; + } + + /// + public bool Equals(string other) + { + return string.Equals(ToString(), other, StringComparison.Ordinal); + } + + /// + public int CompareTo(Tag other) + { + return string.Compare(ToString(), ToString(), StringComparison.Ordinal); + } + + /// + public int CompareTo(string other) + { + return string.Compare(ToString(), other, StringComparison.Ordinal); + } + + /// + public override bool Equals(object obj) + { + return obj is Tag other && Index == other.Index; + } + + /// + public override int GetHashCode() + { + return Index; + } + + /// + public override string ToString() + { + return Tags.Internal_GetTagName(Index); + } + } +} diff --git a/Source/Engine/Level/Tags.h b/Source/Engine/Level/Tags.h new file mode 100644 index 000000000..5616627c9 --- /dev/null +++ b/Source/Engine/Level/Tags.h @@ -0,0 +1,88 @@ +// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Types/BaseTypes.h" +#include "Engine/Core/Collections/Array.h" + +/// +/// Gameplay tag that represents a hierarchical name of the form 'X.Y.Z' (namespaces separated with a dot). Tags are defined in project LayersAndTagsSettings asset but can be also created from code. +/// +API_STRUCT(NoDefault) struct FLAXENGINE_API Tag +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(Tag); + + /// + /// Index of the tag (in global Level.Tags list). + /// + API_FIELD() int32 Index = -1; + + /// + /// Gets the tag name. + /// + const String& ToString() const; + +public: + Tag() = default; + + FORCE_INLINE Tag(int32 index) + : Index(index) + { + } + + FORCE_INLINE operator bool() const + { + return Index != -1; + } + + FORCE_INLINE bool operator==(const Tag& other) const + { + return Index == other.Index; + } + + FORCE_INLINE bool operator!=(const Tag& other) const + { + return Index != other.Index; + } + + bool operator==(const StringView& other) const; + bool operator!=(const StringView& other) const; +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +inline uint32 GetHash(const Tag& key) +{ + return (uint32)key.Index; +} + +/// +/// Gameplay tags utilities. +/// +API_CLASS(Static) class FLAXENGINE_API Tags +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(Tags); + + /// + /// List of all tags. + /// + API_FIELD(ReadOnly) static Array List; + + /// + /// Gets or adds the tag. + /// + /// The tag name. + /// The tag. + API_FUNCTION() static Tag Get(const StringView& tagName); + +private: + API_FUNCTION(NoProxy) static const String& GetTagName(int32 tag); +}; + +#if !BUILD_RELEASE +extern FLAXENGINE_API String* TagsListDebug; // Used by flax.natvis +#endif diff --git a/Source/flax.natvis b/Source/flax.natvis index 193efe0cc..70f9b15e3 100644 --- a/Source/flax.natvis +++ b/Source/flax.natvis @@ -215,4 +215,10 @@ + + + None + Tag={TagsListDebug[Index]} + + From 757728c96ce8b20f066ebd4ba73048e556052dd6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Dec 2022 21:10:16 +0100 Subject: [PATCH 23/45] Add option to override Custom Editor for array of type --- .../Editor/CustomEditors/CustomEditorsUtil.cs | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/Source/Editor/CustomEditors/CustomEditorsUtil.cs b/Source/Editor/CustomEditors/CustomEditorsUtil.cs index 4b934cbd6..4b94bccfa 100644 --- a/Source/Editor/CustomEditors/CustomEditorsUtil.cs +++ b/Source/Editor/CustomEditors/CustomEditorsUtil.cs @@ -65,19 +65,21 @@ namespace FlaxEditor.CustomEditors { if (targetType.Type.GetArrayRank() != 1) return new GenericEditor(); // Not-supported multidimensional array + + // Allow using custom editor for array of custom type + var customEditorType = Internal_GetCustomEditor(targetType.Type); + if (customEditorType != null) + return (CustomEditor)Activator.CreateInstance(customEditorType); + return new ArrayEditor(); } var targetTypeType = TypeUtils.GetType(targetType); if (canUseRefPicker) { if (typeof(Asset).IsAssignableFrom(targetTypeType)) - { return new AssetRefEditor(); - } if (typeof(FlaxEngine.Object).IsAssignableFrom(targetTypeType)) - { return new FlaxObjectRefEditor(); - } } // Use custom editor @@ -85,11 +87,9 @@ namespace FlaxEditor.CustomEditors var checkType = targetTypeType; do { - var type = Internal_GetCustomEditor(checkType); - if (type != null) - { - return (CustomEditor)Activator.CreateInstance(type); - } + var customEditorType = Internal_GetCustomEditor(checkType); + if (customEditorType != null) + return (CustomEditor)Activator.CreateInstance(customEditorType); checkType = checkType.BaseType; // Skip if cannot use ref editors @@ -106,23 +106,17 @@ namespace FlaxEditor.CustomEditors // Select default editor (based on type) if (targetType.IsEnum) - { return new EnumEditor(); - } - if (targetType.IsGenericType) + if (targetType.IsGenericType) { if (targetTypeType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) - { return new DictionaryEditor(); - } // Use custom editor var genericTypeDefinition = targetType.GetGenericTypeDefinition(); - var type = Internal_GetCustomEditor(genericTypeDefinition); - if (type != null) - { - return (CustomEditor)Activator.CreateInstance(type); - } + var customEditorType = Internal_GetCustomEditor(genericTypeDefinition); + if (customEditorType != null) + return (CustomEditor)Activator.CreateInstance(customEditorType); } // The most generic editor From 84f2e652e4a801614b29f275a421ca60c7c33404 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Dec 2022 21:10:54 +0100 Subject: [PATCH 24/45] Fix comparison operator on `Array` and `BitArray` --- Source/Engine/Core/Collections/Array.h | 3 ++- Source/Engine/Core/Collections/BitArray.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h index b89acc44f..d2cd50afd 100644 --- a/Source/Engine/Core/Collections/Array.h +++ b/Source/Engine/Core/Collections/Array.h @@ -872,8 +872,9 @@ public: if (!(data[i] == otherData[i])) return false; } + return true; } - return true; + return false; } template diff --git a/Source/Engine/Core/Collections/BitArray.h b/Source/Engine/Core/Collections/BitArray.h index 660b1e0fe..8d7530e42 100644 --- a/Source/Engine/Core/Collections/BitArray.h +++ b/Source/Engine/Core/Collections/BitArray.h @@ -343,8 +343,9 @@ public: if (!(Get(i) == other.Get(i))) return false; } + return true; } - return true; + return false; } template From 0dd79fe10a11025c038870eff3efa3fbe859aa12 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Dec 2022 21:11:29 +0100 Subject: [PATCH 25/45] Fix sub-context menu child popup still being open when parent context menu gets focused again by the user --- .../Editor/GUI/ContextMenu/ContextMenuBase.cs | 25 +++++++++++++++++++ Source/Editor/Modules/WindowsModule.cs | 2 ++ 2 files changed, 27 insertions(+) diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs index 1093b50b7..0bd87082d 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs @@ -196,6 +196,7 @@ namespace FlaxEditor.GUI.ContextMenu desc.HasSizingFrame = false; OnWindowCreating(ref desc); _window = Platform.CreateWindow(ref desc); + _window.GotFocus += OnWindowGotFocus; _window.LostFocus += OnWindowLostFocus; // Attach to the window @@ -353,6 +354,15 @@ namespace FlaxEditor.GUI.ContextMenu } } + private void OnWindowGotFocus() + { + if (_childCM != null && _window && _window.IsForegroundWindow) + { + // Hide child if user clicked over parent (do it next frame to process other events before - eg. child windows focus loss) + FlaxEngine.Scripting.InvokeOnUpdate(HideChild); + } + } + private void OnWindowLostFocus() { // Skip for parent menus (child should handle lost of focus) @@ -428,6 +438,21 @@ namespace FlaxEditor.GUI.ContextMenu return true; } + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (base.OnKeyDown(key)) + return true; + + switch (key) + { + case KeyboardKeys.Escape: + Hide(); + return true; + } + return false; + } + /// public override void OnDestroy() { diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 0e341581f..2fa93a994 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -193,6 +193,8 @@ namespace FlaxEditor.Modules /// Editor window or null if cannot find any window. public EditorWindow FindEditor(ContentItem item) { + if (item == null) + return null; for (int i = 0; i < Windows.Count; i++) { var win = Windows[i]; From 2f6e793df47db39b8baad0833c63284d92058ba9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Dec 2022 22:39:11 +0100 Subject: [PATCH 26/45] Add utility api for `Tags` usage --- Source/Engine/Level/Tags.cpp | 65 ++++++++++++++++ Source/Engine/Level/Tags.cs | 119 ++++++++++++++++++++++++++++++ Source/Engine/Level/Tags.h | 49 ++++++++++++ Source/Engine/Tests/TestLevel.cpp | 52 +++++++++++++ 4 files changed, 285 insertions(+) diff --git a/Source/Engine/Level/Tags.cpp b/Source/Engine/Level/Tags.cpp index 83bb2e13e..398326885 100644 --- a/Source/Engine/Level/Tags.cpp +++ b/Source/Engine/Level/Tags.cpp @@ -38,6 +38,71 @@ Tag Tags::Get(const StringView& tagName) return tag; } +bool Tags::HasTag(const Array& list, const Tag& tag) +{ + if (tag.Index == -1) + return false; + const String& tagName = tag.ToString(); + for (const Tag& e : list) + { + const String& eName = e.ToString(); + if (e == tag || (eName.Length() > tagName.Length() + 1 && eName.StartsWith(tagName) && eName[tagName.Length()] == '.')) + return true; + } + return false; +} + +bool Tags::HasTagExact(const Array& list, const Tag& tag) +{ + if (tag.Index == -1) + return false; + return list.Contains(tag); +} + +bool Tags::HasAny(const Array& list, const Array& tags) +{ + for (const Tag& tag : tags) + { + if (HasTag(list, tag)) + return true; + } + return false; +} + +bool Tags::HasAnyExact(const Array& list, const Array& tags) +{ + for (const Tag& tag : tags) + { + if (HasTagExact(list, tag)) + return true; + } + return false; +} + +bool Tags::HasAll(const Array& list, const Array& tags) +{ + if (tags.IsEmpty()) + return true; + for (const Tag& tag : tags) + { + if (!HasTag(list, tag)) + return false; + } + return true; +} + +bool Tags::HasAllExact(const Array& list, const Array& tags) +{ + if (tags.IsEmpty()) + return true; + for (const Tag& tag : tags) + { + if (!HasTagExact(list, tag)) + return false; + } + return true; +} + const String& Tags::GetTagName(int32 tag) { return Tag(tag).ToString(); diff --git a/Source/Engine/Level/Tags.cs b/Source/Engine/Level/Tags.cs index 0fda5feab..cab4ff81a 100644 --- a/Source/Engine/Level/Tags.cs +++ b/Source/Engine/Level/Tags.cs @@ -113,4 +113,123 @@ namespace FlaxEngine return Tags.Internal_GetTagName(Index); } } + + partial class Tags + { + /// + /// Checks if the list of tags contains a given tag (including parent tags check). For example, HasTag({"A.B"}, "A") returns true, for exact check use HasTagExact. + /// + /// The tags list to use. + /// The tag to check. + /// True if given tag is contained by the list of tags. Returns false for empty list. + public static bool HasTag(Tag[] list, Tag tag) + { + if (tag.Index == -1) + return false; + string tagName = tag.ToString(); + foreach (Tag e in list) + { + string eName = e.ToString(); + if (e == tag || (eName.Length > tagName.Length + 1 && eName.StartsWith(tagName) && eName[tagName.Length] == '.')) + return true; + } + return false; + } + + /// + /// Checks if the list of tags contains a given tag (exact match). For example, HasTag({"A.B"}, "A") returns false, for parents check use HasTag. + /// + /// The tags list to use. + /// The tag to check. + /// True if given tag is contained by the list of tags. Returns false for empty list. + public static bool HasTagExact(Tag[] list, Tag tag) + { + if (tag.Index == -1) + return false; + if (list == null) + return false; + foreach (Tag e in list) + { + if (e == tag) + return true; + } + return false; + } + + /// + /// Checks if the list of tags contains any of the given tags (including parent tags check). For example, HasAny({"A.B", "C"}, {"A"}) returns true, for exact check use HasAnyExact. + /// + /// The tags list to use. + /// The tags to check. + /// True if any of of the given tags is contained by the list of tags. + public static bool HasAny(Tag[] list, Tag[] tags) + { + if (list == null) + return false; + foreach (Tag tag in list) + { + if (HasTag(list, tag)) + return true; + } + return false; + } + + /// + /// Checks if the list of tags contains any of the given tags (exact match). For example, HasAnyExact({"A.B", "C"}, {"A"}) returns false, for parents check use HasAny. + /// + /// The tags list to use. + /// The tags to check. + /// True if any of the given tags is contained by the list of tags. + public static bool HasAnyExact(Tag[] list, Tag[] tags) + { + if (list == null) + return false; + foreach (Tag tag in list) + { + if (HasTagExact(list, tag)) + return true; + } + return false; + } + + /// + /// Checks if the list of tags contains all of the given tags (including parent tags check). For example, HasAll({"A.B", "C"}, {"A", "C"}) returns true, for exact check use HasAllExact. + /// + /// The tags list to use. + /// The tags to check. + /// True if all of the given tags are contained by the list of tags. Returns true for empty list. + public static bool HasAll(Tag[] list, Tag[] tags) + { + if (tags == null || tags.Length == 0) + return true; + if (list == null || list.Length == 0) + return false; + foreach (Tag tag in list) + { + if (!HasTag(list, tag)) + return false; + } + return true; + } + + /// + /// Checks if the list of tags contains all of the given tags (exact match). For example, HasAllExact({"A.B", "C"}, {"A", "C"}) returns false, for parents check use HasAll. + /// + /// The tags list to use. + /// The tags to check. + /// True if all of the given tags are contained by the list of tags. Returns true for empty list. + public static bool HasAllExact(Tag[] list, Tag[] tags) + { + if (tags == null || tags.Length == 0) + return true; + if (list == null || list.Length == 0) + return false; + foreach (Tag tag in list) + { + if (!HasTagExact(list, tag)) + return false; + } + return true; + } + } } diff --git a/Source/Engine/Level/Tags.h b/Source/Engine/Level/Tags.h index 5616627c9..b52ee0063 100644 --- a/Source/Engine/Level/Tags.h +++ b/Source/Engine/Level/Tags.h @@ -79,6 +79,55 @@ API_CLASS(Static) class FLAXENGINE_API Tags /// The tag. API_FUNCTION() static Tag Get(const StringView& tagName); +public: + /// + /// Checks if the list of tags contains a given tag (including parent tags check). For example, HasTag({"A.B"}, "A") returns true, for exact check use HasTagExact. + /// + /// The tags list to use. + /// The tag to check. + /// True if given tag is contained by the list of tags. Returns false for empty list. + static bool HasTag(const Array& list, const Tag& tag); + + /// + /// Checks if the list of tags contains a given tag (exact match). For example, HasTag({"A.B"}, "A") returns false, for parents check use HasTag. + /// + /// The tags list to use. + /// The tag to check. + /// True if given tag is contained by the list of tags. Returns false for empty list. + static bool HasTagExact(const Array& list, const Tag& tag); + + /// + /// Checks if the list of tags contains any of the given tags (including parent tags check). For example, HasAny({"A.B", "C"}, {"A"}) returns true, for exact check use HasAnyExact. + /// + /// The tags list to use. + /// The tags to check. + /// True if any of of the given tags is contained by the list of tags. + static bool HasAny(const Array& list, const Array& tags); + + /// + /// Checks if the list of tags contains any of the given tags (exact match). For example, HasAnyExact({"A.B", "C"}, {"A"}) returns false, for parents check use HasAny. + /// + /// The tags list to use. + /// The tags to check. + /// True if any of the given tags is contained by the list of tags. + static bool HasAnyExact(const Array& list, const Array& tags); + + /// + /// Checks if the list of tags contains all of the given tags (including parent tags check). For example, HasAll({"A.B", "C"}, {"A", "C"}) returns true, for exact check use HasAllExact. + /// + /// The tags list to use. + /// The tags to check. + /// True if all of the given tags are contained by the list of tags. Returns true for empty list. + static bool HasAll(const Array& list, const Array& tags); + + /// + /// Checks if the list of tags contains all of the given tags (exact match). For example, HasAllExact({"A.B", "C"}, {"A", "C"}) returns false, for parents check use HasAll. + /// + /// The tags list to use. + /// The tags to check. + /// True if all of the given tags are contained by the list of tags. Returns true for empty list. + static bool HasAllExact(const Array& list, const Array& tags); + private: API_FUNCTION(NoProxy) static const String& GetTagName(int32 tag); }; diff --git a/Source/Engine/Tests/TestLevel.cpp b/Source/Engine/Tests/TestLevel.cpp index 830868a1d..0ed90ce45 100644 --- a/Source/Engine/Tests/TestLevel.cpp +++ b/Source/Engine/Tests/TestLevel.cpp @@ -1,7 +1,10 @@ // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. #include "Engine/Core/Math/Vector3.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/StringView.h" #include "Engine/Level/LargeWorlds.h" +#include "Engine/Level/Tags.h" #include TEST_CASE("LargeWorlds") @@ -16,3 +19,52 @@ TEST_CASE("LargeWorlds") CHECK(origin == Vector3(0, 0, LargeWorlds::ChunkSize * 1)); } } + +TEST_CASE("Tags") +{ + SECTION("Tag") + { + auto prevTags = Tags::List; + + Tags::List = Array({ TEXT("A"), TEXT("A.1"), TEXT("B"), TEXT("B.1"), }); + + auto a = Tags::Get(TEXT("A")); + auto a1 = Tags::Get(TEXT("A.1")); + auto b = Tags::Get(TEXT("B")); + auto b1 = Tags::Get(TEXT("B.1")); + auto c = Tags::Get(TEXT("C")); + CHECK(a.Index == 0); + CHECK(a1.Index == 1); + CHECK(b.Index == 2); + CHECK(b1.Index == 3); + CHECK(c.Index == 4); + + Tags::List = prevTags; + } + + SECTION("Tags") + { + auto prevTags = Tags::List; + + Tags::List = Array({ TEXT("A"), TEXT("A.1"), TEXT("B"), TEXT("B.1"), }); + + auto a = Tags::Get(TEXT("A")); + auto a1 = Tags::Get(TEXT("A.1")); + auto b = Tags::Get(TEXT("B")); + auto b1 = Tags::Get(TEXT("B.1")); + auto c = Tags::Get(TEXT("C")); + + Array list = { a1, b1 }; + + CHECK(Tags::HasTag(list, Tag()) == false); + CHECK(Tags::HasTag(list, a1) == true); + CHECK(Tags::HasTag(list, a) == true); + CHECK(Tags::HasTag(list, c) == false); + + CHECK(Tags::HasTagExact(list, a1) == true); + CHECK(Tags::HasTagExact(list, a) == false); + CHECK(Tags::HasTagExact(list, c) == false); + + Tags::List = prevTags; + } +} From e6900d9a17815ab56b258f0bd4d54267fc7103d2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Dec 2022 22:39:20 +0100 Subject: [PATCH 27/45] Minor adjustments to tags --- Source/Engine/Level/Actor.h | 7 ++----- Source/Engine/Level/Tags.cs | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 1133eceae..5281f5c83 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -20,9 +20,6 @@ class PhysicsScene; class SceneRendering; class SceneRenderTask; -// Maximum tag index is used as an invalid value -#define ACTOR_TAG_INVALID 255 - /// /// Base class for all actor objects on the scene. /// @@ -116,13 +113,13 @@ public: API_FUNCTION() bool HasTag() const; /// - /// Determines whether this actor has given tag assigned. + /// Determines whether this actor has given tag assigned (exact match). /// /// The tag to check. API_FUNCTION() bool HasTag(const Tag& tag) const; /// - /// Determines whether this actor has given tag assigned. + /// Determines whether this actor has given tag assigned (exact match). /// /// The tag to check. API_FUNCTION() bool HasTag(const StringView& tag) const; diff --git a/Source/Engine/Level/Tags.cs b/Source/Engine/Level/Tags.cs index cab4ff81a..7e31f7bc5 100644 --- a/Source/Engine/Level/Tags.cs +++ b/Source/Engine/Level/Tags.cs @@ -25,7 +25,7 @@ namespace FlaxEngine internal void OnDeserializing(System.Runtime.Serialization.StreamingContext context) { // Initialize structure with default values to replicate C++ deserialization behavior - this.Index = -1; + Index = -1; } /// From cbef100a174eefa5d08bf7aa3d0ab07f7e63b4d4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Dec 2022 23:01:53 +0100 Subject: [PATCH 28/45] Add `FindActor`/`FindActors` with `Tag` to `Level` --- Source/Engine/Level/Level.cpp | 53 +++++++++++++++++++++++++++++++++++ Source/Engine/Level/Level.h | 18 ++++++++++++ Source/Engine/Level/Tags.cs | 8 +++--- 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 338598541..6c53adf1c 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -755,6 +755,59 @@ int32 Level::GetLayerIndex(const StringView& layer) return result; } +Actor* FindActorRecursive(Actor* node, const Tag& tag) +{ + if (node->HasTag(tag)) + return node; + Actor* result = nullptr; + for (Actor* child : node->Children) + { + result = FindActorRecursive(child, tag); + if (result) + break; + } + return result; +} + +Actor* Level::FindActor(const Tag& tag, Actor* root) +{ + PROFILE_CPU(); + if (root) + return FindActorRecursive(root, tag); + Actor* result = nullptr; + for (Scene* scene : Scenes) + { + result = FindActorRecursive(scene, tag); + if (result) + break; + } + return result; +} + +void FindActorRecursive(Actor* node, const Tag& tag, Array& result) +{ + if (node->HasTag(tag)) + result.Add(node); + for (Actor* child : node->Children) + FindActorRecursive(child, tag, result); +} + +Array Level::FindActors(const Tag& tag, Actor* root) +{ + PROFILE_CPU(); + Array result; + if (root) + { + FindActorRecursive(root, tag); + } + else + { + for (Scene* scene : Scenes) + FindActorRecursive(scene, tag); + } + return result; +} + void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b) { PROFILE_CPU(); diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h index 8039464c5..134d5d8b1 100644 --- a/Source/Engine/Level/Level.h +++ b/Source/Engine/Level/Level.h @@ -16,6 +16,7 @@ class Engine; struct RenderView; struct RenderContext; struct RenderContextBatch; +struct Tag; /// /// The scene manager that contains the loaded scenes collection and spawns/deleted actors. @@ -461,6 +462,23 @@ public: /// API_FUNCTION() static int32 GetLayerIndex(const StringView& layer); +public: + /// + /// Tries to find the actor with the given tag (returns the first one found). + /// + /// The tag of the actor to search for. + /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes. + /// Found actor or null. + API_FUNCTION() static Actor* FindActor(const Tag& tag, Actor* root = nullptr); + + /// + /// Tries to find the actors with the given tag (returns all found). + /// + /// The tag of the actor to search for. + /// The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes. + /// Found actors or empty if none. + API_FUNCTION() static Array FindActors(const Tag& tag, Actor* root = nullptr); + private: // Actor API enum class ActorEventType diff --git a/Source/Engine/Level/Tags.cs b/Source/Engine/Level/Tags.cs index 7e31f7bc5..b8ea3efe6 100644 --- a/Source/Engine/Level/Tags.cs +++ b/Source/Engine/Level/Tags.cs @@ -166,7 +166,7 @@ namespace FlaxEngine { if (list == null) return false; - foreach (Tag tag in list) + foreach (Tag tag in tags) { if (HasTag(list, tag)) return true; @@ -184,7 +184,7 @@ namespace FlaxEngine { if (list == null) return false; - foreach (Tag tag in list) + foreach (Tag tag in tags) { if (HasTagExact(list, tag)) return true; @@ -204,7 +204,7 @@ namespace FlaxEngine return true; if (list == null || list.Length == 0) return false; - foreach (Tag tag in list) + foreach (Tag tag in tags) { if (!HasTag(list, tag)) return false; @@ -224,7 +224,7 @@ namespace FlaxEngine return true; if (list == null || list.Length == 0) return false; - foreach (Tag tag in list) + foreach (Tag tag in tags) { if (!HasTagExact(list, tag)) return false; From dc81147681eaf327a1c608aa9d72ac66198f9d70 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 21 Dec 2022 18:18:17 +0100 Subject: [PATCH 29/45] Convert C# tags checks utilities into extension methods for better usability --- Source/Engine/Level/Tags.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Level/Tags.cs b/Source/Engine/Level/Tags.cs index b8ea3efe6..b4aa3468c 100644 --- a/Source/Engine/Level/Tags.cs +++ b/Source/Engine/Level/Tags.cs @@ -122,7 +122,7 @@ namespace FlaxEngine /// The tags list to use. /// The tag to check. /// True if given tag is contained by the list of tags. Returns false for empty list. - public static bool HasTag(Tag[] list, Tag tag) + public static bool HasTag(this Tag[] list, Tag tag) { if (tag.Index == -1) return false; @@ -142,7 +142,7 @@ namespace FlaxEngine /// The tags list to use. /// The tag to check. /// True if given tag is contained by the list of tags. Returns false for empty list. - public static bool HasTagExact(Tag[] list, Tag tag) + public static bool HasTagExact(this Tag[] list, Tag tag) { if (tag.Index == -1) return false; @@ -162,7 +162,7 @@ namespace FlaxEngine /// The tags list to use. /// The tags to check. /// True if any of of the given tags is contained by the list of tags. - public static bool HasAny(Tag[] list, Tag[] tags) + public static bool HasAny(this Tag[] list, Tag[] tags) { if (list == null) return false; @@ -180,7 +180,7 @@ namespace FlaxEngine /// The tags list to use. /// The tags to check. /// True if any of the given tags is contained by the list of tags. - public static bool HasAnyExact(Tag[] list, Tag[] tags) + public static bool HasAnyExact(this Tag[] list, Tag[] tags) { if (list == null) return false; @@ -198,7 +198,7 @@ namespace FlaxEngine /// The tags list to use. /// The tags to check. /// True if all of the given tags are contained by the list of tags. Returns true for empty list. - public static bool HasAll(Tag[] list, Tag[] tags) + public static bool HasAll(this Tag[] list, Tag[] tags) { if (tags == null || tags.Length == 0) return true; @@ -218,7 +218,7 @@ namespace FlaxEngine /// The tags list to use. /// The tags to check. /// True if all of the given tags are contained by the list of tags. Returns true for empty list. - public static bool HasAllExact(Tag[] list, Tag[] tags) + public static bool HasAllExact(this Tag[] list, Tag[] tags) { if (tags == null || tags.Length == 0) return true; From 1a11238808f32c2898a44fb9fb1bf81d9b2d6792 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 21 Dec 2022 13:47:38 -0600 Subject: [PATCH 30/45] Added name as author --- Source/Editor/Windows/AboutDialog.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index 6c02c4efe..ff41b81ea 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -93,6 +93,7 @@ namespace FlaxEditor.Windows "Stefan Brandmair", "Lukáš Jech", "Jean-Baptiste Perrier", + "Chandler Cox", }); authors.Sort(); var authorsLabel = new Label(4, topParentControl.Bottom + 20, Width - 8, 70) From 96ec28c9a8156ca89449b799dddc3db90247f14a Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 21 Dec 2022 15:07:27 -0600 Subject: [PATCH 31/45] Cleaned up and used cross sprite. --- Source/Editor/GUI/Input/SearchBox.cs | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/Source/Editor/GUI/Input/SearchBox.cs b/Source/Editor/GUI/Input/SearchBox.cs index 04ae0c2d5..8ffd0874a 100644 --- a/Source/Editor/GUI/Input/SearchBox.cs +++ b/Source/Editor/GUI/Input/SearchBox.cs @@ -13,8 +13,6 @@ namespace FlaxEditor.GUI.Input /// public Button ClearSearchButton { get; } - private Color _defaultButtonTextColor; - /// /// Init search box /// @@ -34,33 +32,25 @@ namespace FlaxEditor.GUI.Input ClearSearchButton = new Button { Parent = this, - Width = 15.0f, + Width = 14.0f, Height = 14.0f, AnchorPreset = AnchorPresets.TopRight, - Text = "X", + Text = "", TooltipText = "Cancel Search.", - BackgroundColor = Color.Transparent, + BackgroundColor = TextColor, BorderColor = Color.Transparent, - BackgroundColorHighlighted = Color.Transparent, + BackgroundColorHighlighted = Style.Current.ForegroundGrey, BorderColorHighlighted = Color.Transparent, - BackgroundColorSelected = Color.Transparent, + BackgroundColorSelected = Style.Current.ForegroundGrey, BorderColorSelected = Color.Transparent, + BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Cross12), Visible = false, }; ClearSearchButton.LocalY += 2; ClearSearchButton.LocalX -= 2; ClearSearchButton.Clicked += Clear; - _defaultButtonTextColor = ClearSearchButton.TextColor; TextChanged += () => ClearSearchButton.Visible = !string.IsNullOrEmpty(Text); } - - /// - public override void Update(float deltaTime) - { - base.Update(deltaTime); - - ClearSearchButton.TextColor = ClearSearchButton.IsMouseOver ? Style.Current.ForegroundGrey : _defaultButtonTextColor; - } } } From 01612f1b47acf52ad2609285a7d468826d3bb6b0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 22 Dec 2022 19:23:30 +0100 Subject: [PATCH 32/45] Add tag native serialization --- Source/Engine/Level/Tags.cpp | 16 ++++++++++++++++ Source/Engine/Level/Tags.h | 13 +++++++++++++ 2 files changed, 29 insertions(+) diff --git a/Source/Engine/Level/Tags.cpp b/Source/Engine/Level/Tags.cpp index 398326885..67633df9e 100644 --- a/Source/Engine/Level/Tags.cpp +++ b/Source/Engine/Level/Tags.cpp @@ -3,6 +3,7 @@ #include "Tags.h" #include "Engine/Core/Types/String.h" #include "Engine/Core/Types/StringView.h" +#include "Engine/Serialization/SerializationFwd.h" Array Tags::List; #if !BUILD_RELEASE @@ -24,8 +25,23 @@ bool Tag::operator!=(const StringView& other) const return ToString() != other; } +void FLAXENGINE_API Serialization::Serialize(ISerializable::SerializeStream& stream, const Tag& v, const void* otherObj) +{ + if (v.Index != -1) + stream.String(v.ToString()); + else + stream.String("", 0); +} + +void FLAXENGINE_API Serialization::Deserialize(ISerializable::DeserializeStream& stream, Tag& v, ISerializeModifier* modifier) +{ + v = Tags::Get(stream.GetText()); +} + Tag Tags::Get(const StringView& tagName) { + if (tagName.IsEmpty()) + return Tag(); Tag tag = List.Find(tagName); if (tag.Index == -1 && tagName.HasChars()) { diff --git a/Source/Engine/Level/Tags.h b/Source/Engine/Level/Tags.h index b52ee0063..eda44e224 100644 --- a/Source/Engine/Level/Tags.h +++ b/Source/Engine/Level/Tags.h @@ -4,6 +4,7 @@ #include "Engine/Core/Types/BaseTypes.h" #include "Engine/Core/Collections/Array.h" +#include "Engine/Core/ISerializable.h" /// /// Gameplay tag that represents a hierarchical name of the form 'X.Y.Z' (namespaces separated with a dot). Tags are defined in project LayersAndTagsSettings asset but can be also created from code. @@ -60,6 +61,18 @@ inline uint32 GetHash(const Tag& key) return (uint32)key.Index; } +// @formatter:off +namespace Serialization +{ + inline bool ShouldSerialize(const Tag& v, const void* otherObj) + { + return !otherObj || v != *(Tag*)otherObj; + } + void FLAXENGINE_API Serialize(ISerializable::SerializeStream& stream, const Tag& v, const void* otherObj); + void FLAXENGINE_API Deserialize(ISerializable::DeserializeStream& stream, Tag& v, ISerializeModifier* modifier); +} +// @formatter:on + /// /// Gameplay tags utilities. /// From 8918c71312f94b9622a0a0e7dcec2521ecc16ded Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 22 Dec 2022 19:31:01 +0100 Subject: [PATCH 33/45] Update old header usage --- Source/Engine/Content/JsonAsset.h | 2 +- Source/Engine/ContentImporters/ImportAudio.h | 2 +- Source/Engine/Core/Config/Settings.h | 2 +- Source/Engine/Foliage/FoliageType.h | 2 +- Source/Engine/Graphics/Models/ModelInstanceEntry.h | 2 +- Source/Engine/Graphics/PostProcessSettings.h | 2 +- Source/Engine/Level/Scene/SceneCSGData.h | 2 +- Source/Engine/Level/SceneInfo.h | 2 +- Source/Engine/Level/SceneObject.h | 2 +- Source/Engine/Physics/PhysicalMaterial.h | 2 +- Source/Engine/Renderer/Lightmaps.h | 2 +- Source/Engine/Scripting/BinaryModule.h | 2 +- Source/Engine/Scripting/ManagedSerialization.h | 2 +- Source/Engine/Terrain/TerrainChunk.h | 2 +- Source/Engine/Tools/ModelTool/ModelTool.h | 2 +- Source/Engine/Tools/TextureTool/TextureTool.h | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Source/Engine/Content/JsonAsset.h b/Source/Engine/Content/JsonAsset.h index b8f4a9b50..b6b5d13e7 100644 --- a/Source/Engine/Content/JsonAsset.h +++ b/Source/Engine/Content/JsonAsset.h @@ -3,7 +3,7 @@ #pragma once #include "Asset.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #include "Engine/Serialization/Json.h" /// diff --git a/Source/Engine/ContentImporters/ImportAudio.h b/Source/Engine/ContentImporters/ImportAudio.h index d269e6851..b7b96254b 100644 --- a/Source/Engine/ContentImporters/ImportAudio.h +++ b/Source/Engine/ContentImporters/ImportAudio.h @@ -4,7 +4,7 @@ #include "Types.h" #include "Engine/Tools/AudioTool/AudioDecoder.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #include "Engine/Audio/Config.h" #if COMPILE_WITH_ASSETS_IMPORTER diff --git a/Source/Engine/Core/Config/Settings.h b/Source/Engine/Core/Config/Settings.h index a9fef85f5..17be17fd1 100644 --- a/Source/Engine/Core/Config/Settings.h +++ b/Source/Engine/Core/Config/Settings.h @@ -2,7 +2,7 @@ #pragma once -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" /// /// Base class for all global settings containers for the engine. Helps to apply, store and expose properties to engine/game. diff --git a/Source/Engine/Foliage/FoliageType.h b/Source/Engine/Foliage/FoliageType.h index fbaa8c15f..3d3186e66 100644 --- a/Source/Engine/Foliage/FoliageType.h +++ b/Source/Engine/Foliage/FoliageType.h @@ -5,7 +5,7 @@ #include "Config.h" #include "Engine/Core/Collections/ChunkedArray.h" #include "Engine/Content/Assets/Model.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" /// /// The foliage instances scaling modes. diff --git a/Source/Engine/Graphics/Models/ModelInstanceEntry.h b/Source/Engine/Graphics/Models/ModelInstanceEntry.h index 6e28c2a7e..26d70fe7e 100644 --- a/Source/Engine/Graphics/Models/ModelInstanceEntry.h +++ b/Source/Engine/Graphics/Models/ModelInstanceEntry.h @@ -4,7 +4,7 @@ #include "Engine/Content/AssetReference.h" #include "Engine/Content/Assets/MaterialBase.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #include "Types.h" /// diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 7a8645721..a6ab24964 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -5,7 +5,7 @@ #include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Vector4.h" #include "Engine/Content/AssetReference.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #include "Engine/Content/Assets/Texture.h" #include "Engine/Content/Assets/MaterialBase.h" diff --git a/Source/Engine/Level/Scene/SceneCSGData.h b/Source/Engine/Level/Scene/SceneCSGData.h index 457f4a840..153217892 100644 --- a/Source/Engine/Level/Scene/SceneCSGData.h +++ b/Source/Engine/Level/Scene/SceneCSGData.h @@ -5,7 +5,7 @@ #include "Engine/Core/Math/Triangle.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Physics/CollisionData.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #include "Engine/Content/AssetReference.h" #include "Engine/Content/Assets/RawDataAsset.h" #include "Engine/Content/Assets/Model.h" diff --git a/Source/Engine/Level/SceneInfo.h b/Source/Engine/Level/SceneInfo.h index a0c802e02..ce05e047c 100644 --- a/Source/Engine/Level/SceneInfo.h +++ b/Source/Engine/Level/SceneInfo.h @@ -5,7 +5,7 @@ #include "Engine/Core/Object.h" #include "Engine/Core/Types/String.h" #include "Engine/Core/Collections/Array.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #include "Engine/Renderer/Lightmaps.h" /// diff --git a/Source/Engine/Level/SceneObject.h b/Source/Engine/Level/SceneObject.h index db58a71b8..727939341 100644 --- a/Source/Engine/Level/SceneObject.h +++ b/Source/Engine/Level/SceneObject.h @@ -3,7 +3,7 @@ #pragma once #include "Engine/Scripting/ScriptingObject.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #include "Engine/Core/Collections/Array.h" class SceneTicking; diff --git a/Source/Engine/Physics/PhysicalMaterial.h b/Source/Engine/Physics/PhysicalMaterial.h index b46c1a294..b87cb5035 100644 --- a/Source/Engine/Physics/PhysicalMaterial.h +++ b/Source/Engine/Physics/PhysicalMaterial.h @@ -3,7 +3,7 @@ #pragma once #include "Types.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" /// /// Physical materials are used to define the response of a physical object when interacting dynamically with the world. diff --git a/Source/Engine/Renderer/Lightmaps.h b/Source/Engine/Renderer/Lightmaps.h index 25c0e5262..0be6fbdd8 100644 --- a/Source/Engine/Renderer/Lightmaps.h +++ b/Source/Engine/Renderer/Lightmaps.h @@ -4,7 +4,7 @@ #include "Engine/Core/Types/Guid.h" #include "Engine/Core/Math/Rectangle.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #if USE_EDITOR // Additional options used in editor for lightmaps baking diff --git a/Source/Engine/Scripting/BinaryModule.h b/Source/Engine/Scripting/BinaryModule.h index 7c69a2dc6..6a8e87314 100644 --- a/Source/Engine/Scripting/BinaryModule.h +++ b/Source/Engine/Scripting/BinaryModule.h @@ -8,7 +8,7 @@ #include "Engine/Core/Types/Variant.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Core/Collections/Array.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #include "ManagedCLR/MAssemblyOptions.h" /// diff --git a/Source/Engine/Scripting/ManagedSerialization.h b/Source/Engine/Scripting/ManagedSerialization.h index 10495dd36..874c5ff77 100644 --- a/Source/Engine/Scripting/ManagedSerialization.h +++ b/Source/Engine/Scripting/ManagedSerialization.h @@ -2,7 +2,7 @@ #pragma once -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #include "ManagedCLR/MTypes.h" /// diff --git a/Source/Engine/Terrain/TerrainChunk.h b/Source/Engine/Terrain/TerrainChunk.h index d8eb5a51f..4615a2c17 100644 --- a/Source/Engine/Terrain/TerrainChunk.h +++ b/Source/Engine/Terrain/TerrainChunk.h @@ -5,7 +5,7 @@ #include "Engine/Core/Math/BoundingBox.h" #include "Engine/Core/Math/Matrix.h" #include "Engine/Core/Math/Transform.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #include "Engine/Content/Assets/MaterialBase.h" #include "Engine/Level/Scene/Lightmap.h" diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index 84c763a27..237f41826 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -7,7 +7,7 @@ #include "Engine/Core/Config.h" #include "Engine/Content/Assets/ModelBase.h" #if USE_EDITOR -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" #include "Engine/Graphics/Models/ModelData.h" #include "Engine/Graphics/Models/SkeletonData.h" #include "Engine/Animations/AnimationData.h" diff --git a/Source/Engine/Tools/TextureTool/TextureTool.h b/Source/Engine/Tools/TextureTool/TextureTool.h index 2119fa9f1..159efd58c 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.h +++ b/Source/Engine/Tools/TextureTool/TextureTool.h @@ -7,7 +7,7 @@ #include "Engine/Render2D/SpriteAtlas.h" #include "Engine/Graphics/Textures/Types.h" #include "Engine/Graphics/Textures/GPUTexture.h" -#include "Engine/Serialization/ISerializable.h" +#include "Engine/Core/ISerializable.h" class JsonWriter; From d7d7c1e98f26eff2d19952cb633c2d62a8047ac5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 22 Dec 2022 12:44:34 -0600 Subject: [PATCH 34/45] Center the context menu that is spawned from clicking the add scripts button on an actor --- Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 47147381e..68dca9883 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -72,7 +72,7 @@ namespace FlaxEditor.CustomEditors.Dedicated } cm.ItemClicked += item => AddScript((ScriptType)item.Tag); cm.SortItems(); - cm.Show(this, button.BottomLeft); + cm.Show(this, button.BottomLeft - new Float2((cm.Width - button.Width) / 2, 0)); } /// From 762f460e2391ba750e61cb3b1fd94cafc95fb681 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 27 Dec 2022 18:41:36 +0100 Subject: [PATCH 35/45] Fix missing `NetworkManager::ClientDisconnected` call for local host client #845 --- Source/Engine/Networking/NetworkManager.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Networking/NetworkManager.cpp b/Source/Engine/Networking/NetworkManager.cpp index ef5060e73..a16d46ea3 100644 --- a/Source/Engine/Networking/NetworkManager.cpp +++ b/Source/Engine/Networking/NetworkManager.cpp @@ -325,10 +325,14 @@ void NetworkManager::Stop() Delete(client); Clients.RemoveAt(i); } + if (Mode == NetworkManagerMode::Host && LocalClient) + { + ClientDisconnected(LocalClient); + LocalClient->State = NetworkConnectionState::Disconnected; + } StopPeer(); if (LocalClient) { - LocalClient->State = NetworkConnectionState::Disconnected; Delete(LocalClient); LocalClient = nullptr; } From cb6590e7ef77a9b03a5e29ea942179afa4f07f3a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 27 Dec 2022 18:50:08 +0100 Subject: [PATCH 36/45] Format 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 0e9c34954..2999fe957 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -1047,7 +1047,7 @@ namespace FlaxEngine.GUI // Animate view offset _viewOffset = isDeltaSlow ? _targetViewOffset : Float2.Lerp(_viewOffset, _targetViewOffset, deltaTime * 20.0f); - + // Clicking outside of the text box will end text editing. Left will keep the value, right will restore original value if (_isEditing && EndEditOnClick) { From 9050377526d7aa67ce6c71a298a021c38c94007e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 27 Dec 2022 19:04:09 +0100 Subject: [PATCH 37/45] Fix imported shader source last char value to prevent strange diffs in Version Control Systems --- Content/Shaders/AtmospherePreCompute.flax | 4 ++-- Content/Shaders/BakeLightmap.flax | 4 ++-- Content/Shaders/BitonicSort.flax | 4 ++-- Content/Shaders/ColorGrading.flax | 4 ++-- Content/Shaders/DebugDraw.flax | 4 ++-- Content/Shaders/Editor/LightmapUVsDensity.flax | 4 ++-- Content/Shaders/Editor/MaterialComplexity.flax | 4 ++-- Content/Shaders/Editor/QuadOverdraw.flax | 4 ++-- Content/Shaders/Editor/VertexColors.flax | 4 ++-- Content/Shaders/EyeAdaptation.flax | 4 ++-- Content/Shaders/FXAA.flax | 4 ++-- Content/Shaders/Fog.flax | 4 ++-- Content/Shaders/Forward.flax | 4 ++-- Content/Shaders/GBuffer.flax | 4 ++-- Content/Shaders/GI/DDGI.flax | 4 ++-- Content/Shaders/GI/GlobalSurfaceAtlas.flax | 4 ++-- Content/Shaders/GPUParticlesSorting.flax | 4 ++-- Content/Shaders/GUI.flax | 4 ++-- Content/Shaders/GlobalSignDistanceField.flax | 4 ++-- Content/Shaders/Histogram.flax | 4 ++-- Content/Shaders/Lights.flax | 4 ++-- Content/Shaders/MotionBlur.flax | 4 ++-- Content/Shaders/MultiScaler.flax | 4 ++-- Content/Shaders/PostProcessing.flax | 2 +- Content/Shaders/ProbesFilter.flax | 4 ++-- Content/Shaders/Quad.flax | 4 ++-- Content/Shaders/Reflections.flax | 4 ++-- Content/Shaders/SMAA.flax | 4 ++-- Content/Shaders/SSAO.flax | 4 ++-- Content/Shaders/Shadows.flax | 4 ++-- Content/Shaders/Sky.flax | 4 ++-- Content/Shaders/TAA.flax | 4 ++-- Content/Shaders/VolumetricFog.flax | 4 ++-- Source/Engine/ContentImporters/ImportShader.cpp | 6 ++++-- 34 files changed, 69 insertions(+), 67 deletions(-) diff --git a/Content/Shaders/AtmospherePreCompute.flax b/Content/Shaders/AtmospherePreCompute.flax index 527275f61..20491857f 100644 --- a/Content/Shaders/AtmospherePreCompute.flax +++ b/Content/Shaders/AtmospherePreCompute.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f06c5ac062366b3ef86c355c6cc8b3a984b8eb9bc027ffc0874bc51dbaef4b3e -size 11720 +oid sha256:68e1592216496fd6749ac4538dd2d549c39813d17ec3114514889100782007a6 +size 11418 diff --git a/Content/Shaders/BakeLightmap.flax b/Content/Shaders/BakeLightmap.flax index ef3e02163..03af565fc 100644 --- a/Content/Shaders/BakeLightmap.flax +++ b/Content/Shaders/BakeLightmap.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18eb0ec2642a8f87ee3d4dbe7af8771b0f79f983637aee1a9e5baebce435976b -size 16284 +oid sha256:0cf36b83e0bdbbf47b2d88b39d18ae3e1ac85246d46a05f1e6c58f89f4abc105 +size 15794 diff --git a/Content/Shaders/BitonicSort.flax b/Content/Shaders/BitonicSort.flax index 980b664f3..c49c2b73f 100644 --- a/Content/Shaders/BitonicSort.flax +++ b/Content/Shaders/BitonicSort.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8d1a797f813d3af8a98bcb8110981ea8c033324e94fb6413615c811b5c5bfd7 -size 6808 +oid sha256:fbecce17a3a0dba01aa1e8d537041c27339a5f171c04cdeb83c68765fd6ac652 +size 6548 diff --git a/Content/Shaders/ColorGrading.flax b/Content/Shaders/ColorGrading.flax index a7a466d5e..d36626d2c 100644 --- a/Content/Shaders/ColorGrading.flax +++ b/Content/Shaders/ColorGrading.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2bda196600fbc9d6f362e85471399331941272cdd92f9888bdcf7a1b8b04ada7 -size 10925 +oid sha256:1841743cd96e3efba196cba05678658463c1c1fcd6b06ac7703e87bb2850d641 +size 10618 diff --git a/Content/Shaders/DebugDraw.flax b/Content/Shaders/DebugDraw.flax index ea3807153..4e824c30c 100644 --- a/Content/Shaders/DebugDraw.flax +++ b/Content/Shaders/DebugDraw.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b25f3b058f2bca838a661e46e90931f347b7e9984626e2b0112270e0f3e4fa47 -size 2115 +oid sha256:9fc85c05a9203d53f03db74f4d04a39e651e34d310de119f06279a1ce0299bcc +size 2053 diff --git a/Content/Shaders/Editor/LightmapUVsDensity.flax b/Content/Shaders/Editor/LightmapUVsDensity.flax index c817058f7..145040bbe 100644 --- a/Content/Shaders/Editor/LightmapUVsDensity.flax +++ b/Content/Shaders/Editor/LightmapUVsDensity.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f23c12db6332866a9ac91d576575448af02e1a4e0fff20529f32911970de72e5 -size 4509 +oid sha256:7df3b4a9464524bb0354ab9f4e7dc6256af56d3f13f3b347a946742ecbf4c1d1 +size 4391 diff --git a/Content/Shaders/Editor/MaterialComplexity.flax b/Content/Shaders/Editor/MaterialComplexity.flax index 3c8770e50..490f72a38 100644 --- a/Content/Shaders/Editor/MaterialComplexity.flax +++ b/Content/Shaders/Editor/MaterialComplexity.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a891d1a83c1c99f17f43f75fbe09ffe5df99bd52f150875771f70ee67f2f52e0 -size 1336 +oid sha256:446446a3e8e3e72793cfdf3cf76d705917a61814fd1f40cb60827ad3c002b21d +size 1303 diff --git a/Content/Shaders/Editor/QuadOverdraw.flax b/Content/Shaders/Editor/QuadOverdraw.flax index 7eba0013e..7e6bdfb9a 100644 --- a/Content/Shaders/Editor/QuadOverdraw.flax +++ b/Content/Shaders/Editor/QuadOverdraw.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a25d8d4db381507c4c2a33d794b7eec8994ae8d281478e057232adf788224db -size 1448 +oid sha256:50ab3a3734b0cd349aadd61c404739b48146cc1f326412dc5e4604da3fe03dc6 +size 1414 diff --git a/Content/Shaders/Editor/VertexColors.flax b/Content/Shaders/Editor/VertexColors.flax index 2d862a36d..92b00ddac 100644 --- a/Content/Shaders/Editor/VertexColors.flax +++ b/Content/Shaders/Editor/VertexColors.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf47e691ee17a3bf17948cc365a327685e5298568a993fec47a920934d1a86fb -size 2102 +oid sha256:66e418d872f95996806ff866e4786d8d22e504c5209f4b7b8e140c29a77ce93f +size 2053 diff --git a/Content/Shaders/EyeAdaptation.flax b/Content/Shaders/EyeAdaptation.flax index a81177004..1f52e67da 100644 --- a/Content/Shaders/EyeAdaptation.flax +++ b/Content/Shaders/EyeAdaptation.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00e1a4e4fc4153cb83b090169b9d600686e9bf6a6565914de46d55b1b945025e -size 4610 +oid sha256:0f1a19e5a918049d093d4536e540d4ffb779c2b0b4524a277b1503afbecea79b +size 4453 diff --git a/Content/Shaders/FXAA.flax b/Content/Shaders/FXAA.flax index 7f8e18d8a..4b6bdfe25 100644 --- a/Content/Shaders/FXAA.flax +++ b/Content/Shaders/FXAA.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8a6d89039e93acb1ac658b4b63ab9baa21c7777d948f14732571529a8cfe519 -size 25139 +oid sha256:142d7ede22ed99b095dd07fcce722b7668554911e57a1b41e17842e0383a8d24 +size 24484 diff --git a/Content/Shaders/Fog.flax b/Content/Shaders/Fog.flax index 37410e1af..f19396bd8 100644 --- a/Content/Shaders/Fog.flax +++ b/Content/Shaders/Fog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58dab7514164b3cf941636dbead945da12b13dc485dbec997227711c2e67e58f -size 2878 +oid sha256:4703c3f843dec8248938344d04778d4705eb61247610906af9ec22b4d42f1eae +size 2795 diff --git a/Content/Shaders/Forward.flax b/Content/Shaders/Forward.flax index b922a3b16..393603913 100644 --- a/Content/Shaders/Forward.flax +++ b/Content/Shaders/Forward.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d18f21b456efe8c9f5c38c35aa0ce6e13552966aa48445cb33826837bea2819 -size 1221 +oid sha256:0c76097d4a231be004658cdbbeae05058ff5408cb003d38ac5ef6ca927c04484 +size 1197 diff --git a/Content/Shaders/GBuffer.flax b/Content/Shaders/GBuffer.flax index 78308a4bb..a22a76fb1 100644 --- a/Content/Shaders/GBuffer.flax +++ b/Content/Shaders/GBuffer.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:180768a9304d7bc12c8fb487a39a9991575d55cfaf5fe3135a4985098340d042 -size 2845 +oid sha256:255a153284978cff6e0285f40355ba8695679f97215c2efe39082ae7422f7d8f +size 2774 diff --git a/Content/Shaders/GI/DDGI.flax b/Content/Shaders/GI/DDGI.flax index 9e45adb50..6b0a66187 100644 --- a/Content/Shaders/GI/DDGI.flax +++ b/Content/Shaders/GI/DDGI.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3816b8fdd088656e385f46832c2719c77d386d12ec14ac3d0ac6df017b2c7e4 -size 23668 +oid sha256:e8a1f629462b91a6e520bff1c52e6144e157aa6c0b64508785f867e38f6fa051 +size 23092 diff --git a/Content/Shaders/GI/GlobalSurfaceAtlas.flax b/Content/Shaders/GI/GlobalSurfaceAtlas.flax index 3450fa8c6..ba31eea60 100644 --- a/Content/Shaders/GI/GlobalSurfaceAtlas.flax +++ b/Content/Shaders/GI/GlobalSurfaceAtlas.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c342070b981a0dbf335276434ce59f345861e8f9e7d4f2cf07ae2016542642e8 -size 12954 +oid sha256:8247f56712db3e47150b4f75416ff8baee778f43f4825d9c802e6a95e57c802e +size 12614 diff --git a/Content/Shaders/GPUParticlesSorting.flax b/Content/Shaders/GPUParticlesSorting.flax index 6b2a31931..08b6a4d78 100644 --- a/Content/Shaders/GPUParticlesSorting.flax +++ b/Content/Shaders/GPUParticlesSorting.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5d131298c4f138278a5339e7d1a2dd8263c4afa4e27f3fa345ebda21989ea15 -size 2726 +oid sha256:fcfaa7567daaeac0ba0e3d135db52c674b7f17f8bace31b8a4f75ef8a24b21a7 +size 2639 diff --git a/Content/Shaders/GUI.flax b/Content/Shaders/GUI.flax index a6e231bd4..18f16aa34 100644 --- a/Content/Shaders/GUI.flax +++ b/Content/Shaders/GUI.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5512b7584d43b571a9a30cec4b5c160cebd8924f4305c8de0ae439a664d143a7 -size 5111 +oid sha256:b4b204acc4b60f93a490e3178a853a7ae059ac5e6e1e4ee4dc7ebc0b8771413c +size 4943 diff --git a/Content/Shaders/GlobalSignDistanceField.flax b/Content/Shaders/GlobalSignDistanceField.flax index ec1d39d8f..50ca8c78d 100644 --- a/Content/Shaders/GlobalSignDistanceField.flax +++ b/Content/Shaders/GlobalSignDistanceField.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e14a67948d93abb8ef6124b39fdb7b1bc682e1fdf5341ce6255eefe2606a2dfc -size 11787 +oid sha256:dd3a18c7831eba6b531d82528733e2f7ab58a02b076441a974e2b999b26da616 +size 11498 diff --git a/Content/Shaders/Histogram.flax b/Content/Shaders/Histogram.flax index 4c5967874..9df23aad7 100644 --- a/Content/Shaders/Histogram.flax +++ b/Content/Shaders/Histogram.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92a990f5286c449d3274bd90766d022d03aeaa43ecb93c0589d391828842b16b -size 2609 +oid sha256:761d2f9eea984a5cb46ba7a83f3f49c9cb7452a05420faecc70c55c3b56cfa45 +size 2532 diff --git a/Content/Shaders/Lights.flax b/Content/Shaders/Lights.flax index 612a634ab..c76a501fe 100644 --- a/Content/Shaders/Lights.flax +++ b/Content/Shaders/Lights.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86919e780799e02ebae7ce13f876925219e5b03234c7fdae3b610623d8bc81b8 -size 5299 +oid sha256:37b0d62eff4ed0ea2cd48c351e0f8e0ed98f1aac3b272fd0849227eda6cb6856 +size 5122 diff --git a/Content/Shaders/MotionBlur.flax b/Content/Shaders/MotionBlur.flax index 5dac7c3eb..a2c201bc6 100644 --- a/Content/Shaders/MotionBlur.flax +++ b/Content/Shaders/MotionBlur.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82109c08b00acfcd46818e35b05732ccdfd7f453bf6b3cea5b450603b2d1baa8 -size 9685 +oid sha256:7b468b522e29e3464a2e310334e011d793a10ebc20092e5bedd76d33ed608054 +size 9428 diff --git a/Content/Shaders/MultiScaler.flax b/Content/Shaders/MultiScaler.flax index 3b71c27aa..8e5790610 100644 --- a/Content/Shaders/MultiScaler.flax +++ b/Content/Shaders/MultiScaler.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:737c91950c010adb13cee6669f77d95737aa671f17d71fb6bee8ca5c3c627e27 -size 7206 +oid sha256:a0412a83a79884e77e09f5debfedf45b5856756b2e2298edef196c9434d4bf19 +size 7006 diff --git a/Content/Shaders/PostProcessing.flax b/Content/Shaders/PostProcessing.flax index a771d9882..bba56658c 100644 --- a/Content/Shaders/PostProcessing.flax +++ b/Content/Shaders/PostProcessing.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f72955c7285fb135c80ba73ac0a1e62463394c86979bdad1e0a789158ad7728c +oid sha256:5f3c7923418f5872dc4bbfb08c39327f46d357c62827711da5e4e50332f26bc0 size 16522 diff --git a/Content/Shaders/ProbesFilter.flax b/Content/Shaders/ProbesFilter.flax index 760a91040..59ec47ef3 100644 --- a/Content/Shaders/ProbesFilter.flax +++ b/Content/Shaders/ProbesFilter.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3765117ee658c768184f9af0c7aa7799c575cfc68f5ce0d824e211570d887135 -size 2108 +oid sha256:bfc735d6646e35594c1441298a4718ea8cffe4517762f65982dabed50af8215e +size 2030 diff --git a/Content/Shaders/Quad.flax b/Content/Shaders/Quad.flax index 485a11fbf..6a6897dfb 100644 --- a/Content/Shaders/Quad.flax +++ b/Content/Shaders/Quad.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04c502c31eaceba9738444cd3a9419474ebfbfd42e7efa39b7cc5778a19e267c -size 2310 +oid sha256:f9225e31ac76e66a2f4c09f70af326229c563155dc0c34eccb9f243f80c07908 +size 2242 diff --git a/Content/Shaders/Reflections.flax b/Content/Shaders/Reflections.flax index 9cf210fec..e594c5727 100644 --- a/Content/Shaders/Reflections.flax +++ b/Content/Shaders/Reflections.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b651d798d3e2a36d3afe960e087f061cf53cac826ac48fff17eebba7293ed46f -size 3288 +oid sha256:772f6b54b8ab6f6f054935bd9593616896c9e7b56249f0b084d85858a50f4f00 +size 3193 diff --git a/Content/Shaders/SMAA.flax b/Content/Shaders/SMAA.flax index e315c9ef5..605524a5d 100644 --- a/Content/Shaders/SMAA.flax +++ b/Content/Shaders/SMAA.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c53b7a4929736b7f603f47475e2d668100e52f53c6ba176397b06b2ebc0f050d -size 47673 +oid sha256:8071432958f2e295bc22f7058970c18135c001a4f705f20c4c99385d8acf1daa +size 46449 diff --git a/Content/Shaders/SSAO.flax b/Content/Shaders/SSAO.flax index e4a48b1f8..232e72a8c 100644 --- a/Content/Shaders/SSAO.flax +++ b/Content/Shaders/SSAO.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8ed05e81b6c7822c725976551f75960e770f3a32fdb23ab49f60e32e366ae15 -size 37614 +oid sha256:b6747405d3ef0698c9b84ceebfc1ba2fb3d255ffcec10bd5e5fb86b1ff632187 +size 36841 diff --git a/Content/Shaders/Shadows.flax b/Content/Shaders/Shadows.flax index 8c45d669a..9c5e5b142 100644 --- a/Content/Shaders/Shadows.flax +++ b/Content/Shaders/Shadows.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61ea612b8ee409c1de4bb4ff4455f31bf32ed756a6612d9c5a9a152ff6b034d0 -size 7847 +oid sha256:b823730a83d35980f63d3adb7d23c8ee4585826c6e6dd09555dd97aa77dad269 +size 7654 diff --git a/Content/Shaders/Sky.flax b/Content/Shaders/Sky.flax index 2ff583cfc..e7bc71130 100644 --- a/Content/Shaders/Sky.flax +++ b/Content/Shaders/Sky.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ad54e191498e2c0067b32b041cfb351fa18f928d36372ae259a8b64a9dfb311 -size 3572 +oid sha256:0791b4d9504a0284ecb7f45b8339abab12a00396c2b4063ee4320ac15d92bc9b +size 3473 diff --git a/Content/Shaders/TAA.flax b/Content/Shaders/TAA.flax index 768ffd750..65d5a2fae 100644 --- a/Content/Shaders/TAA.flax +++ b/Content/Shaders/TAA.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e15b9c0eed4678e45c681a789159fff3ff13d5f0cbd0a4880df1f6c6f68d9812 -size 3371 +oid sha256:f8103f574606ee8a27a3e1ac9d2bc796c5110711bb12246e1ff20e67d8844591 +size 3284 diff --git a/Content/Shaders/VolumetricFog.flax b/Content/Shaders/VolumetricFog.flax index 997b1ab8f..660b70338 100644 --- a/Content/Shaders/VolumetricFog.flax +++ b/Content/Shaders/VolumetricFog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fd020a2c82854ed852a2bb7682dc69b7abe31f139ecff485082d21d9188993d -size 14257 +oid sha256:819c97f3b6d28209f0fe3f09a0b89af88e24cff5bf57658e696b586f1ec040ed +size 13843 diff --git a/Source/Engine/ContentImporters/ImportShader.cpp b/Source/Engine/ContentImporters/ImportShader.cpp index fe1253644..c6394c91b 100644 --- a/Source/Engine/ContentImporters/ImportShader.cpp +++ b/Source/Engine/ContentImporters/ImportShader.cpp @@ -32,12 +32,14 @@ CreateAssetResult ImportShader::Import(CreateAssetContext& context) LOG(Warning, "Empty shader source file."); return CreateAssetResult::Error; } - context.Data.Header.Chunks[SourceCodeChunk]->Data.Allocate(sourceCodeSize + 1); - const auto sourceCode = context.Data.Header.Chunks[SourceCodeChunk]->Get(); + const auto& sourceCodeChunk = context.Data.Header.Chunks[SourceCodeChunk]; + sourceCodeChunk->Data.Allocate(sourceCodeSize + 1); + const auto sourceCode = sourceCodeChunk->Get(); Platform::MemoryCopy(sourceCode, sourceCodeText.Get(), sourceCodeSize); // Encrypt source code Encryption::EncryptBytes(sourceCode, sourceCodeSize); + sourceCode[sourceCodeSize] = 0; // Set Custom Data with Header ShaderStorage::Header20 shaderHeader; From 25028e1d67537356df08fef1081157bc23c6c682 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 27 Dec 2022 19:07:24 +0100 Subject: [PATCH 38/45] Improve renaming ContentTreeNode as in #849 --- Source/Editor/Content/Tree/ContentTreeNode.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 1d70a8252..27e7cf76c 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -95,19 +95,17 @@ namespace FlaxEditor.Content { if (!_folder.CanRename) return; - Editor.Instance.Windows.ContentWin.ScrollingOnTreeView(false); + // Start renaming the folder - var dialog = RenamePopup.Show(this, HeaderRect, _folder.ShortName, false); + Editor.Instance.Windows.ContentWin.ScrollingOnTreeView(false); + var dialog = RenamePopup.Show(this, TextRect, _folder.ShortName, false); dialog.Tag = _folder; dialog.Renamed += popup => { Editor.Instance.Windows.ContentWin.Rename((ContentFolder)popup.Tag, popup.Text); Editor.Instance.Windows.ContentWin.ScrollingOnTreeView(true); }; - dialog.Closed += popup => - { - Editor.Instance.Windows.ContentWin.ScrollingOnTreeView(true); - }; + dialog.Closed += popup => { Editor.Instance.Windows.ContentWin.ScrollingOnTreeView(true); }; } /// From 5bccd010cb8f3958307cdf45cf85eda8fa2e0b08 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 27 Dec 2022 19:22:17 +0100 Subject: [PATCH 39/45] Code style fix --- Source/Editor/Modules/UIModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 80f0bb97c..a0b9ca19b 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -738,7 +738,7 @@ namespace FlaxEditor.Modules return; // Find layout to use - var searchFolder = StringUtils.CombinePaths(Editor.LocalCachePath, "LayoutsCache") ; + var searchFolder = StringUtils.CombinePaths(Editor.LocalCachePath, "LayoutsCache"); if (!Directory.Exists(searchFolder)) Directory.CreateDirectory(searchFolder); var files = Directory.GetFiles(searchFolder, "Layout_*.xml", SearchOption.TopDirectoryOnly); From 6b0ead59d2774c2f484d0fdb18880149d5bd04b5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 27 Dec 2022 20:24:01 +0100 Subject: [PATCH 40/45] Fix code style --- Source/Engine/UI/GUI/Common/Button.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 190ba8eff..30ff25aa1 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -75,7 +75,7 @@ namespace FlaxEngine.GUI /// Event fired when users mouse enters the control. /// public event Action HoverBegin; - + /// /// Event fired when users mouse leaves the control. /// @@ -246,6 +246,7 @@ namespace FlaxEngine.GUI public override void OnMouseEnter(Float2 location) { base.OnMouseEnter(location); + HoverBegin?.Invoke(); } @@ -256,7 +257,7 @@ namespace FlaxEngine.GUI { OnPressEnd(); } - + HoverEnd?.Invoke(); base.OnMouseLeave(); From 5c9a27a6d615294a1a94b51a422bf457cf0b9d5b Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 27 Dec 2022 20:59:38 +0200 Subject: [PATCH 41/45] Serialize C++ includes cache results Almost half the build tool runtime is spent scanning includes in C++-files, now the results are cached and invalidated when files last write timestamp changes. --- Source/Tools/Flax.Build/Build/Builder.cs | 11 + .../Build/NativeCpp/IncludesCache.cs | 188 +++++++++++++++++- 2 files changed, 191 insertions(+), 8 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Builder.cs b/Source/Tools/Flax.Build/Build/Builder.cs index 5139ed621..d81205161 100644 --- a/Source/Tools/Flax.Build/Build/Builder.cs +++ b/Source/Tools/Flax.Build/Build/Builder.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Flax.Build.Graph; +using Flax.Build.NativeCpp; namespace Flax.Build { @@ -256,6 +257,11 @@ namespace Flax.Build if (targets.Length == 0) Log.Warning("No targets to build"); + using (new ProfileEventScope("LoadIncludesCache")) + { + IncludesCache.LoadCache(); + } + // Create task graph for building all targets var graph = new TaskGraph(project.ProjectFolderPath); foreach (var target in targets) @@ -393,6 +399,11 @@ namespace Flax.Build } } + using (new ProfileEventScope("SaveIncludesCache")) + { + IncludesCache.SaveCache(); + } + foreach (var target in targets) { target.PostBuild(); diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs b/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs index fa4001158..a65252eab 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.Serialization.Formatters.Binary; using System.Text; namespace Flax.Build.NativeCpp @@ -13,9 +14,151 @@ namespace Flax.Build.NativeCpp /// public static class IncludesCache { - private static readonly Dictionary DirectIncludesCache = new Dictionary(); - private static readonly Dictionary AllIncludesCache = new Dictionary(); + private static Dictionary DirectIncludesCache = new Dictionary(); + private static Dictionary AllIncludesCache = new Dictionary(); + private static Dictionary DirectIncludesTimestampCache = new Dictionary(); + private static Dictionary AllIncludesTimestampCache = new Dictionary(); + private static Dictionary FileExistsCache = new Dictionary(); + private static Dictionary FileTimestampCache = new Dictionary(); private static readonly string IncludeToken = "include"; + private static string CachePath; + + public static void LoadCache() + { + CachePath = Path.Combine(Globals.Root, Configuration.IntermediateFolder, "IncludesCache.cache"); + if (!File.Exists(CachePath)) + return; + + using (var stream = new FileStream(CachePath, FileMode.Open)) + using (var reader = new BinaryReader(stream)) + { + int version = reader.ReadInt32(); + + // DirectIncludesCache + { + int count = reader.ReadInt32(); + for (int i = 0; i < count; i++) + { + string key = reader.ReadString(); + string[] values = new string[reader.ReadInt32()]; + for (int j = 0; j < values.Length; j++) + values[j] = reader.ReadString(); + + DirectIncludesCache.Add(key, values); + } + } + + // AllIncludesCache + { + int count = reader.ReadInt32(); + for (int i = 0; i < count; i++) + { + string key = reader.ReadString(); + string[] values = new string[reader.ReadInt32()]; + for (int j = 0; j < values.Length; j++) + values[j] = reader.ReadString(); + + AllIncludesCache.Add(key, values); + } + } + + // DirectIncludesTimestampCache + { + int count = reader.ReadInt32(); + for (int i = 0; i < count; i++) + { + string key = reader.ReadString(); + DateTime value = new DateTime(reader.ReadInt64()); + DirectIncludesTimestampCache.Add(key, value); + } + } + + // AllIncludesTimestampCache + { + int count = reader.ReadInt32(); + for (int i = 0; i < count; i++) + { + string key = reader.ReadString(); + DateTime value = new DateTime(reader.ReadInt64()); + AllIncludesTimestampCache.Add(key, value); + } + } + } + } + + public static void SaveCache() + { + using (var stream = new FileStream(CachePath, FileMode.Create)) + using (var writer = new BinaryWriter(stream)) + { + // Version + writer.Write(1); + + // DirectIncludesCache + { + writer.Write(DirectIncludesCache.Count); + foreach (KeyValuePair kvp in DirectIncludesCache) + { + writer.Write(kvp.Key); + writer.Write(kvp.Value.Length); + foreach (var value in kvp.Value) + writer.Write(value); + } + } + + // AllIncludesCache + { + writer.Write(AllIncludesCache.Count); + foreach (KeyValuePair kvp in AllIncludesCache) + { + writer.Write(kvp.Key); + writer.Write(kvp.Value.Length); + foreach (var value in kvp.Value) + writer.Write(value); + } + } + + // DirectIncludesTimestampCache + { + writer.Write(DirectIncludesTimestampCache.Count); + foreach (KeyValuePair kvp in DirectIncludesTimestampCache) + { + writer.Write(kvp.Key); + writer.Write(kvp.Value.Ticks); + } + } + + // AllIncludesTimestampCache + { + writer.Write(AllIncludesTimestampCache.Count); + foreach (KeyValuePair kvp in AllIncludesTimestampCache) + { + writer.Write(kvp.Key); + writer.Write(kvp.Value.Ticks); + } + } + } + } + + private static bool FileExists(string path) + { + if (FileExistsCache.TryGetValue(path, out bool result)) + return result; + + result = File.Exists(path); + FileExistsCache.Add(path, result); + return result; + } + + private static DateTime FileLastWriteTime(string path) + { + if (FileTimestampCache.TryGetValue(path, out DateTime result)) + return result; + + result = File.GetLastWriteTime(path); + FileTimestampCache.Add(path, result); + return result; + } /// /// Finds all included files by the source file (including dependencies). @@ -24,12 +167,24 @@ namespace Flax.Build.NativeCpp /// The list of included files by this file. Not null nut may be empty. public static string[] FindAllIncludedFiles(string sourceFile) { + DateTime? lastModified = null; + // Try hit the cache string[] result; if (AllIncludesCache.TryGetValue(sourceFile, out result)) - return result; + { + if (AllIncludesTimestampCache.TryGetValue(sourceFile, out var cachedTimestamp)) + { + lastModified = FileLastWriteTime(sourceFile); + if (lastModified == cachedTimestamp) + return result; + } - if (!File.Exists(sourceFile)) + AllIncludesCache.Remove(sourceFile); + AllIncludesTimestampCache.Remove(sourceFile); + } + + if (!FileExists(sourceFile)) throw new Exception(string.Format("Cannot scan file \"{0}\" for includes because it does not exist.", sourceFile)); //using (new ProfileEventScope("FindAllIncludedFiles")) @@ -44,6 +199,9 @@ namespace Flax.Build.NativeCpp result = includedFiles.ToArray(); AllIncludesCache.Add(sourceFile, result); + if (!AllIncludesTimestampCache.ContainsKey(sourceFile)) + AllIncludesTimestampCache.Add(sourceFile, lastModified ?? File.GetLastWriteTime(sourceFile)); + /*Log.Info("File includes for " + sourceFile); foreach (var e in result) { @@ -72,10 +230,22 @@ namespace Flax.Build.NativeCpp private static string[] GetDirectIncludes(string sourceFile) { + DateTime? lastModified = null; + // Try hit the cache string[] result; if (DirectIncludesCache.TryGetValue(sourceFile, out result)) - return result; + { + if (DirectIncludesTimestampCache.TryGetValue(sourceFile, out var cachedTimestamp)) + { + lastModified = FileLastWriteTime(sourceFile); + if (lastModified == cachedTimestamp) + return result; + } + + DirectIncludesCache.Remove(sourceFile); + DirectIncludesTimestampCache.Remove(sourceFile); + } // Find all files included directly var includedFiles = new HashSet(); @@ -152,11 +322,11 @@ namespace Flax.Build.NativeCpp // Relative to the workspace root var includedFilePath = Path.Combine(Globals.Root, "Source", includedFile); - if (!File.Exists(includedFilePath)) + if (!FileExists(includedFilePath)) { // Relative to the source file includedFilePath = Path.Combine(sourceFileFolder, includedFile); - if (!File.Exists(includedFilePath)) + if (!FileExists(includedFilePath)) { // Relative to any of the included project workspaces var project = Globals.Project; @@ -164,7 +334,7 @@ namespace Flax.Build.NativeCpp foreach (var reference in project.References) { includedFilePath = Path.Combine(reference.Project.ProjectFolderPath, "Source", includedFile); - if (File.Exists(includedFilePath)) + if (FileExists(includedFilePath)) { isValid = true; break; @@ -191,6 +361,8 @@ namespace Flax.Build.NativeCpp // Process result result = includedFiles.ToArray(); DirectIncludesCache.Add(sourceFile, result); + if (!DirectIncludesTimestampCache.ContainsKey(sourceFile)) + DirectIncludesTimestampCache.Add(sourceFile, lastModified ?? FileLastWriteTime(sourceFile)); return result; } } From 163abb107c9225e7dc681de683f2b184be6c9f93 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski <118038102+Withaust@users.noreply.github.com> Date: Wed, 28 Dec 2022 16:23:54 +0300 Subject: [PATCH 42/45] GPUResource Name property glu e generation fix This fixes the issue when compiling the editor in the `Release` --- Source/Editor/Windows/Profiler/MemoryGPU.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Windows/Profiler/MemoryGPU.cs b/Source/Editor/Windows/Profiler/MemoryGPU.cs index 3d1a4f7de..ad02772df 100644 --- a/Source/Editor/Windows/Profiler/MemoryGPU.cs +++ b/Source/Editor/Windows/Profiler/MemoryGPU.cs @@ -133,7 +133,9 @@ namespace FlaxEditor.Windows.Profiler { resource = new Resource { +#if !BUILD_RELEASE Name = gpuResource.Name, +#endif Type = gpuResource.ResourceType, }; From 7859e64db119c5a2584403c19d790971c85dd279 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 28 Dec 2022 16:28:50 +0100 Subject: [PATCH 43/45] Fix crash on Clang-platforms when calling base method from overriden scripting method (C# or Visual Script) --- Source/Engine/Scripting/InternalCalls.h | 24 ++++++++ .../Bindings/BindingsGenerator.Cpp.cs | 55 ++++++++++++++----- .../Build/Plugins/VisualScriptingPlugin.cs | 14 +---- 3 files changed, 66 insertions(+), 27 deletions(-) diff --git a/Source/Engine/Scripting/InternalCalls.h b/Source/Engine/Scripting/InternalCalls.h index 8e7ef9d27..a7b7c2ebd 100644 --- a/Source/Engine/Scripting/InternalCalls.h +++ b/Source/Engine/Scripting/InternalCalls.h @@ -7,6 +7,30 @@ #include "ScriptingType.h" #include "Types.h" +#if defined(__clang__) +// Helper utility to override vtable entry with automatic restore +// See BindingsGenerator.Cpp.cs that generates virtuall method wrappers for scripting to properly call overriden base method +struct FLAXENGINE_API VTableFunctionInjector +{ + void** VTableAddr; + void* OriginalValue; + + VTableFunctionInjector(void* object, void* originalFunc, void* func) + { + void** vtable = *(void***)object; + const int32 vtableIndex = GetVTableIndex(vtable, 200, originalFunc); + VTableAddr = vtable + vtableIndex; + OriginalValue = *VTableAddr; + *VTableAddr = func; + } + + ~VTableFunctionInjector() + { + *VTableAddr = OriginalValue; + } +}; +#endif + #if USE_MONO extern "C" FLAXENGINE_API void mono_add_internal_call(const char* name, const void* method); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 1b54c9d81..624f54d39 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -237,6 +237,41 @@ namespace Flax.Build.Bindings return $"({typeInfo}){value}"; } + public static void GenerateCppVirtualWrapperCallBaseMethod(BuildData buildData, StringBuilder contents, VirtualClassInfo classInfo, FunctionInfo functionInfo, string scriptVTableBase, string scriptVTableOffset) + { + contents.AppendLine(" // Prevent stack overflow by calling native base method"); + if (buildData.Toolchain is Platforms.UnixToolchain) + { + // Clang compiler + // TODO: secure VTableFunctionInjector with mutex (even at cost of performance) + contents.AppendLine($" {functionInfo.UniqueName}_Signature funcPtr = &{classInfo.NativeName}::{functionInfo.Name};"); + contents.AppendLine($" VTableFunctionInjector vtableInjector(object, *(void**)&funcPtr, {scriptVTableBase}[{scriptVTableOffset} + 2]); // TODO: this is not thread-safe"); + if (classInfo is InterfaceInfo) + { + contents.Append($" return (({classInfo.NativeName}*)(void*)object)->{functionInfo.Name}("); + } + else + { + contents.Append(" return (object->*funcPtr)("); + } + } + else + { + // MSVC or other compiler + contents.Append($" return (this->**({functionInfo.UniqueName}_Internal_Signature*)&{scriptVTableBase}[{scriptVTableOffset} + 2])("); + } + bool separator = false; + for (var i = 0; i < functionInfo.Parameters.Count; i++) + { + var parameterInfo = functionInfo.Parameters[i]; + if (separator) + contents.Append(", "); + separator = true; + contents.Append(parameterInfo.Name); + } + contents.AppendLine(");"); + } + private static string GenerateCppGetNativeClass(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, FunctionInfo functionInfo) { // Optimal path for in-build types @@ -1198,19 +1233,7 @@ namespace Flax.Build.Bindings contents.AppendLine(" if (WrapperCallInstance == object)"); contents.AppendLine(" {"); - contents.AppendLine(" // Prevent stack overflow by calling native base method"); - contents.AppendLine(" const auto scriptVTableBase = managedTypePtr->Script.ScriptVTableBase;"); - contents.Append($" return (this->**({functionInfo.UniqueName}_Internal_Signature*)&scriptVTableBase[{scriptVTableOffset} + 2])("); - separator = false; - for (var i = 0; i < functionInfo.Parameters.Count; i++) - { - var parameterInfo = functionInfo.Parameters[i]; - if (separator) - contents.Append(", "); - separator = true; - contents.Append(parameterInfo.Name); - } - contents.AppendLine(");"); + GenerateCppVirtualWrapperCallBaseMethod(buildData, contents, classInfo, functionInfo, "managedTypePtr->Script.ScriptVTableBase", scriptVTableOffset); contents.AppendLine(" }"); contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Script.ScriptVTable;"); contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableOffset}]);"); @@ -1327,7 +1350,11 @@ namespace Flax.Build.Bindings } var t = functionInfo.IsConst ? " const" : string.Empty; contents.AppendLine($" typedef {functionInfo.ReturnType} ({classInfo.NativeName}::*{functionInfo.UniqueName}_Signature)({thunkParams}){t};"); - contents.AppendLine($" typedef {functionInfo.ReturnType} ({classInfo.NativeName}Internal::*{functionInfo.UniqueName}_Internal_Signature)({thunkParams}){t};"); + if (!(buildData.Toolchain is Platforms.UnixToolchain)) + { + // MSVC or other compiler + contents.AppendLine($" typedef {functionInfo.ReturnType} ({classInfo.NativeName}Internal::*{functionInfo.UniqueName}_Internal_Signature)({thunkParams}){t};"); + } } contents.AppendLine(""); diff --git a/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs index 603b66c9f..1fa8ed4f0 100644 --- a/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs +++ b/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs @@ -67,19 +67,7 @@ namespace Flax.Build.Plugins contents.AppendLine(" static THREADLOCAL void* WrapperCallInstance = nullptr;"); contents.AppendLine(" if (WrapperCallInstance == object)"); contents.AppendLine(" {"); - contents.AppendLine(" // Prevent stack overflow by calling base method"); - contents.AppendLine(" const auto scriptVTableBase = object->GetType().Script.ScriptVTableBase;"); - contents.Append($" return (this->**({functionInfo.UniqueName}_Internal_Signature*)&scriptVTableBase[{scriptVTableOffset} + 2])("); - separator = false; - for (var i = 0; i < functionInfo.Parameters.Count; i++) - { - var parameterInfo = functionInfo.Parameters[i]; - if (separator) - contents.Append(", "); - separator = true; - contents.Append(parameterInfo.Name); - } - contents.AppendLine(");"); + BindingsGenerator.GenerateCppVirtualWrapperCallBaseMethod(buildData, contents, classInfo, functionInfo, "object->GetType().Script.ScriptVTableBase", scriptVTableOffset); contents.AppendLine(" }"); contents.AppendLine(" auto scriptVTable = (VisualScript::Method**)object->GetType().Script.ScriptVTable;"); contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableOffset}]);"); From 3a393b6825df00936c46ee41fa8fd423f372663c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 28 Dec 2022 16:28:50 +0100 Subject: [PATCH 44/45] Fix crash on Clang-platforms when calling base method from overriden scripting method (C# or Visual Script) --- Source/Engine/Scripting/InternalCalls.h | 24 ++++++++ .../Bindings/BindingsGenerator.Cpp.cs | 55 ++++++++++++++----- .../Build/Plugins/VisualScriptingPlugin.cs | 14 +---- 3 files changed, 66 insertions(+), 27 deletions(-) diff --git a/Source/Engine/Scripting/InternalCalls.h b/Source/Engine/Scripting/InternalCalls.h index 8e7ef9d27..a7b7c2ebd 100644 --- a/Source/Engine/Scripting/InternalCalls.h +++ b/Source/Engine/Scripting/InternalCalls.h @@ -7,6 +7,30 @@ #include "ScriptingType.h" #include "Types.h" +#if defined(__clang__) +// Helper utility to override vtable entry with automatic restore +// See BindingsGenerator.Cpp.cs that generates virtuall method wrappers for scripting to properly call overriden base method +struct FLAXENGINE_API VTableFunctionInjector +{ + void** VTableAddr; + void* OriginalValue; + + VTableFunctionInjector(void* object, void* originalFunc, void* func) + { + void** vtable = *(void***)object; + const int32 vtableIndex = GetVTableIndex(vtable, 200, originalFunc); + VTableAddr = vtable + vtableIndex; + OriginalValue = *VTableAddr; + *VTableAddr = func; + } + + ~VTableFunctionInjector() + { + *VTableAddr = OriginalValue; + } +}; +#endif + #if USE_MONO extern "C" FLAXENGINE_API void mono_add_internal_call(const char* name, const void* method); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 921043e1d..e57449f40 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -236,6 +236,41 @@ namespace Flax.Build.Bindings return $"({typeInfo}){value}"; } + public static void GenerateCppVirtualWrapperCallBaseMethod(BuildData buildData, StringBuilder contents, VirtualClassInfo classInfo, FunctionInfo functionInfo, string scriptVTableBase, string scriptVTableOffset) + { + contents.AppendLine(" // Prevent stack overflow by calling native base method"); + if (buildData.Toolchain is Platforms.UnixToolchain) + { + // Clang compiler + // TODO: secure VTableFunctionInjector with mutex (even at cost of performance) + contents.AppendLine($" {functionInfo.UniqueName}_Signature funcPtr = &{classInfo.NativeName}::{functionInfo.Name};"); + contents.AppendLine($" VTableFunctionInjector vtableInjector(object, *(void**)&funcPtr, {scriptVTableBase}[{scriptVTableOffset} + 2]); // TODO: this is not thread-safe"); + if (classInfo is InterfaceInfo) + { + contents.Append($" return (({classInfo.NativeName}*)(void*)object)->{functionInfo.Name}("); + } + else + { + contents.Append(" return (object->*funcPtr)("); + } + } + else + { + // MSVC or other compiler + contents.Append($" return (this->**({functionInfo.UniqueName}_Internal_Signature*)&{scriptVTableBase}[{scriptVTableOffset} + 2])("); + } + bool separator = false; + for (var i = 0; i < functionInfo.Parameters.Count; i++) + { + var parameterInfo = functionInfo.Parameters[i]; + if (separator) + contents.Append(", "); + separator = true; + contents.Append(parameterInfo.Name); + } + contents.AppendLine(");"); + } + private static string GenerateCppGetNativeClass(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, FunctionInfo functionInfo) { // Optimal path for in-build types @@ -1190,19 +1225,7 @@ namespace Flax.Build.Bindings contents.AppendLine(" if (WrapperCallInstance == object)"); contents.AppendLine(" {"); - contents.AppendLine(" // Prevent stack overflow by calling native base method"); - contents.AppendLine(" const auto scriptVTableBase = managedTypePtr->Script.ScriptVTableBase;"); - contents.Append($" return (this->**({functionInfo.UniqueName}_Internal_Signature*)&scriptVTableBase[{scriptVTableOffset} + 2])("); - separator = false; - for (var i = 0; i < functionInfo.Parameters.Count; i++) - { - var parameterInfo = functionInfo.Parameters[i]; - if (separator) - contents.Append(", "); - separator = true; - contents.Append(parameterInfo.Name); - } - contents.AppendLine(");"); + GenerateCppVirtualWrapperCallBaseMethod(buildData, contents, classInfo, functionInfo, "managedTypePtr->Script.ScriptVTableBase", scriptVTableOffset); contents.AppendLine(" }"); contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Script.ScriptVTable;"); contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableOffset}]);"); @@ -1311,7 +1334,11 @@ namespace Flax.Build.Bindings } var t = functionInfo.IsConst ? " const" : string.Empty; contents.AppendLine($" typedef {functionInfo.ReturnType} ({classInfo.NativeName}::*{functionInfo.UniqueName}_Signature)({thunkParams}){t};"); - contents.AppendLine($" typedef {functionInfo.ReturnType} ({classInfo.NativeName}Internal::*{functionInfo.UniqueName}_Internal_Signature)({thunkParams}){t};"); + if (!(buildData.Toolchain is Platforms.UnixToolchain)) + { + // MSVC or other compiler + contents.AppendLine($" typedef {functionInfo.ReturnType} ({classInfo.NativeName}Internal::*{functionInfo.UniqueName}_Internal_Signature)({thunkParams}){t};"); + } } contents.AppendLine(""); diff --git a/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs index 603b66c9f..1fa8ed4f0 100644 --- a/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs +++ b/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs @@ -67,19 +67,7 @@ namespace Flax.Build.Plugins contents.AppendLine(" static THREADLOCAL void* WrapperCallInstance = nullptr;"); contents.AppendLine(" if (WrapperCallInstance == object)"); contents.AppendLine(" {"); - contents.AppendLine(" // Prevent stack overflow by calling base method"); - contents.AppendLine(" const auto scriptVTableBase = object->GetType().Script.ScriptVTableBase;"); - contents.Append($" return (this->**({functionInfo.UniqueName}_Internal_Signature*)&scriptVTableBase[{scriptVTableOffset} + 2])("); - separator = false; - for (var i = 0; i < functionInfo.Parameters.Count; i++) - { - var parameterInfo = functionInfo.Parameters[i]; - if (separator) - contents.Append(", "); - separator = true; - contents.Append(parameterInfo.Name); - } - contents.AppendLine(");"); + BindingsGenerator.GenerateCppVirtualWrapperCallBaseMethod(buildData, contents, classInfo, functionInfo, "object->GetType().Script.ScriptVTableBase", scriptVTableOffset); contents.AppendLine(" }"); contents.AppendLine(" auto scriptVTable = (VisualScript::Method**)object->GetType().Script.ScriptVTable;"); contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableOffset}]);"); From 16e7c429fc3bf5a5bc60bca038d954873bb8a17e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 28 Dec 2022 17:25:30 +0100 Subject: [PATCH 45/45] Minor code cleanup #873 --- Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs b/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs index a65252eab..16d690e48 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/IncludesCache.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Runtime.Serialization.Formatters.Binary; using System.Text; namespace Flax.Build.NativeCpp @@ -33,11 +32,13 @@ namespace Flax.Build.NativeCpp using (var reader = new BinaryReader(stream)) { int version = reader.ReadInt32(); + if (version != 1) + return; // DirectIncludesCache { int count = reader.ReadInt32(); - for (int i = 0; i < count; i++) + for (int i = 0; i < count; i++) { string key = reader.ReadString(); string[] values = new string[reader.ReadInt32()]; @@ -231,7 +232,7 @@ namespace Flax.Build.NativeCpp private static string[] GetDirectIncludes(string sourceFile) { DateTime? lastModified = null; - + // Try hit the cache string[] result; if (DirectIncludesCache.TryGetValue(sourceFile, out result))