From dd4bb2f7f1f3825b4e0c57e02d9e513e1e44743b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 4 Jun 2025 11:16:01 -0500 Subject: [PATCH 01/25] Dont scroll to node when play is starting or ending. --- Source/Editor/Windows/SceneTreeWindow.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 705976e6e..999be7f94 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -27,6 +27,7 @@ namespace FlaxEditor.Windows private Panel _sceneTreePanel; private bool _isUpdatingSelection; private bool _isMouseDown; + private bool _isPlayStateChanging = false; private DragAssets _dragAssets; private DragActorType _dragActorType; @@ -93,6 +94,11 @@ namespace FlaxEditor.Windows _tree.Parent = _sceneTreePanel; headerPanel.Parent = this; + Editor.PlayModeBeginning += () => _isPlayStateChanging = true; + Editor.PlayModeBegin += () => _isPlayStateChanging = false; + Editor.PlayModeEnding += () => _isPlayStateChanging = true; + Editor.PlayModeEnd += () => _isPlayStateChanging = false; + // Setup input actions InputActions.Add(options => options.TranslateMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); InputActions.Add(options => options.RotateMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); @@ -250,7 +256,7 @@ namespace FlaxEditor.Windows _tree.Select(nodes); // For single node selected scroll view so user can see it - if (nodes.Count == 1) + if (nodes.Count == 1 && !_isPlayStateChanging) { nodes[0].ExpandAllParents(true); _sceneTreePanel.ScrollViewTo(nodes[0]); From dacc0068e0e76c2c93cfa8b9bb96f0bfec04a261 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Tue, 10 Jun 2025 16:49:30 +0200 Subject: [PATCH 02/25] dont scroll to node after scripts compilation --- Source/Editor/Windows/SceneTreeWindow.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 999be7f94..80b709eb6 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -27,7 +27,7 @@ namespace FlaxEditor.Windows private Panel _sceneTreePanel; private bool _isUpdatingSelection; private bool _isMouseDown; - private bool _isPlayStateChanging = false; + private bool _blockSceneTreeScroll = false; private DragAssets _dragAssets; private DragActorType _dragActorType; @@ -94,10 +94,10 @@ namespace FlaxEditor.Windows _tree.Parent = _sceneTreePanel; headerPanel.Parent = this; - Editor.PlayModeBeginning += () => _isPlayStateChanging = true; - Editor.PlayModeBegin += () => _isPlayStateChanging = false; - Editor.PlayModeEnding += () => _isPlayStateChanging = true; - Editor.PlayModeEnd += () => _isPlayStateChanging = false; + Editor.PlayModeBeginning += () => _blockSceneTreeScroll = true; + Editor.PlayModeBegin += () => _blockSceneTreeScroll = false; + Editor.PlayModeEnding += () => _blockSceneTreeScroll = true; + Editor.PlayModeEnd += () => _blockSceneTreeScroll = false; // Setup input actions InputActions.Add(options => options.TranslateMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); @@ -256,7 +256,7 @@ namespace FlaxEditor.Windows _tree.Select(nodes); // For single node selected scroll view so user can see it - if (nodes.Count == 1 && !_isPlayStateChanging) + if (nodes.Count == 1 && !_blockSceneTreeScroll) { nodes[0].ExpandAllParents(true); _sceneTreePanel.ScrollViewTo(nodes[0]); @@ -266,6 +266,12 @@ namespace FlaxEditor.Windows _isUpdatingSelection = false; } + /// + public override void OnEditorStateChanged() + { + _blockSceneTreeScroll = Editor.StateMachine.ReloadingScriptsState.IsActive; + } + private bool ValidateDragAsset(AssetItem assetItem) { if (assetItem.IsOfType()) From bed1f6e9cc665bc0d876d47dd388bfb915edff7d Mon Sep 17 00:00:00 2001 From: Zode Date: Wed, 11 Jun 2025 19:06:23 +0300 Subject: [PATCH 03/25] Make tag editor expand all if holding shift while clicking the ... button --- Source/Editor/CustomEditors/Editors/TagEditor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index 79f700a2b..60639a8cb 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -604,6 +604,9 @@ namespace FlaxEditor.CustomEditors.Editors root.SortChildrenRecursive(); root.Expand(true); + if (Input.GetKey(KeyboardKeys.Shift)) + root.ExpandAll(true); + return menu; } } From 47e1547d292f210986dd5de06477244f59ec7d37 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Thu, 12 Jun 2025 18:12:06 +0200 Subject: [PATCH 04/25] improve behaviour of visject node cm menu --- Source/Editor/Surface/SurfaceNode.cs | 8 ++++++++ .../Surface/VisjectSurface.ContextMenu.cs | 18 +++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 8a42a7a92..1fe9ea44b 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -40,6 +40,11 @@ namespace FlaxEditor.Surface [HideInEditor] public class SurfaceNode : SurfaceControl { + /// + /// The box to draw a highlight around. Drawing will be skipped if null. + /// + internal Box highlightBox; + /// /// Flag used to discard node values setting during event sending for node UI flushing. /// @@ -1101,6 +1106,9 @@ namespace FlaxEditor.Surface Render2D.DrawSprite(icon, new Rectangle(-7, -7, 16, 16), new Color(0.9f, 0.9f, 0.9f)); Render2D.DrawSprite(icon, new Rectangle(-6, -6, 14, 14), new Color(0.894117647f, 0.0784313725f, 0.0f)); } + + if (highlightBox != null) + Render2D.DrawRectangle(highlightBox.Bounds, style.BorderHighlighted, 2f); } /// diff --git a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs index ee7dd33e5..23e52ca7d 100644 --- a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs +++ b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs @@ -402,7 +402,7 @@ namespace FlaxEditor.Surface _cmFormatNodesConnectionButton = menu.AddButton("Format node(s)", () => { FormatGraph(SelectedNodes); }); _cmFormatNodesConnectionButton.Enabled = CanEdit && HasNodesSelection; - _cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections to that node(s)", () => + _cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections", () => { var nodes = ((List)menu.Tag); @@ -428,8 +428,10 @@ namespace FlaxEditor.Surface MarkAsEdited(); }); - _cmRemoveNodeConnectionsButton.Enabled = CanEdit; - _cmRemoveBoxConnectionsButton = menu.AddButton("Remove all connections to that box", () => + bool anyConnection = SelectedNodes.Any(n => n.GetBoxes().Any(b => b.HasAnyConnection)); + _cmRemoveNodeConnectionsButton.Enabled = CanEdit && anyConnection; + + _cmRemoveBoxConnectionsButton = menu.AddButton("Remove all socket connections", () => { var boxUnderMouse = (Box)_cmRemoveBoxConnectionsButton.Tag; if (Undo != null) @@ -450,6 +452,16 @@ namespace FlaxEditor.Surface var boxUnderMouse = GetChildAtRecursive(location) as Box; _cmRemoveBoxConnectionsButton.Enabled = boxUnderMouse != null && boxUnderMouse.HasAnyConnection; _cmRemoveBoxConnectionsButton.Tag = boxUnderMouse; + + if (boxUnderMouse != null) + { + boxUnderMouse.ParentNode.highlightBox = boxUnderMouse; + menu.VisibleChanged += (c) => + { + if (!c.Visible) + boxUnderMouse.ParentNode.highlightBox = null; + }; + } } controlUnderMouse?.OnShowSecondaryContextMenu(menu, controlUnderMouse.PointFromParent(location)); From cd7233d559dded4da4ec88e5d6f498e18de22ff8 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 16 Jun 2025 14:10:44 +0200 Subject: [PATCH 05/25] allow the user to unfold folded categories when scrolling with keyboard --- Source/Editor/GUI/ItemsListContextMenu.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index 58f8c2eb4..0f138fb80 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -227,9 +227,8 @@ namespace FlaxEditor.GUI { int order = -1 * SortScore.CompareTo(otherItem.SortScore); if (order == 0) - { order = string.Compare(Name, otherItem.Name, StringComparison.Ordinal); - } + return order; } return base.Compare(other); @@ -535,6 +534,12 @@ namespace FlaxEditor.GUI return result; } + private void ExpandToItem(Item item) + { + if (item.Parent is DropPanel dropPanel) + dropPanel.Open(false); + } + /// protected override void OnShow() { @@ -564,7 +569,7 @@ namespace FlaxEditor.GUI Hide(); return true; case KeyboardKeys.Backspace: - // Alow the user to quickly focus the searchbar + // Allow the user to quickly focus the searchbar if (_searchBox != null && !_searchBox.IsFocused) { _searchBox.Focus(); @@ -590,6 +595,11 @@ namespace FlaxEditor.GUI // Focus the next item nextItem.Focus(); + + // Allow the user to expand groups while scrolling + if (Root.GetKey(KeyboardKeys.Control)) + ExpandToItem(nextItem); + _scrollPanel.ScrollViewTo(nextItem); return true; case KeyboardKeys.Return: From d110237423949a04cb8da04de342c9dac16873f9 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 16 Jun 2025 14:51:29 +0200 Subject: [PATCH 06/25] fix GetVisibleItems() to only get visible items --- Source/Editor/GUI/ItemsListContextMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index 0f138fb80..e2a06342d 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -522,7 +522,7 @@ namespace FlaxEditor.GUI for (int i = 0; i < _categoryPanels.Count; i++) { var category = _categoryPanels[i]; - if (!category.Visible) + if (!category.Visible || (category is DropPanel panel && panel.IsClosed)) continue; for (int j = 0; j < category.Children.Count; j++) { From 8164ce924fd5867e3f236736b0baff3f23dbf482 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 16 Jun 2025 15:30:51 +0200 Subject: [PATCH 07/25] fix unfolding to actually work --- Source/Editor/GUI/ItemsListContextMenu.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index e2a06342d..6cb5a84fb 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -508,7 +508,7 @@ namespace FlaxEditor.GUI OnSearchFilterChanged(); } - private List GetVisibleItems() + private List GetVisibleItems(bool ignoreFoldedCategories) { var result = new List(); var items = ItemsPanel.Children; @@ -522,7 +522,7 @@ namespace FlaxEditor.GUI for (int i = 0; i < _categoryPanels.Count; i++) { var category = _categoryPanels[i]; - if (!category.Visible || (category is DropPanel panel && panel.IsClosed)) + if (!category.Visible || (ignoreFoldedCategories && category is DropPanel panel && panel.IsClosed)) continue; for (int j = 0; j < category.Children.Count; j++) { @@ -587,8 +587,14 @@ namespace FlaxEditor.GUI } // Get the next item - var items = GetVisibleItems(); + bool controlDown = Root.GetKey(KeyboardKeys.Control); + var items = GetVisibleItems(!controlDown); var focusedIndex = items.IndexOf(focusedItem); + + // If the user hasn't selected anything yet and is holding control, focus first folded item + if (focusedIndex == -1 && controlDown) + focusedIndex = GetVisibleItems(true).Count - 1; + int delta = key == KeyboardKeys.ArrowDown ? -1 : 1; int nextIndex = Mathf.Wrap(focusedIndex - delta, 0, items.Count - 1); var nextItem = items[nextIndex]; @@ -597,7 +603,7 @@ namespace FlaxEditor.GUI nextItem.Focus(); // Allow the user to expand groups while scrolling - if (Root.GetKey(KeyboardKeys.Control)) + if (controlDown) ExpandToItem(nextItem); _scrollPanel.ScrollViewTo(nextItem); @@ -611,7 +617,7 @@ namespace FlaxEditor.GUI else { // Select first item if no item is focused (most likely to be the best result), saves the user from pressing arrow down first - var visibleItems = GetVisibleItems(); + var visibleItems = GetVisibleItems(true); if (visibleItems.Count > 0) { OnClickItem(visibleItems[0]); From bbe54969b0eede35e7405eeff1a91495c3d2931f Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 16 Jun 2025 16:51:30 +0200 Subject: [PATCH 08/25] fix close visject surface was_ flags to properly reset to false --- Source/Editor/Surface/VisjectSurface.Draw.cs | 4 ++++ Source/Editor/Surface/VisjectSurface.Input.cs | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index 01277d0d2..f60c19d21 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -225,6 +225,10 @@ namespace FlaxEditor.Surface _rootControl.DrawComments(); + // Reset input flags here because this is the closest to Update we have + WasBoxSelecting = IsBoxSelecting; + WasMovingSelection = IsMovingSelection; + if (IsBoxSelecting) { DrawSelection(); diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 63dfa063d..09df195eb 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -590,9 +590,6 @@ namespace FlaxEditor.Surface // Cache flags and state if (_leftMouseDown && button == MouseButton.Left) { - WasBoxSelecting = IsBoxSelecting; - WasMovingSelection = _isMovingSelection; - _leftMouseDown = false; EndMouseCapture(); Cursor = CursorType.Default; From 3cb74d48ca4ac57a27f830715952949c69394c40 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 19 Jun 2025 20:01:27 -0500 Subject: [PATCH 09/25] Convert using event to using window method overrides. --- Source/Editor/Windows/SceneTreeWindow.cs | 33 ++++++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 851cd62f3..75a3723cf 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -104,11 +104,6 @@ namespace FlaxEditor.Windows headerPanel.Parent = this; - Editor.PlayModeBeginning += () => _blockSceneTreeScroll = true; - Editor.PlayModeBegin += () => _blockSceneTreeScroll = false; - Editor.PlayModeEnding += () => _blockSceneTreeScroll = true; - Editor.PlayModeEnd += () => _blockSceneTreeScroll = false; - // Setup input actions InputActions.Add(options => options.TranslateMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); InputActions.Add(options => options.RotateMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); @@ -117,6 +112,34 @@ namespace FlaxEditor.Windows InputActions.Add(options => options.LockFocusSelection, () => Editor.Windows.EditWin.Viewport.LockFocusSelection()); InputActions.Add(options => options.Rename, RenameSelection); } + + /// + public override void OnPlayBeginning() + { + base.OnPlayBeginning(); + _blockSceneTreeScroll = true; + } + + /// + public override void OnPlayBegin() + { + base.OnPlayBegin(); + _blockSceneTreeScroll = false; + } + + /// + public override void OnPlayEnding() + { + base.OnPlayEnding(); + _blockSceneTreeScroll = true; + } + + /// + public override void OnPlayEnd() + { + base.OnPlayEnd(); + _blockSceneTreeScroll = true; + } /// /// Enables or disables vertical and horizontal scrolling on the scene tree panel. From 9e4c1da0328c98e90792d56265f709e974e0ff51 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 20 Jun 2025 19:42:30 -0500 Subject: [PATCH 10/25] Clear entries and pending entries before play for debug log. --- Source/Editor/Windows/DebugLogWindow.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index b5d71f6a0..36a7ffa82 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -735,11 +735,15 @@ namespace FlaxEditor.Windows } /// - public override void OnPlayBegin() + public override void OnPlayBeginning() { // Clear on Play - if (_clearOnPlayButton.Checked) + if (Editor.Options.Options.Interface.DebugLogClearOnPlay) { + lock (_locker) + { + _pendingEntries?.Clear(); + } Clear(); } } From 169db79b2a9358c96114312b5e14c8d84410336d Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 20 Jun 2025 20:12:43 -0500 Subject: [PATCH 11/25] Fix issue with tooltip not updating on scene reload. --- Source/Editor/States/ChangingScenesState.cs | 36 +++++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/Source/Editor/States/ChangingScenesState.cs b/Source/Editor/States/ChangingScenesState.cs index a146b7fa9..72259e832 100644 --- a/Source/Editor/States/ChangingScenesState.cs +++ b/Source/Editor/States/ChangingScenesState.cs @@ -19,6 +19,7 @@ namespace FlaxEditor.States private readonly List _scenesToLoad = new List(); private readonly List _scenesToUnload = new List(); private Guid _lastSceneFromRequest; + private bool _sameSceneReload = false; internal ChangingScenesState(Editor editor) : base(editor) @@ -164,10 +165,22 @@ namespace FlaxEditor.States { Assert.AreEqual(Guid.Empty, _lastSceneFromRequest, "Invalid state."); - // Bind events - Level.SceneLoaded += OnSceneEvent; - Level.SceneLoadError += OnSceneEvent; - Level.SceneUnloaded += OnSceneEvent; + // Bind events, only bind loading event and error if re-loading the same scene to avoid issues. + if (_scenesToUnload.Count == 1 && _scenesToLoad.Count == 1) + { + if (_scenesToLoad[0] == _scenesToUnload[0].ID) + { + Level.SceneLoaded += OnSceneEvent; + Level.SceneLoadError += OnSceneEvent; + _sameSceneReload = true; + } + } + if (!_sameSceneReload) + { + Level.SceneLoaded += OnSceneEvent; + Level.SceneLoadError += OnSceneEvent; + Level.SceneUnloaded += OnSceneEvent; + } // Push scenes changing requests for (int i = 0; i < _scenesToUnload.Count; i++) @@ -210,9 +223,18 @@ namespace FlaxEditor.States } // Unbind events - Level.SceneLoaded -= OnSceneEvent; - Level.SceneLoadError -= OnSceneEvent; - Level.SceneUnloaded -= OnSceneEvent; + if (_sameSceneReload) + { + Level.SceneLoaded -= OnSceneEvent; + Level.SceneLoadError -= OnSceneEvent; + _sameSceneReload = false; + } + else + { + Level.SceneLoaded -= OnSceneEvent; + Level.SceneLoadError -= OnSceneEvent; + Level.SceneUnloaded -= OnSceneEvent; + } } private void OnSceneEvent(Scene scene, Guid sceneId) From 6c1e380a3e005cb5fd7aec2b3d80e05434673c27 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 23 Jun 2025 17:24:53 +0200 Subject: [PATCH 12/25] fix search item hover auto focusing item Change what happens when search items get focused to prevent focus being taken away from search box. Also adds a highlight to mouse hovered search item. --- Source/Editor/Windows/Search/SearchItem.cs | 30 +++++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Windows/Search/SearchItem.cs b/Source/Editor/Windows/Search/SearchItem.cs index 11bc79991..5336c2855 100644 --- a/Source/Editor/Windows/Search/SearchItem.cs +++ b/Source/Editor/Windows/Search/SearchItem.cs @@ -74,6 +74,20 @@ namespace FlaxEditor.Windows.Search typeLabel.TextColor = Style.Current.ForegroundGrey; } + /// + public override bool OnMouseDown(Float2 location, MouseButton button) + { + // Select and focus the item on right click to prevent the search from being cleared + if (button == MouseButton.Right) + { + _finder.SelectedItem = this; + _finder.Hand = true; + Focus(); + return true; + } + return base.OnMouseUp(location, button); + } + /// public override bool OnMouseUp(Float2 location, MouseButton button) { @@ -86,6 +100,15 @@ namespace FlaxEditor.Windows.Search return base.OnMouseUp(location, button); } + /// + public override void Draw() + { + if (IsMouseOver) + Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), Style.Current.BackgroundHighlighted); + + base.Draw(); + } + /// public override void OnMouseEnter(Float2 location) { @@ -93,12 +116,7 @@ namespace FlaxEditor.Windows.Search var root = RootWindow; if (root != null) - { root.Cursor = CursorType.Hand; - } - - _finder.SelectedItem = this; - _finder.Hand = true; } /// @@ -176,9 +194,7 @@ namespace FlaxEditor.Windows.Search { string importLocation = System.IO.Path.GetDirectoryName(importPath); if (!string.IsNullOrEmpty(importLocation) && System.IO.Directory.Exists(importLocation)) - { cm.AddButton("Show import location", () => FileSystem.ShowFileExplorer(importLocation)); - } } } cm.AddSeparator(); From 109d4423bb66c60ebe5e71fe04d39e26cf120e68 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 23 Jun 2025 20:14:18 +0200 Subject: [PATCH 13/25] add accent color strip to search item icons --- Source/Editor/Windows/Search/SearchItem.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/Search/SearchItem.cs b/Source/Editor/Windows/Search/SearchItem.cs index 5336c2855..4d610159e 100644 --- a/Source/Editor/Windows/Search/SearchItem.cs +++ b/Source/Editor/Windows/Search/SearchItem.cs @@ -20,6 +20,11 @@ namespace FlaxEditor.Windows.Search /// protected Image _icon; + /// + /// The color of the accent strip. + /// + protected Color _accentColor; + /// /// The item name. /// @@ -56,7 +61,7 @@ namespace FlaxEditor.Windows.Search var icon = new Image { Size = new Float2(logoSize), - Location = new Float2(5, (height - logoSize) / 2) + Location = new Float2(7, (height - logoSize) / 2) }; _icon = icon; @@ -146,6 +151,7 @@ namespace FlaxEditor.Windows.Search { _asset = item; _asset.AddReference(this); + _accentColor = Editor.Instance.ContentDatabase.GetProxy(item).AccentColor; } /// @@ -228,6 +234,10 @@ namespace FlaxEditor.Windows.Search // Draw icon var iconRect = _icon.Bounds; _asset.DrawThumbnail(ref iconRect); + + // Draw icon color strip + var rect = iconRect with { Width = 2, Height = Height, Location = Float2.Zero }; + Render2D.FillRectangle(rect, _accentColor); } /// From bf10d0949e4aa937eb518570bc6ed36557173f8b Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Thu, 26 Jun 2025 12:47:31 +0200 Subject: [PATCH 14/25] add a bit of spacing to the left edge of the popup to reduce colors bleeding into the background --- Source/Editor/Windows/Search/SearchItem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/Search/SearchItem.cs b/Source/Editor/Windows/Search/SearchItem.cs index 4d610159e..63263cc58 100644 --- a/Source/Editor/Windows/Search/SearchItem.cs +++ b/Source/Editor/Windows/Search/SearchItem.cs @@ -235,8 +235,8 @@ namespace FlaxEditor.Windows.Search var iconRect = _icon.Bounds; _asset.DrawThumbnail(ref iconRect); - // Draw icon color strip - var rect = iconRect with { Width = 2, Height = Height, Location = Float2.Zero }; + // Draw color strip + var rect = iconRect with { Width = 2, Height = Height, Location = new Float2(2, 0) }; Render2D.FillRectangle(rect, _accentColor); } From 4e4d380267de895fb1798d03afd251f71e8dfb7c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 29 Jun 2025 20:02:24 +0200 Subject: [PATCH 15/25] Fix fog to draw Fog Cutoff Distance via a plane, not sphere test Add support for negative Fog Cutoff Distance on fog to draw it in front of the camera Far Plane, no matter the setup. Fix hot-reloading Fog shader in Editor. --- .../Features/ForwardShading.hlsl | 2 +- Content/Editor/Particles/Smoke Material.flax | 4 +- Content/Shaders/Fog.flax | 4 +- .../Level/Actors/ExponentialHeightFog.cpp | 8 +-- .../Level/Actors/ExponentialHeightFog.h | 4 +- Source/Shaders/ExponentialHeightFog.hlsl | 9 ++- Source/Shaders/Fog.shader | 55 +++++++------------ 7 files changed, 39 insertions(+), 47 deletions(-) diff --git a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl index 2db55111b..08bb82698 100644 --- a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl +++ b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl @@ -145,7 +145,7 @@ void PS_Forward( #if USE_FOG // Calculate exponential height fog - float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0); + float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0, gBuffer.ViewPos.z); // Apply fog to the output color #if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE diff --git a/Content/Editor/Particles/Smoke Material.flax b/Content/Editor/Particles/Smoke Material.flax index bd8c1c0e9..d5b8cb872 100644 --- a/Content/Editor/Particles/Smoke Material.flax +++ b/Content/Editor/Particles/Smoke Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6097a8ca31dbe7a985b5c512d2049d2d22c73175551965c75d6b360323505491 -size 38427 +oid sha256:a16a3fa5bed3bc8030c40fbe0e946f2bdec28745542bf08db1d7b4a43180f785 +size 38900 diff --git a/Content/Shaders/Fog.flax b/Content/Shaders/Fog.flax index 75590f84d..3f934412c 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:7735a770a87483d4df5e4e653373067c26469de8088f071ca092ed3e797bf461 -size 2785 +oid sha256:e83f9dbbcf84550de09e7c63bbdd3acc6591cf6ba1bcce2a2699772122ae07f4 +size 2633 diff --git a/Source/Engine/Level/Actors/ExponentialHeightFog.cpp b/Source/Engine/Level/Actors/ExponentialHeightFog.cpp index d62aecac4..efb5351e7 100644 --- a/Source/Engine/Level/Actors/ExponentialHeightFog.cpp +++ b/Source/Engine/Level/Actors/ExponentialHeightFog.cpp @@ -41,11 +41,10 @@ void ExponentialHeightFog::Draw(RenderContext& renderContext) && _shader->IsLoaded() && renderContext.View.IsPerspectiveProjection()) { - // Prepare if (_psFog.States[0] == nullptr) - { - // Create pipeline states _psFog.CreatePipelineStates(); + if (!_psFog.States[0]->IsValid()) + { GPUPipelineState::Description psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle; psDesc.DepthWriteEnable = false; psDesc.BlendMode.BlendEnable = true; @@ -59,6 +58,7 @@ void ExponentialHeightFog::Draw(RenderContext& renderContext) if (_psFog.Create(psDesc, _shader->GetShader(), "PS_Fog")) { LOG(Warning, "Cannot create graphics pipeline state object for '{0}'.", ToString()); + return; } } @@ -160,7 +160,7 @@ void ExponentialHeightFog::GetExponentialHeightFogData(const RenderView& view, S result.FogAtViewPosition = density * Math::Pow(2.0f, Math::Clamp(-heightFalloff * (viewHeight - height), -125.f, 126.f)); result.StartDistance = StartDistance; result.FogMinOpacity = 1.0f - FogMaxOpacity; - result.FogCutoffDistance = FogCutoffDistance; + result.FogCutoffDistance = FogCutoffDistance >= 0 ? FogCutoffDistance : view.Far + FogCutoffDistance; if (useDirectionalLightInscattering) { result.InscatteringLightDirection = -DirectionalInscatteringLight->GetDirection(); diff --git a/Source/Engine/Level/Actors/ExponentialHeightFog.h b/Source/Engine/Level/Actors/ExponentialHeightFog.h index 0b442ba9f..c0e5407d2 100644 --- a/Source/Engine/Level/Actors/ExponentialHeightFog.h +++ b/Source/Engine/Level/Actors/ExponentialHeightFog.h @@ -55,9 +55,9 @@ public: float StartDistance = 0.0f; /// - /// Scene elements past this distance will not have fog applied. This is useful for excluding skyboxes which already have fog baked in. Setting this value to 0 disables it. + /// Scene elements past this distance will not have fog applied. This is useful for excluding skyboxes which already have fog baked in. Setting this value to 0 disables it. Negative value sets the cutoff distance relative to the far plane of the camera. /// - API_FIELD(Attributes="EditorOrder(60), DefaultValue(0.0f), Limit(0), EditorDisplay(\"Exponential Height Fog\")") + API_FIELD(Attributes="EditorOrder(60), DefaultValue(0.0f), EditorDisplay(\"Exponential Height Fog\")") float FogCutoffDistance = 0.0f; public: diff --git a/Source/Shaders/ExponentialHeightFog.hlsl b/Source/Shaders/ExponentialHeightFog.hlsl index 2e34936eb..f6fb918f5 100644 --- a/Source/Shaders/ExponentialHeightFog.hlsl +++ b/Source/Shaders/ExponentialHeightFog.hlsl @@ -29,7 +29,7 @@ struct ExponentialHeightFogData float StartDistance; }; -float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, float3 posWS, float3 camWS, float skipDistance) +float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, float3 posWS, float3 camWS, float skipDistance, float sceneDistance) { float3 cameraToPos = posWS - camWS; float cameraToPosSqr = dot(cameraToPos, cameraToPos); @@ -78,7 +78,7 @@ float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, fl // Disable fog after a certain distance FLATTEN - if (exponentialHeightFog.FogCutoffDistance > 0 && cameraToPosLen > exponentialHeightFog.FogCutoffDistance) + if (exponentialHeightFog.FogCutoffDistance > 0 && sceneDistance > exponentialHeightFog.FogCutoffDistance) { expFogFactor = 1; directionalInscattering = 0; @@ -87,4 +87,9 @@ float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, fl return float4(inscatteringColor * (1.0f - expFogFactor) + directionalInscattering, expFogFactor); } +float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, float3 posWS, float3 camWS, float skipDistance) +{ + return GetExponentialHeightFog(exponentialHeightFog, posWS, camWS, skipDistance, distance(posWS, camWS)); +} + #endif diff --git a/Source/Shaders/Fog.shader b/Source/Shaders/Fog.shader index 7dcc679ec..dfea921cc 100644 --- a/Source/Shaders/Fog.shader +++ b/Source/Shaders/Fog.shader @@ -24,41 +24,17 @@ Texture2D Depth : register(t0); Texture3D IntegratedLightScattering : register(t1); #endif -// Get world space position at given pixel coordinate -float3 GetWorldPos(float2 uv) -{ - float depth = SAMPLE_RT(Depth, uv).r; - GBufferData gBufferData = GetGBufferData(); - float3 viewPos = GetViewPos(gBufferData, uv, depth); - return mul(float4(viewPos, 1), gBufferData.InvViewMatrix).xyz; -} - -float4 CalculateCombinedFog(float3 posWS, float sceneDepth, float3 volumeUV) -{ - float skipDistance = 0; - -#if VOLUMETRIC_FOG - skipDistance = max(ExponentialHeightFog.VolumetricFogMaxDistance - 100, 0); -#endif - - float4 fog = GetExponentialHeightFog(ExponentialHeightFog, posWS, GBuffer.ViewPos, skipDistance); - -#if VOLUMETRIC_FOG - float4 volumetricFog = IntegratedLightScattering.SampleLevel(SamplerLinearClamp, volumeUV, 0); - fog = float4(volumetricFog.rgb + fog.rgb * volumetricFog.a, volumetricFog.a * fog.a); -#endif - - return fog; -} - META_PS(true, FEATURE_LEVEL_ES2) META_PERMUTATION_1(VOLUMETRIC_FOG=0) META_PERMUTATION_1(VOLUMETRIC_FOG=1) float4 PS_Fog(Quad_VS2PS input) : SV_Target0 { - // Calculate pixel world space position - float3 posWS = GetWorldPos(input.TexCoord); - float3 viewVector = posWS - GBuffer.ViewPos; + // Get world space position at given pixel coordinate + float rawDepth = SAMPLE_RT(Depth, input.TexCoord).r; + GBufferData gBufferData = GetGBufferData(); + float3 viewPos = GetViewPos(gBufferData, input.TexCoord, rawDepth); + float3 worldPos = mul(float4(viewPos, 1), gBufferData.InvViewMatrix).xyz; + float3 viewVector = worldPos - GBuffer.ViewPos; float sceneDepth = length(viewVector); // Calculate volumetric fog coordinates @@ -67,17 +43,28 @@ float4 PS_Fog(Quad_VS2PS input) : SV_Target0 // Debug code #if VOLUMETRIC_FOG && 0 - volumeUV = posWS / 1000; + volumeUV = worldPos / 1000; if (!all(volumeUV >= 0 && volumeUV <= 1)) return 0; return float4(IntegratedLightScattering.SampleLevel(SamplerLinearClamp, volumeUV, 0).rgb, 1); //return float4(volumeUV, 1); - //return float4(posWS / 100, 1); + //return float4(worldPos / 100, 1); #endif - // Calculate fog color - float4 fog = CalculateCombinedFog(posWS, sceneDepth, volumeUV); + float skipDistance = 0; +#if VOLUMETRIC_FOG + skipDistance = max(ExponentialHeightFog.VolumetricFogMaxDistance - 100, 0); +#endif + + // Calculate exponential fog color + float4 fog = GetExponentialHeightFog(ExponentialHeightFog, worldPos, GBuffer.ViewPos, skipDistance, viewPos.z); + +#if VOLUMETRIC_FOG + // Sample volumetric fog and mix it in + float4 volumetricFog = IntegratedLightScattering.SampleLevel(SamplerLinearClamp, volumeUV, 0); + fog = float4(volumetricFog.rgb + fog.rgb * volumetricFog.a, volumetricFog.a * fog.a); +#endif return fog; } From 12c8bb4f31f79d2a94ce9187785b09cdffe21e5d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jul 2025 08:27:29 +0200 Subject: [PATCH 16/25] Fix scene lightmaps removal clearing actors linkage to lightmaps --- Source/Engine/Foliage/Foliage.cpp | 6 ++++++ Source/Engine/Foliage/Foliage.h | 5 +++++ Source/Engine/Level/Scene/Scene.cpp | 15 +++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index a1cd046ae..c0e0b0b0e 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -993,6 +993,12 @@ void Foliage::RemoveAllInstances() RebuildClusters(); } +void Foliage::RemoveLightmap() +{ + for (auto& e : Instances) + e.RemoveLightmap(); +} + static float GlobalDensityScale = 1.0f; float Foliage::GetGlobalDensityScale() diff --git a/Source/Engine/Foliage/Foliage.h b/Source/Engine/Foliage/Foliage.h index f9055f377..1855e9914 100644 --- a/Source/Engine/Foliage/Foliage.h +++ b/Source/Engine/Foliage/Foliage.h @@ -139,6 +139,11 @@ public: /// API_FUNCTION() void RemoveAllInstances(); + /// + /// Removes the lightmap data from the foliage instances. + /// + API_FUNCTION() void RemoveLightmap(); + public: /// /// Gets the global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices. diff --git a/Source/Engine/Level/Scene/Scene.cpp b/Source/Engine/Level/Scene/Scene.cpp index 215153122..f60aa8d2f 100644 --- a/Source/Engine/Level/Scene/Scene.cpp +++ b/Source/Engine/Level/Scene/Scene.cpp @@ -7,6 +7,7 @@ #include "Engine/Content/Content.h" #include "Engine/Content/Deprecated.h" #include "Engine/Content/Factories/JsonAssetFactory.h" +#include "Engine/Foliage/Foliage.h" #include "Engine/Physics/Colliders/MeshCollider.h" #include "Engine/Level/Actors/StaticModel.h" #include "Engine/Level/ActorsCache.h" @@ -15,6 +16,7 @@ #include "Engine/Navigation/NavMesh.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Serialization/Serialization.h" +#include "Engine/Terrain/Terrain.h" #if USE_EDITOR #include "Engine/Engine/Globals.h" #endif @@ -98,6 +100,19 @@ void Scene::SetLightmapSettings(const LightmapSettings& value) void Scene::ClearLightmaps() { LightmapsData.ClearLightmaps(); + + // Unlink any actors from lightmap + Function function = [this](Actor* actor) + { + if (auto* staticModel = Cast(actor)) + staticModel->RemoveLightmap(); + else if (auto* terrain = Cast(actor)) + terrain->RemoveLightmap(); + else if (auto* foliage = Cast(actor)) + foliage->RemoveLightmap(); + return true; + }; + TreeExecute(function); } void Scene::BuildCSG(float timeoutMs) From dd256ad51828b895192d9291b4f5108690d370c7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jul 2025 10:37:39 +0200 Subject: [PATCH 17/25] Fix typo --- Flax.sln.DotSettings | 1 + Source/Engine/Core/Config/GraphicsSettings.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Flax.sln.DotSettings b/Flax.sln.DotSettings index 86647d380..3611934dd 100644 --- a/Flax.sln.DotSettings +++ b/Flax.sln.DotSettings @@ -267,6 +267,7 @@ True True True + True True True True diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h index 831605596..d8b35533a 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.h +++ b/Source/Engine/Core/Config/GraphicsSettings.h @@ -84,7 +84,7 @@ public: bool EnableGlobalSDF = false; /// - /// Draw distance of the Global SDF. Actual value can be large when using DDGI. + /// Draw distance of the Global SDF. Actual value can be larger when using DDGI. /// API_FIELD(Attributes="EditorOrder(2001), EditorDisplay(\"Global SDF\"), Limit(1000), ValueCategory(Utils.ValueCategory.Distance)") float GlobalSDFDistance = 15000.0f; From 78f0ff75f29348b8df21f071b286314f977d8394 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jul 2025 10:52:04 +0200 Subject: [PATCH 18/25] Move debug log clearing to handle pending entries too in publci api --- Source/Editor/Windows/DebugLogWindow.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index 36a7ffa82..ed133d4e8 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -447,6 +447,10 @@ namespace FlaxEditor.Windows /// public void Clear() { + lock (_locker) + { + _pendingEntries.Clear(); + } if (_entriesPanel == null) return; RemoveEntries(); @@ -740,10 +744,6 @@ namespace FlaxEditor.Windows // Clear on Play if (Editor.Options.Options.Interface.DebugLogClearOnPlay) { - lock (_locker) - { - _pendingEntries?.Clear(); - } Clear(); } } From 7f783bb7dae1e6f1be7c014cad0486c9e11a3114 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 2 Jul 2025 17:23:04 +0200 Subject: [PATCH 19/25] Fix crash when importing mesh with incorrect triangulation --- Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index 2cc2823af..81ce46d6c 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -692,8 +692,12 @@ int Triangulate(OpenFbxImporterData& data, const ofbx::GeometryData& geom, const triangulatedIndices[i] = polygon.from_vertex + (earIndices[i] % polygon.vertex_count); // Ensure that we've written enough ears - ASSERT(earIndices.Count() == 3 * (polygon.vertex_count - 2)); - return earIndices.Count(); + const int32 indexCount = 3 * (polygon.vertex_count - 2); + if (indexCount != earIndices.Count()) + { + LOG(Error, "Invalid amount of vertices after triangulation. Expected {} but got {}. Use proper mesh triangulation when exporting source asset to the engine.", earIndices.Count(), indexCount); + } + return indexCount; } bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* aMesh, MeshData& mesh, String& errorMsg, int partitionIndex) @@ -729,7 +733,7 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* mesh.Positions.Get()[j] = ToFloat3(positions.get(triangulatedIndices[j])); numIndicesTotal += numIndices; } - ASSERT(numIndicesTotal == vertexCount); + ASSERT_LOW_LAYER(numIndicesTotal == vertexCount); } // Indices (dummy index buffer) From 056fea9a8c0c02e8a7ef1368f38e5e1d72f9d71f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 3 Jul 2025 14:48:38 +0200 Subject: [PATCH 20/25] Fix forward shading compilation when using fog inside unlit material --- Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl index 08bb82698..22d9ca9b4 100644 --- a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl +++ b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl @@ -143,7 +143,7 @@ void PS_Forward( #endif -#if USE_FOG +#if USE_FOG && MATERIAL_SHADING_MODEL != SHADING_MODEL_UNLIT // Calculate exponential height fog float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0, gBuffer.ViewPos.z); From da08be42b4d73307eb4a25fc3fb3fecf282636aa Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 3 Jul 2025 20:32:12 +0200 Subject: [PATCH 21/25] Fix deadlock in Debug builds on object dtor --- Source/Engine/Core/ObjectsRemovalService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/ObjectsRemovalService.cpp b/Source/Engine/Core/ObjectsRemovalService.cpp index 4d9159ea9..052bd6040 100644 --- a/Source/Engine/Core/ObjectsRemovalService.cpp +++ b/Source/Engine/Core/ObjectsRemovalService.cpp @@ -154,7 +154,7 @@ void ObjectsRemoval::Dispose() Object::~Object() { -#if BUILD_DEBUG +#if BUILD_DEBUG && 0 // Prevent removing object that is still reverenced by the removal service ASSERT(!ObjectsRemovalService::IsInPool(this)); #endif From 85aed8c4d70f37e63b536aa88ac0252e01132525 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 3 Jul 2025 20:32:41 +0200 Subject: [PATCH 22/25] Fix using material VS to PS node directly within material input --- .../Tools/MaterialGenerator/MaterialGenerator.Material.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 0617385bc..9105fcd2e 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -10,6 +10,10 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) { switch (node->TypeID) { + // Material + case 1: + value = tryGetValue(box, Value::Zero); + break; // World Position case 2: value = Value(VariantType::Float3, TEXT("input.WorldPosition.xyz")); From 4b10d7057aad73fdc39db71029c6d7d54878085a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 3 Jul 2025 20:33:14 +0200 Subject: [PATCH 23/25] Fix crash when using material instance that has more parameters that base due to material error --- Source/Engine/Graphics/Materials/MaterialParams.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Graphics/Materials/MaterialParams.cpp b/Source/Engine/Graphics/Materials/MaterialParams.cpp index e31697f77..d670b188a 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.cpp +++ b/Source/Engine/Graphics/Materials/MaterialParams.cpp @@ -604,10 +604,11 @@ int32 MaterialParams::GetVersionHash() const void MaterialParams::Bind(MaterialParamsLink* link, MaterialParameter::BindMeta& meta) { ASSERT(link && link->This); - for (int32 i = 0; i < link->This->Count(); i++) + const int32 count = link->This->Count(); + for (int32 i = 0; i < count; i++) { MaterialParamsLink* l = link; - while (l->Down && !l->This->At(i).IsOverride()) + while (l->Down && !l->This->At(i).IsOverride() && l->Down->This->Count() == count) { l = l->Down; } From 7abed939729a701381c508fe2970c30e676a15a3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 4 Jul 2025 11:31:27 +0200 Subject: [PATCH 24/25] Optimize terrain heightmap decoding to use shared code --- .../Editor/MaterialTemplates/Terrain.shader | 12 +++--- Source/Engine/Visject/ShaderGraph.cpp | 2 +- Source/Shaders/TerrainCommon.hlsl | 37 +++++++++---------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/Content/Editor/MaterialTemplates/Terrain.shader b/Content/Editor/MaterialTemplates/Terrain.shader index abc444316..63313e304 100644 --- a/Content/Editor/MaterialTemplates/Terrain.shader +++ b/Content/Editor/MaterialTemplates/Terrain.shader @@ -15,6 +15,7 @@ #include "./Flax/Common.hlsl" #include "./Flax/MaterialCommon.hlsl" #include "./Flax/GBufferCommon.hlsl" +#include "./Flax/TerrainCommon.hlsl" @7 // Primary constant buffer (with additional material parameters) META_CB_BEGIN(0, Data) @@ -334,7 +335,7 @@ VertexOutput VS(TerrainVertexInput input) float lodValue = CurrentLOD; float morphAlpha = lodCalculated - CurrentLOD; - // Sample heightmap + // Sample heightmap and splatmaps float2 heightmapUVs = input.TexCoord * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw; #if USE_SMOOTH_LOD_TRANSITION float4 heightmapValueThisLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); @@ -342,7 +343,6 @@ VertexOutput VS(TerrainVertexInput input) float2 heightmapUVsNextLOD = nextLODPos * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw; float4 heightmapValueNextLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1); float4 heightmapValue = lerp(heightmapValueThisLOD, heightmapValueNextLOD, morphAlpha); - bool isHole = max(heightmapValueThisLOD.b + heightmapValueThisLOD.a, heightmapValueNextLOD.b + heightmapValueNextLOD.a) >= 1.9f; #if USE_TERRAIN_LAYERS float4 splatmapValueThisLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); float4 splatmapValueNextLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1); @@ -355,7 +355,6 @@ VertexOutput VS(TerrainVertexInput input) #endif #else float4 heightmapValue = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); - bool isHole = (heightmapValue.b + heightmapValue.a) >= 1.9f; #if USE_TERRAIN_LAYERS float4 splatmap0Value = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); #if TERRAIN_LAYERS_DATA_SIZE > 1 @@ -363,12 +362,11 @@ VertexOutput VS(TerrainVertexInput input) #endif #endif #endif - float height = (float)((int)(heightmapValue.x * 255.0) + ((int)(heightmapValue.y * 255) << 8)) / 65535.0; + float height = DecodeHeightmapHeight(heightmapValue); // Extract normal and the holes mask - float2 normalTemp = float2(heightmapValue.b, heightmapValue.a) * 2.0f - 1.0f; - float3 normal = float3(normalTemp.x, sqrt(1.0 - saturate(dot(normalTemp, normalTemp))), normalTemp.y); - normal = normalize(normal); + bool isHole; + float3 normal = DecodeHeightmapNormal(heightmapValue, isHole); output.Geometry.HolesMask = isHole ? 0 : 1; if (isHole) { diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index ef4f53cf9..b6616d159 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -286,7 +286,7 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) case 29: { Value inXY = tryGetValue(node->GetBox(0), Value::Zero).AsFloat2(); - value = writeLocal(ValueType::Float3, String::Format(TEXT("float3({0}, sqrt(saturate(1.0 - dot({0}.xy, {0}.xy))))"), inXY.Value), node); + value = writeLocal(ValueType::Float3, String::Format(TEXT("float3({0}, sqrt(saturate(1.0 - dot({0}, {0}))))"), inXY.Value), node); break; } // Mad diff --git a/Source/Shaders/TerrainCommon.hlsl b/Source/Shaders/TerrainCommon.hlsl index a4db9bd4f..0c2f57168 100644 --- a/Source/Shaders/TerrainCommon.hlsl +++ b/Source/Shaders/TerrainCommon.hlsl @@ -5,28 +5,30 @@ #include "./Flax/Common.hlsl" +float DecodeHeightmapHeight(float4 value) +{ + return (float)((int)(value.x * 255.0) + ((int)(value.y * 255) << 8)) / 65535.0; +} + +float3 DecodeHeightmapNormal(float4 value, out bool isHole) +{ + isHole = (value.b + value.a) >= 1.9f; + float2 normalTemp = float2(value.b, value.a) * 2.0f - 1.0f; + float3 normal = float3(normalTemp.x, sqrt(1.0 - saturate(dot(normalTemp, normalTemp))), normalTemp.y); + return normalize(normal); +} + float SampleHeightmap(Texture2D heightmap, float2 uv, float mipOffset = 0.0f) { - // Sample heightmap float4 value = heightmap.SampleLevel(SamplerPointClamp, uv, mipOffset); - - // Decode heightmap - float height = (float)((int)(value.x * 255.0) + ((int)(value.y * 255) << 8)) / 65535.0; - return height; + return DecodeHeightmapHeight(value); } float SampleHeightmap(Texture2D heightmap, float2 uv, out float3 normal, out bool isHole, float mipOffset = 0.0f) { - // Sample heightmap float4 value = heightmap.SampleLevel(SamplerPointClamp, uv, mipOffset); - - // Decode heightmap - float height = (float)((int)(value.x * 255.0) + ((int)(value.y * 255) << 8)) / 65535.0; - float2 normalTemp = float2(value.b, value.a) * 2.0f - 1.0f; - normal = float3(normalTemp.x, sqrt(1.0 - saturate(dot(normalTemp, normalTemp))), normalTemp.y); - isHole = (value.b + value.a) >= 1.9f; - normal = normalize(normal); - return height; + normal = DecodeHeightmapNormal(value, isHole); + return DecodeHeightmapHeight(value); } float3 SampleHeightmap(Texture2D heightmap, float3 localPosition, float4 localToUV, out float3 normal, out bool isHole, float mipOffset = 0.0f) @@ -36,12 +38,9 @@ float3 SampleHeightmap(Texture2D heightmap, float3 localPosition, float4 float4 value = heightmap.SampleLevel(SamplerPointClamp, uv, mipOffset); // Decode heightmap - isHole = (value.b + value.a) >= 1.9f; - float height = (float)((int)(value.x * 255.0) + ((int)(value.y * 255) << 8)) / 65535.0; + normal = DecodeHeightmapNormal(value, isHole); + float height = DecodeHeightmapHeight(value);; float3 position = float3(localPosition.x, height, localPosition.z); - float2 normalTemp = float2(value.b, value.a) * 2.0f - 1.0f; - normal = float3(normalTemp.x, sqrt(1.0 - saturate(dot(normalTemp, normalTemp))), normalTemp.y); - normal = normalize(normal); // UVs outside the heightmap are empty isHole = isHole || any(uv < 0.0f) || any(uv > 1.0f); From 0bc595f16fb3a3dc7076aa0b4af217895ac7e57e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 4 Jul 2025 12:04:36 +0200 Subject: [PATCH 25/25] Disable terarin normals smoothening --- .../Editor/SceneGraph/Actors/TerrainNode.cs | 4 ++++ Source/Engine/Terrain/TerrainPatch.cpp | 20 ++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/TerrainNode.cs b/Source/Editor/SceneGraph/Actors/TerrainNode.cs index f5cef604d..4e2cd3346 100644 --- a/Source/Editor/SceneGraph/Actors/TerrainNode.cs +++ b/Source/Editor/SceneGraph/Actors/TerrainNode.cs @@ -76,9 +76,13 @@ namespace FlaxEditor.SceneGraph.Actors // Skip removing this terrain file sif it's still referenced var sceneReferences = Editor.GetAssetReferences(e.SceneId); if (sceneReferences != null && sceneReferences.Contains(e.TerrainId)) + { + Debug.Log($"Skip removing files used by terrain {e.TerrainId} on scene {e.SceneId} as it's still in use"); continue; + } // Delete files + Debug.Log($"Removing files used by removed terrain {e.TerrainId} on scene {e.SceneId}"); foreach (var file in e.Files) { if (file != null && File.Exists(file)) diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 1c754d843..a9388224d 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -431,8 +431,6 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh GET_VERTEX(1, 1); #undef GET_VERTEX - // TODO: use SIMD for those calculations - // Calculate normals for quad two vertices Float3 n0 = Float3::Normalize((v00 - v01) ^ (v01 - v10)); Float3 n1 = Float3::Normalize((v11 - v10) ^ (v10 - v01)); @@ -446,6 +444,7 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh } } +#if 0 // Smooth normals for (int32 z = 1; z < normalsSize.Y - 1; z++) { @@ -466,8 +465,6 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh GET_NORMAL(2, 2); #undef GET_VERTEX - // TODO: use SIMD for those calculations - /* * The current vertex is (11). Calculate average for the nearby vertices. * 00 01 02 @@ -481,6 +478,7 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh normalsPerVertex[i11] = Float3::Lerp(n11, avg, 0.6f); } } +#endif // Write back to the data container const auto ptr = (Color32*)data; @@ -525,10 +523,9 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh const int32 textureIndex = tz + tx; const int32 heightmapIndex = hz + hx; const int32 normalIndex = sz + sx; -#if BUILD_DEBUG - ASSERT(normalIndex >= 0 && normalIndex < normalsLength); -#endif - Float3 normal = Float3::NormalizeFast(normalsPerVertex[normalIndex]) * 0.5f + 0.5f; + ASSERT_LOW_LAYER(normalIndex >= 0 && normalIndex < normalsLength); + Float3 normal = Float3::NormalizeFast(normalsPerVertex[normalIndex]); + normal = normal * 0.5f + 0.5f; if (holesMask && !holesMask[heightmapIndex]) normal = Float3::One; @@ -1247,6 +1244,11 @@ void TerrainPatch::ClearCache() void TerrainPatch::CacheHeightData() { + if (Heightmap == nullptr) + { + LOG(Error, "Missing heightmap."); + return; + } PROFILE_CPU_NAMED("Terrain.CacheHeightData"); const TerrainDataUpdateInfo info(this); @@ -1745,7 +1747,7 @@ bool TerrainPatch::UpdateHeightData(TerrainDataUpdateInfo& info, const Int2& mod // Prepare data for the uploading to GPU ASSERT(Heightmap); auto texture = Heightmap->GetTexture(); - ASSERT(texture->ResidentMipLevels() > 0); + ASSERT(texture->IsAllocated()); const int32 textureSize = texture->Width(); const PixelFormat pixelFormat = texture->Format(); const int32 pixelStride = PixelFormatExtensions::SizeInBytes(pixelFormat);