From 51ffac25a3db273c3b3f1684b42facb83a2d9fda Mon Sep 17 00:00:00 2001 From: Tyler Gregorcyk Date: Thu, 25 Jun 2026 21:29:43 -0500 Subject: [PATCH 01/13] Fixes focus entries list when opening import files dialog Closes #3567 Also fixes root node margin from -14 to -16 as mentioned in #3567 Signed-off-by: Tyler Gregorcyk --- .../Content/Import/ImportFilesDialog.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Content/Import/ImportFilesDialog.cs b/Source/Editor/Content/Import/ImportFilesDialog.cs index c9b1b8c4f..f245f49ee 100644 --- a/Source/Editor/Content/Import/ImportFilesDialog.cs +++ b/Source/Editor/Content/Import/ImportFilesDialog.cs @@ -21,6 +21,7 @@ namespace FlaxEditor.Content.Import { private TreeNode _rootNode; private CustomEditorPresenter _settingsEditor; + private Tree _tree; /// /// Gets the entries count. @@ -106,11 +107,11 @@ namespace FlaxEditor.Content.Import _settingsEditor.Panel.Parent = splitPanel.Panel2; // Setup tree - var tree = new Tree(true) + _tree = new Tree(true) { Parent = splitPanel.Panel1 }; - tree.RightClick += OnTreeRightClick; + _tree.RightClick += OnTreeRightClick; _rootNode = new TreeNode(false); for (int i = 0; i < entries.Count; i++) { @@ -124,12 +125,12 @@ namespace FlaxEditor.Content.Import } _rootNode.Expand(); _rootNode.ChildrenIndent = 0; - _rootNode.Parent = tree; - tree.Margin = new Margin(0.0f, 0.0f, -14.0f, 2.0f); // Hide root node - tree.SelectedChanged += OnSelectedChanged; + _rootNode.Parent = _tree; + _tree.Margin = new Margin(0.0f, 0.0f, -16.0f, 2.0f); // Hide root node + _tree.SelectedChanged += OnSelectedChanged; // Select the first item - tree.Select(_rootNode.Children[0] as TreeNode); + _tree.Select(_rootNode.Children[0] as TreeNode); _dialogSize = new Float2(TotalWidth, EditorHeight + splitPanel.Offsets.Height); } @@ -257,5 +258,12 @@ namespace FlaxEditor.Content.Import settings.MinimumSize = new Float2(300, 400); settings.HasSizingFrame = true; } + + /// + public override void Focus() + { + base.Focus(); + _tree.SelectedNode?.Focus(); + } } } From b9c93970d082fd7ddb50b5af4e9835e8f573e1c4 Mon Sep 17 00:00:00 2001 From: Tyler Gregorcyk Date: Fri, 26 Jun 2026 13:01:45 -0500 Subject: [PATCH 02/13] Adds camera facing point to debug draw closes #4133 Signed-off-by: Tyler Gregorcyk --- Source/Engine/Debug/DebugDraw.cpp | 39 +++++++++++++++++++++++++++++++ Source/Engine/Debug/DebugDraw.h | 11 +++++++++ 2 files changed, 50 insertions(+) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 0e1768f63..aeb2cb747 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -1526,6 +1526,45 @@ void DebugDraw::DrawCircle(const Vector3& position, const Float3& normal, float } } +void DebugDraw::DrawPoint(const Vector3& position, float radius, const Color& color, float duration, bool depthTest) +{ + Float3 normal = (Float3)(Context->LastViewPosition - position); + if (normal.Length() < ZeroTolerance) + normal = Float3::Up; + normal.Normalize(); + + // Create matrix transform for unit circle points + Matrix world, scale, matrix; + Float3 right, up; + if (Float3::Dot(normal, Float3::Up) > 0.99f) + right = Float3::Right; + else if (Float3::Dot(normal, Float3::Down) > 0.99f) + right = Float3::Left; + else + Float3::Cross(normal, Float3::Up, right); + Float3::Cross(right, normal, up); + Matrix::Scaling(radius, scale); + const Float3 positionF = position - Context->Origin; + Matrix::CreateWorld(positionF, normal, up, world); + Matrix::Multiply(scale, world, matrix); + + // Draw lines of the unit circle after linear transform + PROFILE_MEM(EngineDebug); + Float3 prev = Float3::Transform(CircleCache[0], matrix); + for (int32 i = 1; i < DEBUG_DRAW_CIRCLE_VERTICES;) + { + Float3 cur = Float3::Transform(CircleCache[i++], matrix); + DebugTriangle t; + t.Color = Color32(color); + t.TimeLeft = duration; + t.V0 = positionF; + t.V1 = prev; + t.V2 = cur; + (depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault).Add(t); + prev = cur; + } +} + void DebugDraw::DrawWireTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& color, float duration, bool depthTest) { DrawLine(v0, v1, color, duration, depthTest); diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index c4ae12283..9084ab962 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -267,6 +267,16 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. API_FUNCTION() static void DrawCircle(const Vector3& position, const Float3& normal, float radius, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + + /// + /// Draws the point facing camera. + /// + /// The center position. + /// The radius. + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + API_FUNCTION() static void DrawPoint(const Vector3& position, float radius, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); /// /// Draws the wireframe triangle. @@ -780,6 +790,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw #define DEBUG_DRAW_LINES(lines, transform, color, duration, depthTest) DebugDraw::DrawLines(lines, transform, color, duration, depthTest) #define DEBUG_DRAW_BEZIER(p1, p2, p3, p4, color, duration, depthTest) DebugDraw::DrawBezier(p1, p2, p3, p4, color, duration, depthTest) #define DEBUG_DRAW_CIRCLE(position, normal, radius, color, duration, depthTest) DebugDraw::DrawCircle(position, normal, radius, color, duration, depthTest) +#define DEBUG_DRAW_POINT(position, radius, color, duration, depthTest) DebugDraw::DrawPoint(position, radius, color, duration, depthTest) #define DEBUG_DRAW_TRIANGLE(v0, v1, v2, color, duration, depthTest) DebugDraw::DrawTriangle(v0, v1, v2, color, duration, depthTest) #define DEBUG_DRAW_TRIANGLES(vertices, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, color, duration, depthTest) #define DEBUG_DRAW_TRIANGLES_EX(vertices, indices, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, indices, color, duration, depthTest) From ff10fa64e26c9377118e1258d54206d042ebeccc Mon Sep 17 00:00:00 2001 From: Tyler Gregorcyk Date: Fri, 26 Jun 2026 13:06:52 -0500 Subject: [PATCH 03/13] Corrected comment in DebugDraw::DrawPoint Signed-off-by: Tyler Gregorcyk --- Source/Engine/Debug/DebugDraw.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index aeb2cb747..0f6ea9c2f 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -1548,7 +1548,7 @@ void DebugDraw::DrawPoint(const Vector3& position, float radius, const Color& co Matrix::CreateWorld(positionF, normal, up, world); Matrix::Multiply(scale, world, matrix); - // Draw lines of the unit circle after linear transform + // Build a filled disc as a triangle fan from the center over the transformed unit circle points PROFILE_MEM(EngineDebug); Float3 prev = Float3::Transform(CircleCache[0], matrix); for (int32 i = 1; i < DEBUG_DRAW_CIRCLE_VERTICES;) From b23f4571ab8a53b0e608b94a8246a72450aa37c1 Mon Sep 17 00:00:00 2001 From: Tyler Gregorcyk Date: Fri, 26 Jun 2026 13:39:12 -0500 Subject: [PATCH 04/13] Halved DrawPoints traingle count by skipping degenerate wedges Signed-off-by: Tyler Gregorcyk --- Source/Engine/Debug/DebugDraw.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 0f6ea9c2f..d025448b1 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -1550,18 +1550,15 @@ void DebugDraw::DrawPoint(const Vector3& position, float radius, const Color& co // Build a filled disc as a triangle fan from the center over the transformed unit circle points PROFILE_MEM(EngineDebug); - Float3 prev = Float3::Transform(CircleCache[0], matrix); - for (int32 i = 1; i < DEBUG_DRAW_CIRCLE_VERTICES;) + for (int32 i = 0; i < DEBUG_DRAW_CIRCLE_VERTICES; i += 2) { - Float3 cur = Float3::Transform(CircleCache[i++], matrix); DebugTriangle t; t.Color = Color32(color); t.TimeLeft = duration; t.V0 = positionF; - t.V1 = prev; - t.V2 = cur; + t.V1 = Float3::Transform(CircleCache[i], matrix); + t.V2 = Float3::Transform(CircleCache[i + 1], matrix); (depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault).Add(t); - prev = cur; } } From 5ead0d482b8fac97bd1b75fe890e7eaa9c24fa0e Mon Sep 17 00:00:00 2001 From: Tyler Gregorcyk Date: Sun, 28 Jun 2026 13:38:40 -0500 Subject: [PATCH 05/13] Adds Brightness, ResolusionScale, DebugView, and LayerMask to viewport persistence Signed-off-by: Tyler Gregorcyk --- Source/Editor/Windows/EditGameWindow.cs | 26 ++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Windows/EditGameWindow.cs b/Source/Editor/Windows/EditGameWindow.cs index 5f91aebe6..d445b55ee 100644 --- a/Source/Editor/Windows/EditGameWindow.cs +++ b/Source/Editor/Windows/EditGameWindow.cs @@ -433,14 +433,18 @@ namespace FlaxEditor.Windows writer.WriteAttributeString("GridEnabled", Viewport.Grid.Enabled.ToString()); writer.WriteAttributeString("ShowFpsCounter", Viewport.ShowFpsCounter.ToString()); writer.WriteAttributeString("ShowNavigation", Viewport.ShowNavigation.ToString()); + writer.WriteAttributeString("UseOrthographicProjection", Viewport.UseOrthographicProjection.ToString()); writer.WriteAttributeString("NearPlane", Viewport.NearPlane.ToString()); writer.WriteAttributeString("FarPlane", Viewport.FarPlane.ToString()); writer.WriteAttributeString("FieldOfView", Viewport.FieldOfView.ToString()); writer.WriteAttributeString("MovementSpeed", Viewport.MovementSpeed.ToString()); + writer.WriteAttributeString("Brightness", Viewport.Brightness.ToString()); writer.WriteAttributeString("ViewportIconsScale", ViewportIconsRenderer.Scale.ToString()); + writer.WriteAttributeString("ResolutionScale", Viewport.ResolutionScale.ToString()); writer.WriteAttributeString("OrthographicScale", Viewport.OrthographicScale.ToString()); - writer.WriteAttributeString("UseOrthographicProjection", Viewport.UseOrthographicProjection.ToString()); writer.WriteAttributeString("ViewFlags", ((ulong)Viewport.Task.View.Flags).ToString()); + writer.WriteAttributeString("DebugView", ((int)Viewport.Task.ViewMode).ToString()); + writer.WriteAttributeString("LayerMask", Viewport.Task.ViewLayersMask.Mask.ToString()); } /// @@ -452,6 +456,8 @@ namespace FlaxEditor.Windows Viewport.ShowFpsCounter = value1; if (bool.TryParse(node.GetAttribute("ShowNavigation"), out value1)) Viewport.ShowNavigation = value1; + if (bool.TryParse(node.GetAttribute("UseOrthographicProjection"), out value1)) + Viewport.UseOrthographicProjection = value1; if (float.TryParse(node.GetAttribute("NearPlane"), out float value2)) Viewport.NearPlane = value2; if (float.TryParse(node.GetAttribute("FarPlane"), out value2)) @@ -460,18 +466,28 @@ namespace FlaxEditor.Windows Viewport.FieldOfView = value2; if (float.TryParse(node.GetAttribute("MovementSpeed"), out value2)) Viewport.MovementSpeed = value2; + if (float.TryParse(node.GetAttribute("Brightness"), out value2)) + Viewport.Brightness = value2; + if (float.TryParse(node.GetAttribute("ResolutionScale"), out value2)) + Viewport.ResolutionScale = value2; if (float.TryParse(node.GetAttribute("ViewportIconsScale"), out value2)) ViewportIconsRenderer.Scale = value2; if (float.TryParse(node.GetAttribute("OrthographicScale"), out value2)) Viewport.OrthographicScale = value2; - if (bool.TryParse(node.GetAttribute("UseOrthographicProjection"), out value1)) - Viewport.UseOrthographicProjection = value1; if (ulong.TryParse(node.GetAttribute("ViewFlags"), out ulong value3)) Viewport.Task.ViewFlags = (ViewFlags)value3; - - // Reset view flags if opening with different engine version (ViewFlags enum could be modified) + if (int.TryParse(node.GetAttribute("DebugView"), out int value4)) + Viewport.Task.ViewMode = (ViewMode)value4; + if (uint.TryParse(node.GetAttribute("LayerMask"), out uint value5)) + Viewport.Task.ViewLayersMask = new LayersMask(value5); + + // Reset view flags and view mode if opening with different engine version + // (ViewFlags and ViewMode enums could be modified) if (Editor.LastProjectOpenedEngineBuild != Globals.EngineBuildNumber) + { Viewport.Task.ViewFlags = ViewFlags.DefaultEditor; + Viewport.Task.ViewMode = ViewMode.Default; + } } /// From 37905f1ccb2dabaf5d9cb15647cc8dcf2e47a48b Mon Sep 17 00:00:00 2001 From: Tyler Gregorcyk Date: Sun, 28 Jun 2026 13:51:51 -0500 Subject: [PATCH 06/13] Adds option to use persistent settings over defualt viewport options Signed-off-by: Tyler Gregorcyk --- Source/Editor/Options/ViewportOptions.cs | 7 +++++++ Source/Editor/Windows/EditGameWindow.cs | 3 +++ 2 files changed, 10 insertions(+) diff --git a/Source/Editor/Options/ViewportOptions.cs b/Source/Editor/Options/ViewportOptions.cs index 2dd08b6d1..485ac7294 100644 --- a/Source/Editor/Options/ViewportOptions.cs +++ b/Source/Editor/Options/ViewportOptions.cs @@ -136,6 +136,13 @@ namespace FlaxEditor.Options [DefaultValue(50.0f), Limit(25.0f, 500.0f, 5.0f)] [EditorDisplay("Defaults"), EditorOrder(220), Tooltip("The default editor viewport grid scale.")] public float ViewportGridScale { get; set; } = 50.0f; + + /// + /// Gets or sets the use persistence over defaults setting + /// + [DefaultValue(true)] + [EditorDisplay("Defaults"), EditorOrder(230), Tooltip("Allow persistence setting from last session to override default settings")] + public bool UsePersistenceOverDefaults { get; set; } = true; /// /// Gets or sets the view distance you can see the grid. diff --git a/Source/Editor/Windows/EditGameWindow.cs b/Source/Editor/Windows/EditGameWindow.cs index d445b55ee..8d0c146d9 100644 --- a/Source/Editor/Windows/EditGameWindow.cs +++ b/Source/Editor/Windows/EditGameWindow.cs @@ -450,6 +450,9 @@ namespace FlaxEditor.Windows /// public override void OnLayoutDeserialize(XmlElement node) { + if (!Editor.Options.Options.Viewport.UsePersistenceOverDefaults) + return; + if (bool.TryParse(node.GetAttribute("GridEnabled"), out bool value1)) Viewport.Grid.Enabled = value1; if (bool.TryParse(node.GetAttribute("ShowFpsCounter"), out value1)) From 593b989958c7f7886cd3bd423a4589c2b8f27ab2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 29 Jun 2026 15:18:21 +0200 Subject: [PATCH 07/13] Fix procedural Skinned Mesh creation from code https://forum.flaxengine.com/t/issues-with-creating-skinnedmodel-procedurally/2627 --- Source/Engine/Graphics/Models/Mesh.cpp | 3 +-- Source/Engine/Graphics/Models/SkinnedMesh.cpp | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index c2f340645..b793cfe38 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -70,8 +70,7 @@ namespace if (accessor.AllocateBuffer(MeshBufferType::Vertex0, vertexCount, vb0layout)) return true; auto positionStream = accessor.Position(); - ASSERT(positionStream.IsLinear(PixelFormat::R32G32B32_Float)); - positionStream.SetLinear(vertices); + positionStream.Set(Span(vertices, vertexCount)); } // Vertex Buffer 1 (general purpose components) diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index 0377003be..97aa6f6fb 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -44,7 +44,7 @@ namespace // Index Buffer { - if (accessor.AllocateBuffer(MeshBufferType::Index, triangleCount, indexFormat)) + if (accessor.AllocateBuffer(MeshBufferType::Index, triangleCount * 3, indexFormat)) return true; auto indexStream = accessor.Index(); ASSERT(indexStream.IsLinear(indexFormat)); @@ -73,8 +73,7 @@ namespace return true; auto positionStream = accessor.Position(); - ASSERT(positionStream.IsLinear(PixelFormat::R32G32B32_Float)); - positionStream.SetLinear(vertices); + positionStream.Set(Span(vertices, vertexCount)); if (normals) { auto normalStream = accessor.Normal(); From db24203b8ab5c1701b5500078168010048246189 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Jun 2026 08:42:47 +0200 Subject: [PATCH 08/13] Minor adjustment to #4165 --- Source/Engine/Debug/DebugDraw.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index d025448b1..1699626aa 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -1550,6 +1550,8 @@ void DebugDraw::DrawPoint(const Vector3& position, float radius, const Color& co // Build a filled disc as a triangle fan from the center over the transformed unit circle points PROFILE_MEM(EngineDebug); + auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; + auto& debugDrawList = duration > 0 ? debugDrawData.DefaultTriangles : debugDrawData.OneFrameTriangles; for (int32 i = 0; i < DEBUG_DRAW_CIRCLE_VERTICES; i += 2) { DebugTriangle t; @@ -1558,7 +1560,7 @@ void DebugDraw::DrawPoint(const Vector3& position, float radius, const Color& co t.V0 = positionF; t.V1 = Float3::Transform(CircleCache[i], matrix); t.V2 = Float3::Transform(CircleCache[i + 1], matrix); - (depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault).Add(t); + debugDrawList.Add(t); } } From 5cd6c98ff35260dc9c59161d24f4a1c4a72cf61f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Jun 2026 08:48:24 +0200 Subject: [PATCH 09/13] Fix auto-selecting newly imported or created assets in Editor --- Source/Editor/Content/GUI/ContentView.cs | 2 +- Source/Editor/GUI/Tree/Tree.cs | 15 +- .../Editor/Modules/ContentDatabaseModule.cs | 4 - .../Editor/Modules/ContentImportingModule.cs | 4 + .../Windows/ContentWindow.Navigation.cs | 86 ++++------- Source/Editor/Windows/ContentWindow.cs | 134 ++++++++++++------ 6 files changed, 140 insertions(+), 105 deletions(-) diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 4928722b6..5382bd731 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -384,7 +384,7 @@ namespace FlaxEditor.Content.GUI /// Selects the specified item. /// /// The item. - /// If set to true item will be added to the current selection. Otherwise selection will be cleared before. + /// If set to true item will be added to the current selection. Otherwise, selection will be cleared before. public void Select(ContentItem item, bool additive = false) { if (item == null) diff --git a/Source/Editor/GUI/Tree/Tree.cs b/Source/Editor/GUI/Tree/Tree.cs index 3f60572f6..d48b1756e 100644 --- a/Source/Editor/GUI/Tree/Tree.cs +++ b/Source/Editor/GUI/Tree/Tree.cs @@ -138,7 +138,8 @@ namespace FlaxEditor.GUI.Tree /// Selects single tree node. /// /// Node to select. - public void Select(TreeNode node) + /// If set to true item will be added to the current selection. Otherwise, selection will be cleared before. + public void Select(TreeNode node, bool additive = false) { if (node == null) throw new ArgumentNullException(); @@ -151,8 +152,16 @@ namespace FlaxEditor.GUI.Tree var prev = new List(Selection); // Update selection - Selection.Clear(); - Selection.Add(node); + if (additive) + { + if (!Selection.Contains(node)) + Selection.Add(node); + } + else + { + Selection.Clear(); + Selection.Add(node); + } // Ensure that node can be visible (all it's parents are expanded) node.ExpandAllParents(); diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index 53075fe38..0559e18db 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -1261,7 +1261,6 @@ namespace FlaxEditor.Modules private void OnImportFileDone(string path) { - // Check if already has that element var item = Find(path); if (item is BinaryAssetItem binaryAssetItem) { @@ -1284,9 +1283,6 @@ namespace FlaxEditor.Modules binaryAssetItem.OnReimport(ref assetInfo.ID); } } - - // Refresh content view (not the best design because window could also track this event but it gives better performance) - Editor.Windows.ContentWin?.RefreshView(); } } diff --git a/Source/Editor/Modules/ContentImportingModule.cs b/Source/Editor/Modules/ContentImportingModule.cs index 8029c7418..99994ec1f 100644 --- a/Source/Editor/Modules/ContentImportingModule.cs +++ b/Source/Editor/Modules/ContentImportingModule.cs @@ -252,6 +252,8 @@ namespace FlaxEditor.Modules /// Import settings to override. Use null to skip this value. private void Import(string inputPath, string outputPath, bool isInBuilt, bool skipSettingsDialog = false, object settings = null) { + inputPath = StringUtils.NormalizePath(inputPath); + outputPath = StringUtils.NormalizePath(outputPath); lock (_requests) { _requests.Add(new Request @@ -311,7 +313,9 @@ namespace FlaxEditor.Modules } _importBatchDone++; + Profiler.BeginEvent("ImportFileEnd"); ImportFileEnd?.Invoke(entry, failed); + Profiler.EndEvent(); } } else diff --git a/Source/Editor/Windows/ContentWindow.Navigation.cs b/Source/Editor/Windows/ContentWindow.Navigation.cs index 4c7373aa6..27826e195 100644 --- a/Source/Editor/Windows/ContentWindow.Navigation.cs +++ b/Source/Editor/Windows/ContentWindow.Navigation.cs @@ -81,28 +81,10 @@ namespace FlaxEditor.Windows _navigationUndo.Push(source); } - // Show folder contents and select tree node - if (!_showAllContentInTree) - RefreshView(target); - _tree.Select(target); - target.ExpandAllParents(); - // Clear redo list _navigationRedo.Clear(); - // Set valid sizes for stacks - //RedoList.SetSize(32); - //UndoList.SetSize(32); - - // Update search - if (!_showAllContentInTree) - UpdateItemsSearch(); - - // Unlock navigation - _navigationUnlocked = true; - - // Update UI - UpdateUI(); + DoNavigate(target); } } @@ -123,25 +105,7 @@ namespace FlaxEditor.Windows // Add to Redo list _navigationRedo.Push(SelectedNode); - // Select node - if (!_showAllContentInTree) - RefreshView(node); - _tree.Select(node); - node.ExpandAllParents(); - - // Set valid sizes for stacks - //RedoList.SetSize(32); - //UndoList.SetSize(32); - - // Update search - if (!_showAllContentInTree) - UpdateItemsSearch(); - - // Unlock navigation - _navigationUnlocked = true; - - // Update UI - UpdateUI(); + DoNavigate(node); if (!_showAllContentInTree) _view.SelectFirstItem(); } @@ -164,25 +128,7 @@ namespace FlaxEditor.Windows // Add to Undo list _navigationUndo.Push(SelectedNode); - // Select node - if (!_showAllContentInTree) - RefreshView(node); - _tree.Select(node); - node.ExpandAllParents(); - - // Set valid sizes for stacks - //RedoList.SetSize(32); - //UndoList.SetSize(32); - - // Update search - if (!_showAllContentInTree) - UpdateItemsSearch(); - - // Unlock navigation - _navigationUnlocked = true; - - // Update UI - UpdateUI(); + DoNavigate(node); if (!_showAllContentInTree) _view.SelectFirstItem(); } @@ -214,6 +160,32 @@ namespace FlaxEditor.Windows UpdateUI(); } + private void DoNavigate(ContentFolderTreeNode node) + { + // Select node + if (!_showAllContentInTree) + RefreshView(node); + _tree.Select(node); + node.ExpandAllParents(); + + // Set valid sizes for stacks + //RedoList.SetSize(32); + //UndoList.SetSize(32); + + // Update search + if (!_showAllContentInTree) + UpdateItemsSearch(); + + // Unlock navigation + _navigationUnlocked = true; + + UpdateUI(); + + // Clear auto-select cache for new/imported files + _newFilesCache?.Clear(); + _newFilesCacheSize = 0; + } + private void UpdateNavigationBar() { if (_navigationBar == null) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 5a73530bc..2d9daabeb 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -67,6 +67,8 @@ namespace FlaxEditor.Windows private readonly Stack _navigationRedo = new Stack(32); private NewItem _newElement; + private List _newFilesCache; + private int _newFilesCacheSize; /// /// Gets the toolstrip. @@ -598,21 +600,9 @@ namespace FlaxEditor.Windows // Disable scrolling in proper view _renameInTree = _showAllContentInTree; if (_renameInTree) - { - if (_contentTreePanel.VScrollBar != null) - _contentTreePanel.VScrollBar.ThumbEnabled = false; - if (_contentTreePanel.HScrollBar != null) - _contentTreePanel.HScrollBar.ThumbEnabled = false; ScrollingOnTreeView(false); - } else - { - if (_contentViewPanel.VScrollBar != null) - _contentViewPanel.VScrollBar.ThumbEnabled = false; - if (_contentViewPanel.HScrollBar != null) - _contentViewPanel.HScrollBar.ThumbEnabled = false; ScrollingOnContentView(false); - } // Show rename popup RenamePopup popup; @@ -664,21 +654,9 @@ namespace FlaxEditor.Windows { // Restore scrolling in proper view if (_renameInTree) - { - if (_contentTreePanel.VScrollBar != null) - _contentTreePanel.VScrollBar.ThumbEnabled = true; - if (_contentTreePanel.HScrollBar != null) - _contentTreePanel.HScrollBar.ThumbEnabled = true; ScrollingOnTreeView(true); - } else - { - if (_contentViewPanel.VScrollBar != null) - _contentViewPanel.VScrollBar.ThumbEnabled = true; - if (_contentViewPanel.HScrollBar != null) - _contentViewPanel.HScrollBar.ThumbEnabled = true; ScrollingOnContentView(true); - } _renameInTree = false; // Check if was creating new element @@ -704,7 +682,6 @@ namespace FlaxEditor.Windows // Check if can rename this item if (!item.CanRename) { - // Cannot MessageBox.Show("Cannot rename this item.", "Cannot rename", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } @@ -718,7 +695,6 @@ namespace FlaxEditor.Windows // Check if name is valid if (!Editor.ContentEditing.IsValidAssetName(item, newShortName, out string hint)) { - // Invalid name MessageBox.Show("Given asset name is invalid. " + hint, "Invalid name", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } @@ -740,6 +716,7 @@ namespace FlaxEditor.Windows // Note: we create `_newElement` and then rename it to create new asset var itemFolder = item.ParentFolder; Action endEvent = null; + bool lazyCreation = false; if (_newElement == item) { try @@ -750,6 +727,9 @@ namespace FlaxEditor.Windows var proxy = _newElement.Proxy; Editor.Log(string.Format("Creating asset {0} in {1}", proxy.Name, newPath)); proxy.Create(newPath, _newElement.Argument); + + // When creating item with options dialog deffer processing + lazyCreation = !File.Exists(newPath); } catch (Exception ex) { @@ -773,6 +753,16 @@ namespace FlaxEditor.Windows if (_newElement.Proxy is ScriptProxy && Editor.Instance.Options.Options.General.AutoReloadScriptsOnMainWindowFocus) ScriptsBuilder.MarkWorkspaceDirty(); + // Cache new file to be auto-selected after actual creation + _newFilesCache?.Clear(); + _newFilesCacheSize = 0; + if (lazyCreation) + { + _newFilesCache ??= new List(); + _newFilesCache.Add(newPath); + _newFilesCacheSize = 1; + } + // Destroy mock control _newElement.ParentFolder = null; _newElement.Dispose(); @@ -789,7 +779,8 @@ namespace FlaxEditor.Windows var newItem = itemFolder.FindChild(newPath); if (newItem == null) { - Editor.LogWarning("Failed to find the created new item."); + if (!lazyCreation) + Editor.LogWarning("Failed to find the created new item."); return; } @@ -1143,11 +1134,12 @@ namespace FlaxEditor.Windows } /// - /// Selects the specified item in the content view. + /// Selects the specified item in the content view. Does nothing if the current view doesn't show the folder containing that item. /// /// The item to select. /// True of scroll to the item quickly without smoothing. - public void Select(ContentItem item, bool fastScroll = false) + /// True of select item in additive mode with existing selection preservation, otherwise current selection will be cleared. + public void Select(ContentItem item, bool fastScroll = false, bool additive = false) { if (item == null) throw new ArgumentNullException(); @@ -1169,7 +1161,7 @@ namespace FlaxEditor.Windows targetNode.ExpandAllParents(); if (item is ContentFolder) { - _tree.Select(targetNode); + _tree.Select(targetNode, additive); _contentTreePanel.ScrollViewTo(targetNode, fastScroll); targetNode.Focus(); } @@ -1178,13 +1170,13 @@ namespace FlaxEditor.Windows var itemNode = FindTreeItemNode(targetNode, item); if (itemNode != null) { - _tree.Select(itemNode); + _tree.Select(itemNode, additive); _contentTreePanel.ScrollViewTo(itemNode, fastScroll); itemNode.Focus(); } else { - _tree.Select(targetNode); + _tree.Select(targetNode, additive); } } } @@ -1195,7 +1187,7 @@ namespace FlaxEditor.Windows Navigate(parent.Node); // Select and scroll to cover in view - _view.Select(item); + _view.Select(item, additive); _contentViewPanel.ScrollViewTo(item, fastScroll); // Focus @@ -1574,7 +1566,7 @@ namespace FlaxEditor.Windows /// public override void OnInit() { - // Content database events + // Content events Editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true; Editor.ContentDatabase.ItemAdded += OnContentDatabaseItemAdded; Editor.ContentDatabase.ItemRemoved += OnContentDatabaseItemRemoved; @@ -1594,6 +1586,9 @@ namespace FlaxEditor.Windows else if (_root != null) ShowRoot(); }; + Editor.ContentImporting.ImportFileBegin += OnImportFileBegin; + Editor.ContentImporting.ImportFileEnd += OnImportFileEnd; + Editor.ContentImporting.ImportingQueueBegin += OnImportingQueueBegin; LoadExpandedFolders(); Refresh(); @@ -1631,6 +1626,64 @@ namespace FlaxEditor.Windows OnFoldersSearchBoxTextChanged(); } + private void OnImportFileBegin(IFileEntryAction entry) + { + // Add to auto-select cache + _newFilesCache ??= new List(); + _newFilesCache.Add(entry.ResultUrl); + _newFilesCacheSize++; + } + + private void OnImportFileEnd(IFileEntryAction entry, bool failed) + { + if (failed) + return; + if (!Platform.IsInMainThread) + { + FlaxEngine.Scripting.InvokeOnUpdate(() => OnImportFileEnd(entry, false)); + return; + } + + // Refresh view (gives faster response than waiting for filesystem event) + //RefreshView(); // TODO: is this still needed? + + // Auto-select pending items + if (_newFilesCache != null && _newFilesCache.Contains(entry.ResultUrl)) + { + var item = EnsureItem(entry.ResultUrl); + if (item != null) + { + bool additive = _newFilesCache.Count != _newFilesCacheSize; + Select(item, true, additive); + } + _newFilesCache.Remove(entry.ResultUrl); + } + } + + private void OnImportingQueueBegin() + { + // Clear cache to auto-select all imported files + _newFilesCache?.Clear(); + _newFilesCacheSize = 0; + } + + private ContentItem EnsureItem(string path) + { + var item = Editor.ContentDatabase.Find(path); + if (item == null) + { + // Cannot find the item (eg. just created file, content database event not yet handled) so refresh to take effect quickly + var parentPath = Path.GetDirectoryName(path); + var parentItem = Editor.ContentDatabase.Find(parentPath); + if (parentItem != null) + { + Editor.ContentDatabase.RefreshFolder(parentItem, false); + item = Editor.ContentDatabase.Find(path); + } + } + return item; + } + private void Refresh() { // Setup content root node @@ -1774,16 +1827,11 @@ namespace FlaxEditor.Windows return base.OnMouseUp(location, button); } - /// - protected override void PerformLayoutBeforeChildren() - { - base.PerformLayoutBeforeChildren(); - } - /// protected override void PerformLayoutAfterChildren() { base.PerformLayoutAfterChildren(); + UpdateNavigationBarBounds(); } @@ -1846,12 +1894,18 @@ namespace FlaxEditor.Windows _treeHeaderPanel = null; _treeOnlyPanel = null; _contentItemsSearchPanel = null; + _newFilesCache = null; Editor.Options.OptionsChanged -= OnOptionsChanged; ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd; if (Editor?.ContentDatabase != null) + { Editor.ContentDatabase.ItemAdded -= OnContentDatabaseItemAdded; + Editor.ContentImporting.ImportFileBegin -= OnImportFileBegin; + Editor.ContentImporting.ImportFileEnd -= OnImportFileEnd; + Editor.ContentImporting.ImportingQueueBegin -= OnImportingQueueBegin; + } base.OnDestroy(); } From 768beebd000ac31fda058d19df0d36f3dc456719 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Jun 2026 08:48:56 +0200 Subject: [PATCH 10/13] Fix reloading assets that failed to load --- Source/Engine/Content/Asset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index 5560c20b9..5960badf6 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -430,7 +430,7 @@ void Asset::Reload() ScopeLock lock(Locker); - if (IsLoaded()) + if (IsLoaded() || LastLoadFailed()) { // Unload current data unload(true); From 96819d2dcdb0f3777abd388cdc081743fd2e2a7d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Jun 2026 08:49:37 +0200 Subject: [PATCH 11/13] Fix scene texture node with `WorldPosition` --- .../Tools/MaterialGenerator/MaterialGenerator.Textures.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp index 09ad93cec..e09b1e9ad 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp @@ -475,7 +475,11 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) uv = MaterialValue::Cast(tryGetValue(uvBox, getUVs), VariantType::Float2).Value; else uv = TEXT("input.TexCoord.xy"); - value = writeLocal(VariantType::Float3, String::Format(TEXT("GetWorldPos({1}, {0}.rgb)"), depthSample->Value, uv), node); + const auto layer = GetRootLayer(); + if (layer && layer->Domain == MaterialDomain::PostProcess) + value = writeLocal(VariantType::Float3, String::Format(TEXT("GetWorldPos({1}, {0})"), depthSample->Value, uv), node); + else // TODO: reimpl GetWorldPos() for other domains (see 'Content/Editor/MaterialTemplates/PostProcess.shader'), can be via matrix inverse in a shader + value = ShaderGraphValue::InitForZero(VariantType::Float3); break; } case MaterialSceneTextures::SceneStencil: From 1b954a1edcb9bb3aaec7f09dd0b4f13df9416b2d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Jun 2026 09:59:09 +0200 Subject: [PATCH 12/13] Exclude more C# stdlib from Visual Script to reduce clobber --- Source/Editor/Surface/VisualScriptSurface.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Editor/Surface/VisualScriptSurface.cs b/Source/Editor/Surface/VisualScriptSurface.cs index 57aa29d67..0d9a29e7f 100644 --- a/Source/Editor/Surface/VisualScriptSurface.cs +++ b/Source/Editor/Surface/VisualScriptSurface.cs @@ -39,8 +39,16 @@ namespace FlaxEditor.Surface { "Newtonsoft.Json.", "System.Array", + "System.ComponentModel.", "System.Linq.Expressions.", "System.Reflection.", + "System.Runtime.CompilerServices.", + "System.Runtime.InteropServices.", + "System.Runtime.Intrinsics.", + "System.Security.", + "System.Text.", + "System.Xml.", + "MS.", }; private static NodesCache _nodesCache = new NodesCache(IterateNodesCache); From 9952d6cbf8613d64fe654967e621399f67d5f84f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Jun 2026 09:59:14 +0200 Subject: [PATCH 13/13] Update asset --- Content/Shaders/DepthOfField.flax | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content/Shaders/DepthOfField.flax b/Content/Shaders/DepthOfField.flax index 7febbf090..167b0bdf8 100644 --- a/Content/Shaders/DepthOfField.flax +++ b/Content/Shaders/DepthOfField.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abd45cf24b2b6c728ec603052a2aace6f335d6ccfc8b0d5e3184c48c7c2a35f1 +oid sha256:ba2624e8a3949e339c4b10d356be69c67d6e39bad89a34755fbbad191135f953 size 13356