diff --git a/Content/Editor/MaterialTemplates/Terrain.shader b/Content/Editor/MaterialTemplates/Terrain.shader
index 3b786a7cd..abc444316 100644
--- a/Content/Editor/MaterialTemplates/Terrain.shader
+++ b/Content/Editor/MaterialTemplates/Terrain.shader
@@ -438,7 +438,6 @@ VertexOutput VS(TerrainVertexInput input)
// Apply world position offset per-vertex
#if USE_POSITION_OFFSET
output.Geometry.WorldPosition += material.PositionOffset;
- output.Geometry.PrevWorldPosition += material.PositionOffset;
output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix);
#endif
diff --git a/README.md b/README.md
index c4d6e7298..8e9de0424 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
Flax Engine is a high quality modern 3D game engine written in C++ and C#.
From stunning graphics to powerful scripts, it's designed for fast workflow with many ready-to-use features waiting for you right now. To learn more see the website ([www.flaxengine.com](https://flaxengine.com)).
-This repository contains full source code of the Flax Engine (excluding NDA-protected platforms support). Anyone is welcome to contribute or use the modified source in Flax-based games.
+This repository contains full source code of the Flax Engine (excluding NDA-protected platforms support). Documentation source is also available in a separate repository. Anyone is welcome to contribute or use the modified source in Flax-based games.
# Development
@@ -46,21 +46,26 @@ Follow the instructions below to compile and run the engine from source.
* Install Visual Studio Code
* Install .NET 8 or 9 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
* Ubuntu: `sudo apt install dotnet-sdk-8.0`
+ * Fedora: `sudo dnf install dotnet-sdk-8.0`
* Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host`
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
* Ubuntu: `sudo apt install vulkan-sdk`
- * Arch: `sudo pacman -S spirv-tools vulkan-headers vulkan-tools vulkan-validation-layers`
+ * Fedora: `sudo dnf install vulkan-headers vulkan-tools vulkan-validation-layers`
+ * Arch: `sudo pacman -S vulkan-headers vulkan-tools vulkan-validation-layers`
* Install Git with LFS
* Ubuntu: `sudo apt-get install git git-lfs`
* Arch: `sudo pacman -S git git-lfs`
* `git-lfs install`
* Install the required packages:
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev`
+ * Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel`
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib`
* Install Clang compiler (version 6 or later):
* Ubuntu: `sudo apt-get install clang lldb lld`
+ * Fedora: `sudo dnf install clang llvm lldb lld`
* Arch: `sudo pacman -S clang lldb lld`
* Clone the repository (with LFS)
+ * git-lfs clone https://github.com/FlaxEngine/FlaxEngine.git
* Run `./GenerateProjectFiles.sh`
* Open workspace with Visual Code
* Build and run (configuration and task named `Flax|Editor.Linux.Development|x64`)
diff --git a/Source/Editor/Content/Create/CreateFileEntry.cs b/Source/Editor/Content/Create/CreateFileEntry.cs
index da2779b98..d2940b040 100644
--- a/Source/Editor/Content/Create/CreateFileEntry.cs
+++ b/Source/Editor/Content/Create/CreateFileEntry.cs
@@ -13,6 +13,11 @@ namespace FlaxEditor.Content.Create
///
public string ResultUrl { get; }
+ ///
+ /// Gets a value indicating wether a file can be created based on the current settings.
+ ///
+ public abstract bool CanBeCreated { get; }
+
///
/// Gets a value indicating whether this entry has settings to modify.
///
diff --git a/Source/Editor/Content/Create/CreateFilesDialog.cs b/Source/Editor/Content/Create/CreateFilesDialog.cs
index ef5f2d58b..a49aac52a 100644
--- a/Source/Editor/Content/Create/CreateFilesDialog.cs
+++ b/Source/Editor/Content/Create/CreateFilesDialog.cs
@@ -60,7 +60,8 @@ namespace FlaxEditor.Content.Create
Text = "Create",
AnchorPreset = AnchorPresets.BottomRight,
Offsets = new Margin(-ButtonsWidth - ButtonsMargin, ButtonsWidth, -ButtonsHeight - ButtonsMargin, ButtonsHeight),
- Parent = this
+ Parent = this,
+ Enabled = entry.CanBeCreated,
};
createButton.Clicked += OnSubmit;
var cancelButton = new Button
@@ -68,7 +69,7 @@ namespace FlaxEditor.Content.Create
Text = "Cancel",
AnchorPreset = AnchorPresets.BottomRight,
Offsets = new Margin(-ButtonsWidth - ButtonsMargin - ButtonsWidth - ButtonsMargin, ButtonsWidth, -ButtonsHeight - ButtonsMargin, ButtonsHeight),
- Parent = this
+ Parent = this,
};
cancelButton.Clicked += OnCancel;
@@ -77,7 +78,7 @@ namespace FlaxEditor.Content.Create
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Offsets = new Margin(2, 2, infoLabel.Bottom + 2, EditorHeight),
- Parent = this
+ Parent = this,
};
// Settings editor
@@ -87,6 +88,7 @@ namespace FlaxEditor.Content.Create
_dialogSize = new Float2(TotalWidth, panel.Bottom);
_settingsEditor.Select(_entry.Settings);
+ _settingsEditor.Modified += () => createButton.Enabled = _entry.CanBeCreated;
}
///
diff --git a/Source/Editor/Content/Create/ParticleEmitterCreateEntry.cs b/Source/Editor/Content/Create/ParticleEmitterCreateEntry.cs
index a862c3370..b64f7b1d0 100644
--- a/Source/Editor/Content/Create/ParticleEmitterCreateEntry.cs
+++ b/Source/Editor/Content/Create/ParticleEmitterCreateEntry.cs
@@ -12,6 +12,9 @@ namespace FlaxEditor.Content.Create
///
public class ParticleEmitterCreateEntry : CreateFileEntry
{
+ ///
+ public override bool CanBeCreated => true;
+
///
/// Types of the emitter templates that can be created.
///
diff --git a/Source/Editor/Content/Create/PrefabCreateEntry.cs b/Source/Editor/Content/Create/PrefabCreateEntry.cs
index db187e37f..90cca263d 100644
--- a/Source/Editor/Content/Create/PrefabCreateEntry.cs
+++ b/Source/Editor/Content/Create/PrefabCreateEntry.cs
@@ -14,6 +14,9 @@ namespace FlaxEditor.Content.Create
///
public class PrefabCreateEntry : CreateFileEntry
{
+ ///
+ public override bool CanBeCreated => _options.RootActorType != null;
+
///
/// The create options.
///
@@ -73,6 +76,9 @@ namespace FlaxEditor.Content.Create
///
public class WidgetCreateEntry : CreateFileEntry
{
+ ///
+ public override bool CanBeCreated => _options.RootControlType != null;
+
///
/// The create options.
///
diff --git a/Source/Editor/Content/Create/SettingsCreateEntry.cs b/Source/Editor/Content/Create/SettingsCreateEntry.cs
index fd43f43e0..bdeeeadf0 100644
--- a/Source/Editor/Content/Create/SettingsCreateEntry.cs
+++ b/Source/Editor/Content/Create/SettingsCreateEntry.cs
@@ -17,6 +17,8 @@ namespace FlaxEditor.Content.Create
///
internal class SettingsCreateEntry : CreateFileEntry
{
+ public override bool CanBeCreated => _options.Type != null;
+
internal class Options
{
[Tooltip("The settings type.")]
diff --git a/Source/Editor/Content/Create/VisualScriptCreateEntry.cs b/Source/Editor/Content/Create/VisualScriptCreateEntry.cs
index 768db6310..b74eb6ab2 100644
--- a/Source/Editor/Content/Create/VisualScriptCreateEntry.cs
+++ b/Source/Editor/Content/Create/VisualScriptCreateEntry.cs
@@ -11,6 +11,9 @@ namespace FlaxEditor.Content.Create
///
public class VisualScriptCreateEntry : CreateFileEntry
{
+ ///
+ public override bool CanBeCreated => _options.BaseClass != null;
+
///
/// The create options.
///
diff --git a/Source/Editor/Content/Proxy/JsonAssetProxy.cs b/Source/Editor/Content/Proxy/JsonAssetProxy.cs
index b71c044b5..2d5f20b47 100644
--- a/Source/Editor/Content/Proxy/JsonAssetProxy.cs
+++ b/Source/Editor/Content/Proxy/JsonAssetProxy.cs
@@ -65,6 +65,9 @@ namespace FlaxEditor.Content
///
public class GenericJsonCreateEntry : CreateFileEntry
{
+ ///
+ public override bool CanBeCreated => _options.Type != null;
+
///
/// The create options.
///
diff --git a/Source/Editor/CustomEditorWindow.cs b/Source/Editor/CustomEditorWindow.cs
index 72c28234d..f28a844ef 100644
--- a/Source/Editor/CustomEditorWindow.cs
+++ b/Source/Editor/CustomEditorWindow.cs
@@ -33,8 +33,8 @@ namespace FlaxEditor
private void Set(CustomEditorWindow value)
{
_customEditor = value;
- _presenter.Select(value);
_presenter.OverrideEditor = value;
+ _presenter.Select(value);
}
///
diff --git a/Source/Editor/CustomEditors/CustomEditorPresenter.cs b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
index f441d2928..1030abfda 100644
--- a/Source/Editor/CustomEditors/CustomEditorPresenter.cs
+++ b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using FlaxEditor.SceneGraph;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -52,6 +53,16 @@ namespace FlaxEditor.CustomEditors
///
/// The nodes to select
public void Select(List nodes);
+
+ ///
+ /// Gets the current selection.
+ ///
+ public List Selection { get; }
+
+ ///
+ /// Indication of if the properties window is locked on specific objects.
+ ///
+ public bool LockSelection { get; set; }
}
///
@@ -81,6 +92,8 @@ namespace FlaxEditor.CustomEditors
Offsets = Margin.Zero;
Pivot = Float2.Zero;
IsScrollable = true;
+ Spacing = Utilities.Constants.UIMargin;
+ Margin = new Margin(Utilities.Constants.UIMargin);
}
///
@@ -95,7 +108,7 @@ namespace FlaxEditor.CustomEditors
{
FlaxEditor.Editor.LogWarning(ex);
- // Refresh layout on errors to reduce lgo spam
+ // Refresh layout on errors to reduce log spam
_presenter.BuildLayout();
}
@@ -132,6 +145,8 @@ namespace FlaxEditor.CustomEditors
get => _overrideEditor;
set
{
+ if (_overrideEditor == value)
+ return;
_overrideEditor = value;
RebuildLayout();
}
@@ -200,7 +215,6 @@ namespace FlaxEditor.CustomEditors
protected override void Deinitialize()
{
Editor = null;
- _overrideEditor = null;
base.Deinitialize();
}
diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
index 062aeeedd..540b602ec 100644
--- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
@@ -9,7 +9,6 @@ using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.GUI;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Tree;
-using FlaxEditor.Modules;
using FlaxEditor.Scripting;
using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets;
@@ -71,14 +70,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Display prefab UI (when displaying object inside Prefab Window then display only nested prefabs)
prefab.GetNestedObject(ref prefabObjectId, out var nestedPrefabId, out var nestedPrefabObjectId);
var nestedPrefab = FlaxEngine.Content.Load(nestedPrefabId);
- var panel = layout.CustomContainer();
+ var panel = layout.UniformGrid();
panel.CustomControl.Height = 20.0f;
panel.CustomControl.SlotsVertically = 1;
if (Presenter == Editor.Instance.Windows.PropertiesWin.Presenter || nestedPrefab)
{
var targetPrefab = nestedPrefab ?? prefab;
panel.CustomControl.SlotsHorizontally = 3;
-
+
// Selecting actor prefab asset
var selectPrefab = panel.Button("Select Prefab");
selectPrefab.Button.Clicked += () =>
@@ -133,35 +132,22 @@ namespace FlaxEditor.CustomEditors.Dedicated
var actor = (Actor)Values[0];
var scriptType = TypeUtils.GetType(actor.TypeName);
var item = scriptType.ContentItem;
- if (Presenter.Owner is PropertiesWindow propertiesWindow)
+ if (Presenter.Owner != null)
{
- var lockButton = cm.AddButton(propertiesWindow.LockObjects ? "Unlock" : "Lock");
+ var lockButton = cm.AddButton(Presenter.Owner.LockSelection ? "Unlock" : "Lock");
lockButton.ButtonClicked += button =>
{
- propertiesWindow.LockObjects = !propertiesWindow.LockObjects;
+ var owner = Presenter?.Owner;
+ if (owner == null)
+ return;
+ owner.LockSelection = !owner.LockSelection;
// Reselect current selection
- if (!propertiesWindow.LockObjects && Editor.Instance.SceneEditing.SelectionCount > 0)
+ if (!owner.LockSelection && owner.Selection.Count > 0)
{
- var cachedSelection = Editor.Instance.SceneEditing.Selection.ToArray();
- Editor.Instance.SceneEditing.Select(null);
- Editor.Instance.SceneEditing.Select(cachedSelection);
- }
- };
- }
- else if (Presenter.Owner is PrefabWindow prefabWindow)
- {
- var lockButton = cm.AddButton(prefabWindow.LockSelectedObjects ? "Unlock" : "Lock");
- lockButton.ButtonClicked += button =>
- {
- prefabWindow.LockSelectedObjects = !prefabWindow.LockSelectedObjects;
-
- // Reselect current selection
- if (!prefabWindow.LockSelectedObjects && prefabWindow.Selection.Count > 0)
- {
- var cachedSelection = prefabWindow.Selection.ToList();
- prefabWindow.Select(null);
- prefabWindow.Select(cachedSelection);
+ var cachedSelection = owner.Selection.ToList();
+ owner.Select(null);
+ owner.Select(cachedSelection);
}
};
}
@@ -258,7 +244,17 @@ namespace FlaxEditor.CustomEditors.Dedicated
else if (editor.Values[0] is SceneObject sceneObject)
{
node.TextColor = sceneObject.HasPrefabLink ? FlaxEngine.GUI.Style.Current.ProgressNormal : FlaxEngine.GUI.Style.Current.BackgroundSelected;
- node.Text = Utilities.Utils.GetPropertyNameUI(sceneObject.GetType().Name);
+ if (editor.Values.Info != ScriptMemberInfo.Null)
+ {
+ if (editor.Values.GetAttributes().FirstOrDefault(x => x is EditorDisplayAttribute) is EditorDisplayAttribute editorDisplayAttribute && !string.IsNullOrEmpty(editorDisplayAttribute.Name))
+ node.Text = $"{Utilities.Utils.GetPropertyNameUI(editorDisplayAttribute.Name)} ({Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name)})";
+ else
+ node.Text = Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name);
+ }
+ else if (sceneObject is Actor actor)
+ node.Text = $"{actor.Name} ({Utilities.Utils.GetPropertyNameUI(sceneObject.GetType().Name)})";
+ else
+ node.Text = Utilities.Utils.GetPropertyNameUI(sceneObject.GetType().Name);
}
// Array Item
else if (editor.ParentEditor is CollectionEditor)
@@ -268,7 +264,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Common type
else if (editor.Values.Info != ScriptMemberInfo.Null)
{
- node.Text = Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name);
+ if (editor.Values.GetAttributes().FirstOrDefault(x => x is EditorDisplayAttribute) is EditorDisplayAttribute editorDisplayAttribute
+ && !string.IsNullOrEmpty(editorDisplayAttribute.Name)
+ && !editorDisplayAttribute.Name.Contains("_inline"))
+ node.Text = $"{Utilities.Utils.GetPropertyNameUI(editorDisplayAttribute.Name)} ({Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name)})";
+ else
+ node.Text = Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name);
}
// Custom type
else if (editor.Values[0] != null)
@@ -316,7 +317,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
var childEditor = editor.ChildrenEditors[i];
// Special case for root actor transformation (can be applied only in Prefab editor, not in Level)
- if (isActorEditorInLevel && childEditor.Values.Info.Name is "LocalPosition" or "LocalOrientation" or "LocalScale")
+ if (isActorEditorInLevel && childEditor.Values.Info.Name is "LocalPosition" or "LocalOrientation" or "LocalScale" or "Name")
continue;
var child = ProcessDiff(childEditor, !isScriptEditorWithRefValue);
@@ -361,13 +362,39 @@ namespace FlaxEditor.CustomEditors.Dedicated
return result;
}
+ private TreeNode CreateDiffTree(Actor actor, CustomEditorPresenter presenter, LayoutElementsContainer layout)
+ {
+ var actorNode = Editor.Instance.Scene.GetActorNode(actor);
+ ValueContainer vc = new ValueContainer(ScriptMemberInfo.Null);
+ vc.SetType(new ScriptType(actorNode.EditableObject.GetType()));
+ vc.Add(actorNode.EditableObject);
+ var editor = CustomEditorsUtil.CreateEditor(vc, null, false);
+ editor.Initialize(presenter, layout, vc);
+ var node = ProcessDiff(editor, false);
+ layout.ClearLayout();
+ foreach (var child in actor.Children)
+ {
+ var childNode = CreateDiffTree(child, presenter, layout);
+ if (childNode == null)
+ continue;
+ if (node == null)
+ node = CreateDiffNode(editor);
+ node.AddChild(childNode);
+ }
+ return node;
+ }
+
private void ViewChanges(Control target, Float2 targetLocation)
{
// Build a tree out of modified properties
- var rootNode = ProcessDiff(this, false);
+ var thisActor = (Actor)Values[0];
+ var rootActor = thisActor.IsPrefabRoot ? thisActor : thisActor.GetPrefabRoot();
+ var presenter = new CustomEditorPresenter(null);
+ var layout = new CustomElementsContainer();
+ var rootNode = CreateDiffTree(rootActor, presenter, layout);
// Skip if no changes detected
- if (rootNode == null || rootNode.ChildrenCount == 0)
+ if (rootNode == null)
{
var cm1 = new ContextMenu();
cm1.AddButton("No changes detected");
diff --git a/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs b/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs
index 4f18baa84..1ddc1c144 100644
--- a/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs
@@ -28,7 +28,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
_infoLabel = playbackGroup.Label(string.Empty).Label;
_infoLabel.AutoHeight = true;
- var grid = playbackGroup.CustomContainer();
+ var grid = playbackGroup.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = Button.DefaultHeight;
diff --git a/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs b/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs
index e16f1141c..abb27867a 100644
--- a/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs
@@ -59,7 +59,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
var paintValue = new ReadOnlyValueContainer(new ScriptType(typeof(ClothPaintingGizmoMode)), _gizmoMode);
paintGroup.Object(paintValue);
{
- var grid = paintGroup.CustomContainer();
+ var grid = paintGroup.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = Button.DefaultHeight;
diff --git a/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs b/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs
index 70d6305b8..79e512122 100644
--- a/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs
@@ -92,12 +92,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Update add button
var update = group.Button("Update").Button;
+ group.Space(0);
update.TooltipText = "Refreshes the dashboard statistics";
update.Height = 16.0f;
update.Clicked += RebuildLayout;
// New locale add button
var addLocale = group.Button("Add Locale...").Button;
+ group.Space(0);
addLocale.TooltipText = "Shows a locale picker and creates new localization for it with not translated string tables";
addLocale.Height = 16.0f;
addLocale.ButtonClicked += delegate(Button button)
@@ -167,12 +169,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Export button
var exportLocalization = group.Button("Export...").Button;
+ group.Space(0);
exportLocalization.TooltipText = "Exports the localization strings into .pot file for translation";
exportLocalization.Height = 16.0f;
exportLocalization.Clicked += () => Export(tableEntries, allKeys);
// Find localized strings in code button
var findStringsCode = group.Button("Find localized strings in code").Button;
+ group.Space(0);
findStringsCode.TooltipText = "Searches for localized string usage in inside a project source files";
findStringsCode.Height = 16.0f;
findStringsCode.Clicked += delegate
diff --git a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs
index 65b39962b..22c5d7ece 100644
--- a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs
@@ -54,7 +54,8 @@ public class ModelPrefabEditor : GenericEditor
}
// Creates the import path UI
- Utilities.Utils.CreateImportPathUI(layout, modelPrefab.ImportPath, false);
+ var group = layout.Group("Import Path");
+ Utilities.Utils.CreateImportPathUI(group, modelPrefab.ImportPath);
var button = layout.Button("Reimport", "Reimports the source asset as prefab.");
_reimportButton = button.Button;
diff --git a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs
index 783b787ae..3de013bfb 100644
--- a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs
@@ -92,7 +92,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
_infoLabel = playbackGroup.Label(string.Empty).Label;
_infoLabel.AutoHeight = true;
- var grid = playbackGroup.CustomContainer();
+ var grid = playbackGroup.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = Button.DefaultHeight;
diff --git a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
index b68ade40b..ce157240a 100644
--- a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs
@@ -39,7 +39,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (ragdoll.Parent is AnimatedModel animatedModel && animatedModel.SkinnedModel)
{
// Builder
- var grid = editorGroup.CustomContainer();
+ var grid = editorGroup.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = Button.DefaultHeight;
@@ -53,7 +53,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (Presenter.Owner != null)
{
// Selection
- var grid = editorGroup.CustomContainer();
+ var grid = editorGroup.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = Button.DefaultHeight;
diff --git a/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs b/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs
index 6537c0c59..53dade508 100644
--- a/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs
@@ -1,6 +1,7 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System.Collections.Generic;
+using System.Reflection.Emit;
using FlaxEditor.CustomEditors.GUI;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -93,8 +94,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Add info box
if (IsSingleObject && Values[0] is RigidBody && Editor.IsPlayMode)
{
- _infoLabel = layout.Label(string.Empty).Label;
+ var group = layout.Group("Info");
+ _infoLabel = group.Label(string.Empty).Label;
_infoLabel.AutoHeight = true;
+ _infoLabel.Margin = new Margin(3);
}
}
}
diff --git a/Source/Editor/CustomEditors/Dedicated/SceneAnimationPlayerEditor.cs b/Source/Editor/CustomEditors/Dedicated/SceneAnimationPlayerEditor.cs
index aa2b42edc..e4496601b 100644
--- a/Source/Editor/CustomEditors/Dedicated/SceneAnimationPlayerEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/SceneAnimationPlayerEditor.cs
@@ -28,7 +28,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
_infoLabel = playbackGroup.Label(string.Empty).Label;
_infoLabel.AutoHeight = true;
- var grid = playbackGroup.CustomContainer();
+ var grid = playbackGroup.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = Button.DefaultHeight;
diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs
index 15566a631..614d2c160 100644
--- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs
@@ -682,7 +682,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
private CustomElementsContainer UniformGridTwoByOne(LayoutElementsContainer cont)
{
- var grid = cont.CustomContainer();
+ var grid = cont.UniformGrid();
grid.CustomControl.SlotsHorizontally = 2;
grid.CustomControl.SlotsVertically = 1;
grid.CustomControl.SlotPadding = Margin.Zero;
diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
index 355321329..2daf2f6af 100644
--- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
@@ -41,13 +41,9 @@ namespace FlaxEditor.CustomEditors.Editors
public override void Initialize(LayoutElementsContainer layout)
{
base.Initialize(layout);
-
+
if (XElement.ValueBox.Parent is UniformGridPanel ug)
- {
- ug.Height += 2;
- ug.SlotSpacing = new Float2(4);
- ug.SlotPadding = new Margin(0, 0, 1, 1);
- }
+ CheckLayout(ug);
// Override colors
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
@@ -75,11 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
base.Initialize(layout);
if (XElement.ValueBox.Parent is UniformGridPanel ug)
- {
- ug.Height += 2;
- ug.SlotSpacing = new Float2(4);
- ug.SlotPadding = new Margin(0, 0, 1, 1);
- }
+ CheckLayout(ug);
// Override colors
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
@@ -136,13 +128,9 @@ namespace FlaxEditor.CustomEditors.Editors
menu.AddButton("Link", ToggleLink).LinkTooltip("Links scale components for uniform scaling");
};
}
-
+
if (XElement.ValueBox.Parent is UniformGridPanel ug)
- {
- ug.Height += 2;
- ug.SlotSpacing = new Float2(4);
- ug.SlotPadding = new Margin(0, 0, 1, 1);
- }
+ CheckLayout(ug);
// Override colors
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
@@ -203,5 +191,13 @@ namespace FlaxEditor.CustomEditors.Editors
_linkButton.TooltipText = LinkValues ? "Unlinks scale components from uniform scaling" : "Links scale components for uniform scaling";
}
}
+
+ private static void CheckLayout(UniformGridPanel ug)
+ {
+ // Enlarge to fix border visibility
+ ug.Height += 2;
+ ug.SlotSpacing += new Float2(2);
+ ug.SlotPadding += new Margin(0, 0, 1, 1);
+ }
}
}
diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
index 6ebdb9a58..cfc11d5a5 100644
--- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
@@ -642,10 +642,10 @@ namespace FlaxEditor.CustomEditors.Editors
if (_canResize && !_readOnly)
{
var panel = dragArea.HorizontalPanel();
- panel.Panel.Size = new Float2(0, 20);
- panel.Panel.Margin = new Margin(2);
+ panel.Panel.Size = new Float2(0, 18);
+ panel.Panel.Margin = new Margin(0, 0, Utilities.Constants.UIMargin, 0);
- var removeButton = panel.Button("-", "Remove last item");
+ var removeButton = panel.Button("-", "Remove the last item");
removeButton.Button.Size = new Float2(16, 16);
removeButton.Button.Enabled = size > _minCount;
removeButton.Button.AnchorPreset = AnchorPresets.TopRight;
@@ -656,7 +656,7 @@ namespace FlaxEditor.CustomEditors.Editors
Resize(Count - 1);
};
- var addButton = panel.Button("+", "Add new item");
+ var addButton = panel.Button("+", "Add a new item");
addButton.Button.Size = new Float2(16, 16);
addButton.Button.Enabled = (!NotNullItems || size > 0) && size < _maxCount;
addButton.Button.AnchorPreset = AnchorPresets.TopRight;
diff --git a/Source/Editor/CustomEditors/Editors/ColorTrackball.cs b/Source/Editor/CustomEditors/Editors/ColorTrackball.cs
index d0259102d..87b9a9e0c 100644
--- a/Source/Editor/CustomEditors/Editors/ColorTrackball.cs
+++ b/Source/Editor/CustomEditors/Editors/ColorTrackball.cs
@@ -27,7 +27,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- float trackBallSize = 80.0f;
+ float trackBallSize = 100f;
float margin = 4.0f;
// Panel
@@ -50,7 +50,7 @@ namespace FlaxEditor.CustomEditors.Editors
// Scale editor
{
- var grid = masterPanel.CustomContainer();
+ var grid = masterPanel.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.SlotPadding = new Margin(4, 2, 2, 2);
gridControl.ClipChildren = false;
diff --git a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs
index feaada5df..3baad685d 100644
--- a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs
@@ -46,7 +46,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- var grid = layout.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = TextBox.DefaultHeight;
diff --git a/Source/Editor/CustomEditors/Editors/Vector2Editor.cs b/Source/Editor/CustomEditors/Editors/Vector2Editor.cs
index 6ae8e608f..8c09298c7 100644
--- a/Source/Editor/CustomEditors/Editors/Vector2Editor.cs
+++ b/Source/Editor/CustomEditors/Editors/Vector2Editor.cs
@@ -42,7 +42,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- var grid = layout.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = TextBox.DefaultHeight;
@@ -131,7 +131,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- var grid = layout.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = TextBox.DefaultHeight;
@@ -220,7 +220,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- var grid = layout.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = TextBox.DefaultHeight;
diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs
index cb44c60c8..d81cae199 100644
--- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs
+++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs
@@ -82,7 +82,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- var grid = layout.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = TextBox.DefaultHeight;
@@ -469,7 +469,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- var grid = layout.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = TextBox.DefaultHeight;
@@ -783,7 +783,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- var grid = layout.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = TextBox.DefaultHeight;
diff --git a/Source/Editor/CustomEditors/Editors/Vector4Editor.cs b/Source/Editor/CustomEditors/Editors/Vector4Editor.cs
index 01328c34b..34d96a80a 100644
--- a/Source/Editor/CustomEditors/Editors/Vector4Editor.cs
+++ b/Source/Editor/CustomEditors/Editors/Vector4Editor.cs
@@ -52,7 +52,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- var grid = layout.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = TextBox.DefaultHeight;
@@ -163,7 +163,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- var grid = layout.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = TextBox.DefaultHeight;
@@ -274,7 +274,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- var grid = layout.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = TextBox.DefaultHeight;
diff --git a/Source/Editor/CustomEditors/Editors/VersionEditor.cs b/Source/Editor/CustomEditors/Editors/VersionEditor.cs
index 5fc047fed..de9781dc4 100644
--- a/Source/Editor/CustomEditors/Editors/VersionEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/VersionEditor.cs
@@ -39,7 +39,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- var grid = layout.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = TextBox.DefaultHeight;
diff --git a/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs b/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs
index e3226ef2b..64bc9080b 100644
--- a/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs
+++ b/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs
@@ -22,7 +22,8 @@ namespace FlaxEditor.CustomEditors.Elements
ArrowImageClosed = new SpriteBrush(Style.Current.ArrowRight),
ArrowImageOpened = new SpriteBrush(Style.Current.ArrowDown),
EnableDropDownIcon = true,
- ItemsMargin = new Margin(7, 7, 3, 3),
+ ItemsMargin = new Margin(Utilities.Constants.UIMargin),
+ ItemsSpacing = Utilities.Constants.UIMargin,
HeaderHeight = 18.0f,
EnableContainmentLines = true,
};
diff --git a/Source/Editor/CustomEditors/GUI/PropertiesList.cs b/Source/Editor/CustomEditors/GUI/PropertiesList.cs
index 02efbb2a3..ef90fc706 100644
--- a/Source/Editor/CustomEditors/GUI/PropertiesList.cs
+++ b/Source/Editor/CustomEditors/GUI/PropertiesList.cs
@@ -20,13 +20,6 @@ namespace FlaxEditor.CustomEditors.GUI
///
public const int SplitterSize = 2;
- ///
- /// The splitter margin (in pixels).
- ///
- public const int SplitterMargin = 4;
-
- private const int SplitterSizeHalf = SplitterSize / 2;
-
private PropertiesListElement _element;
private float _splitterValue;
private Rectangle _splitterRect;
@@ -65,16 +58,18 @@ namespace FlaxEditor.CustomEditors.GUI
/// The element.
public PropertiesList(PropertiesListElement element)
{
+ ClipChildren = false;
_element = element;
_splitterValue = 0.4f;
- BottomMargin = TopMargin = RightMargin = SplitterMargin;
+ Margin = new Margin();
+ Spacing = Utilities.Constants.UIMargin;
UpdateSplitRect();
}
private void UpdateSplitRect()
{
- _splitterRect = new Rectangle(Mathf.Clamp(_splitterValue * Width - SplitterSizeHalf, 0.0f, Width), 0, SplitterSize, Height);
- LeftMargin = _splitterValue * Width + SplitterMargin;
+ _splitterRect = new Rectangle(Mathf.Clamp(_splitterValue * Width - SplitterSize * 0.5f, 0.0f, Width), 0, SplitterSize, Height);
+ LeftMargin = _splitterValue * Width + _spacing;
}
private void StartTracking()
@@ -222,23 +217,33 @@ namespace FlaxEditor.CustomEditors.GUI
///
protected override void PerformLayoutAfterChildren()
{
- // Sort controls from up to down into two columns: one for labels and one for the rest of the stuff
-
+ // Place non-label controls from top to down
float y = _margin.Top;
float w = Width - _margin.Width;
+ bool firstItem = true;
for (int i = 0; i < _children.Count; i++)
{
Control c = _children[i];
if (!(c is PropertyNameLabel))
{
- var h = c.Height;
- c.Bounds = new Rectangle(_margin.Left, y + _spacing, w, h);
+ var rect = new Rectangle(_margin.Left, y, w, c.Height);
+ if (c.Visible)
+ {
+ if (firstItem)
+ firstItem = false;
+ else
+ rect.Y += _spacing;
+ }
+ else if (!firstItem)
+ rect.Y += _spacing;
+ c.Bounds = rect;
if (c.Visible)
y = c.Bottom;
}
}
y += _margin.Bottom;
+ // Place labels accordingly to their respective controls placement
float namesWidth = _splitterValue * Width;
int count = _element.Labels.Count;
float[] yStarts = new float[count + 1];
@@ -271,7 +276,9 @@ namespace FlaxEditor.CustomEditors.GUI
{
var label = _element.Labels[i];
- var rect = new Rectangle(0, yStarts[i] + 1, namesWidth, yStarts[i + 1] - yStarts[i] - 2);
+ var rect = new Rectangle(0, yStarts[i], namesWidth, yStarts[i + 1] - yStarts[i]);
+ if (i != count - 1)
+ rect.Height -= _spacing;
//label.Parent = this;
label.Bounds = rect;
}
diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs
index 855a730df..68ac9f47a 100644
--- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs
+++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs
@@ -202,6 +202,17 @@ namespace FlaxEditor.CustomEditors
return element;
}
+ ///
+ /// Adds new uniform grid control.
+ ///
+ /// The created element.
+ public CustomElementsContainer UniformGrid()
+ {
+ var grid = CustomContainer();
+ grid.CustomControl.SlotSpacing = new Float2(Utilities.Constants.UIMargin);
+ return grid;
+ }
+
///
/// Adds new custom element.
///
diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs
index 394f907d9..c6ead7a76 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -1031,6 +1031,8 @@ namespace FlaxEditor
{
Internal_GetEditorBoxWithChildren(FlaxEngine.Object.GetUnmanagedPtr(actor), out var box);
BoundingSphere.FromBox(ref box, out sphere);
+ if (sphere == BoundingSphere.Empty)
+ sphere = new BoundingSphere(actor.Position, sphere.Radius);
sphere.Radius = Math.Max(sphere.Radius, 15.0f);
}
else
diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs
index bf425a46b..82699e55c 100644
--- a/Source/Editor/GUI/ItemsListContextMenu.cs
+++ b/Source/Editor/GUI/ItemsListContextMenu.cs
@@ -611,6 +611,16 @@ namespace FlaxEditor.GUI
OnClickItem(focusedItem);
return true;
}
+ 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();
+ if (visibleItems.Count > 0)
+ {
+ OnClickItem(visibleItems[0]);
+ return true;
+ }
+ }
break;
}
diff --git a/Source/Editor/GUI/Tabs/Tabs.cs b/Source/Editor/GUI/Tabs/Tabs.cs
index f2bd260d4..7555185e6 100644
--- a/Source/Editor/GUI/Tabs/Tabs.cs
+++ b/Source/Editor/GUI/Tabs/Tabs.cs
@@ -47,6 +47,7 @@ namespace FlaxEditor.GUI.Tabs
if (EnabledInHierarchy && Tab.Enabled)
{
Tabs.SelectedTab = Tab;
+ Tab.PerformLayout(true);
Tabs.Focus();
}
return true;
diff --git a/Source/Editor/GUI/Tree/Tree.cs b/Source/Editor/GUI/Tree/Tree.cs
index 02a9f47cb..e36f0ccd5 100644
--- a/Source/Editor/GUI/Tree/Tree.cs
+++ b/Source/Editor/GUI/Tree/Tree.cs
@@ -40,6 +40,7 @@ namespace FlaxEditor.GUI.Tree
private readonly bool _supportMultiSelect;
private Margin _margin;
private bool _autoSize = true;
+ private bool _deferLayoutUpdate = false;
///
/// The TreeNode that is being dragged over. This could have a value when not dragging.
@@ -66,6 +67,11 @@ namespace FlaxEditor.GUI.Tree
/// Gets the first selected node or null.
///
public TreeNode SelectedNode => Selection.Count > 0 ? Selection[0] : null;
+
+ ///
+ /// Allow nodes to Draw the root tree line.
+ ///
+ public bool DrawRootTreeLine = true;
///
/// Gets or sets the margin for the child tree nodes.
@@ -353,9 +359,25 @@ namespace FlaxEditor.GUI.Tree
BulkSelectUpdateExpanded(false);
}
+ ///
+ public override void PerformLayout(bool force = false)
+ {
+ if (_isLayoutLocked && !force)
+ return;
+
+ // In case the tree was fully expanded or collapsed along its children, avoid calculating the layout multiple times for each child
+ _deferLayoutUpdate = true;
+ }
+
///
public override void Update(float deltaTime)
{
+ if (_deferLayoutUpdate)
+ {
+ base.PerformLayout();
+ _deferLayoutUpdate = false;
+ }
+
var node = SelectedNode;
// Check if has focus and if any node is focused and it isn't a root
diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs
index eb7f345cf..40c276bf4 100644
--- a/Source/Editor/GUI/Tree/TreeNode.cs
+++ b/Source/Editor/GUI/Tree/TreeNode.cs
@@ -760,20 +760,21 @@ namespace FlaxEditor.GUI.Tree
// Show tree guidelines
if (Editor.Instance.Options.Options.Interface.ShowTreeLines)
{
- TreeNode parentNode = Parent as TreeNode;
+ ContainerControl parent = Parent;
+ TreeNode parentNode = parent as TreeNode;
bool thisNodeIsLast = false;
- while (parentNode != null && parentNode != ParentTree.Children[0])
+ while (parentNode != null && (parentNode != tree.Children[0] || tree.DrawRootTreeLine))
{
float bottomOffset = 0;
float topOffset = 0;
- if (Parent == parentNode && this == Parent.Children[0])
+ if (parent == parentNode && this == parent.Children[0])
topOffset = 2;
if (thisNodeIsLast && parentNode.Children.Count == 1)
bottomOffset = topOffset != 0 ? 4 : 2;
- if (Parent == parentNode && this == Parent.Children[Parent.Children.Count - 1] && !_opened)
+ if (parent == parentNode && this == parent.Children[^1] && !_opened)
{
thisNodeIsLast = true;
bottomOffset = topOffset != 0 ? 4 : 2;
@@ -784,6 +785,8 @@ namespace FlaxEditor.GUI.Tree
if (_iconCollaped.IsValid)
leftOffset += 18;
var lineRect1 = new Rectangle(parentNode.TextRect.Left - leftOffset, parentNode.HeaderRect.Top + topOffset, 1, parentNode.HeaderRect.Height - bottomOffset);
+ if (HasAnyVisibleChild && CustomArrowRect.HasValue && CustomArrowRect.Value.Intersects(lineRect1))
+ lineRect1 = Rectangle.Empty; // Skip drawing line if it's overlapping the arrow rectangle
Render2D.FillRectangle(lineRect1, isSelected ? style.ForegroundGrey : style.LightBackground);
parentNode = parentNode.Parent as TreeNode;
}
diff --git a/Source/Editor/Modules/PrefabsModule.cs b/Source/Editor/Modules/PrefabsModule.cs
index d1b254200..617202062 100644
--- a/Source/Editor/Modules/PrefabsModule.cs
+++ b/Source/Editor/Modules/PrefabsModule.cs
@@ -255,12 +255,17 @@ namespace FlaxEditor.Modules
// When applying changes to prefab from actor in level ignore it's root transformation (see ActorEditor.ProcessDiff)
var originalTransform = instance.LocalTransform;
+ var originalName = instance.Name;
if (instance.IsPrefabRoot && instance.HasScene)
+ {
instance.LocalTransform = prefab.GetDefaultInstance().Transform;
+ instance.Name = prefab.GetDefaultInstance().Name;
+ }
// Call backend
var failed = PrefabManager.Internal_ApplyAll(FlaxEngine.Object.GetUnmanagedPtr(instance));
instance.LocalTransform = originalTransform;
+ instance.Name = originalName;
if (failed)
throw new Exception("Failed to apply the prefab. See log to learn more.");
diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs
index 5c9d88f15..3c7130615 100644
--- a/Source/Editor/Modules/SceneEditingModule.cs
+++ b/Source/Editor/Modules/SceneEditingModule.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Actions;
using FlaxEditor.SceneGraph;
+using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
namespace FlaxEditor.Modules
@@ -561,7 +562,8 @@ namespace FlaxEditor.Modules
public void CreateParentForSelectedActors()
{
List selection = Editor.SceneEditing.Selection;
- var actors = selection.Where(x => x is ActorNode).Select(x => ((ActorNode)x).Actor);
+ // Get Actors but skip scene node
+ var actors = selection.Where(x => x is ActorNode and not SceneNode).Select(x => ((ActorNode)x).Actor);
var actorsCount = actors.Count();
if (actorsCount == 0)
return;
diff --git a/Source/Editor/Modules/SimulationModule.cs b/Source/Editor/Modules/SimulationModule.cs
index a7adc0a39..663c2ab32 100644
--- a/Source/Editor/Modules/SimulationModule.cs
+++ b/Source/Editor/Modules/SimulationModule.cs
@@ -306,19 +306,21 @@ namespace FlaxEditor.Modules
public override void OnPlayEnd()
{
var gameWin = Editor.Windows.GameWin;
-
- switch (gameWin.FocusOnPlayOption)
+ if (gameWin != null)
{
- case Options.InterfaceOptions.PlayModeFocus.None: break;
- case Options.InterfaceOptions.PlayModeFocus.GameWindow: break;
- case Options.InterfaceOptions.PlayModeFocus.GameWindowThenRestore:
- if (_previousWindow != null && !_previousWindow.IsDisposing)
+ switch (gameWin.FocusOnPlayOption)
{
- if (!Editor.Windows.GameWin.ParentDockPanel.ContainsTab(_previousWindow))
- break;
- _previousWindow.Focus();
+ case Options.InterfaceOptions.PlayModeFocus.None: break;
+ case Options.InterfaceOptions.PlayModeFocus.GameWindow: break;
+ case Options.InterfaceOptions.PlayModeFocus.GameWindowThenRestore:
+ if (_previousWindow != null && !_previousWindow.IsDisposing)
+ {
+ if (!Editor.Windows.GameWin.ParentDockPanel.ContainsTab(_previousWindow))
+ break;
+ _previousWindow.Focus();
+ }
+ break;
}
- break;
}
Editor.UI.UncheckPauseButton();
diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs
index cd580bfcf..191f3a975 100644
--- a/Source/Editor/Modules/UIModule.cs
+++ b/Source/Editor/Modules/UIModule.cs
@@ -621,19 +621,19 @@ namespace FlaxEditor.Modules
MenuWindow = MainMenu.AddButton("Window");
cm = MenuWindow.ContextMenu;
cm.VisibleChanged += OnMenuWindowVisibleChanged;
- cm.AddButton("Content", Editor.Windows.ContentWin.FocusOrShow);
- cm.AddButton("Scene", Editor.Windows.SceneWin.FocusOrShow);
- cm.AddButton("Toolbox", Editor.Windows.ToolboxWin.FocusOrShow);
- cm.AddButton("Properties", Editor.Windows.PropertiesWin.FocusOrShow);
- cm.AddButton("Game", Editor.Windows.GameWin.FocusOrShow);
- cm.AddButton("Editor", Editor.Windows.EditWin.FocusOrShow);
- cm.AddButton("Debug Log", Editor.Windows.DebugLogWin.FocusOrShow);
- cm.AddButton("Output Log", Editor.Windows.OutputLogWin.FocusOrShow);
- cm.AddButton("Graphics Quality", Editor.Windows.GraphicsQualityWin.FocusOrShow);
- cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow);
+ cm.AddButton("Content", inputOptions.ContentWindow,Editor.Windows.ContentWin.FocusOrShow);
+ cm.AddButton("Scene", inputOptions.SceneWindow, Editor.Windows.SceneWin.FocusOrShow);
+ cm.AddButton("Toolbox", inputOptions.ToolboxWindow, Editor.Windows.ToolboxWin.FocusOrShow);
+ cm.AddButton("Properties", inputOptions.PropertiesWindow, Editor.Windows.PropertiesWin.FocusOrShow);
+ cm.AddButton("Game", inputOptions.GameWindow, Editor.Windows.GameWin.FocusOrShow);
+ cm.AddButton("Editor", inputOptions.EditorWindow, Editor.Windows.EditWin.FocusOrShow);
+ cm.AddButton("Debug Log", inputOptions.DebugLogWindow, Editor.Windows.DebugLogWin.FocusOrShow);
+ cm.AddButton("Output Log", inputOptions.OutputLogWindow, Editor.Windows.OutputLogWin.FocusOrShow);
+ cm.AddButton("Graphics Quality", inputOptions.GraphicsQualityWindow, Editor.Windows.GraphicsQualityWin.FocusOrShow);
+ cm.AddButton("Game Cooker", inputOptions.GameCookerWindow, Editor.Windows.GameCookerWin.FocusOrShow);
cm.AddButton("Profiler", inputOptions.ProfilerWindow, Editor.Windows.ProfilerWin.FocusOrShow);
- cm.AddButton("Content Search", Editor.ContentFinding.ShowSearch);
- cm.AddButton("Visual Script Debugger", Editor.Windows.VisualScriptDebuggerWin.FocusOrShow);
+ cm.AddButton("Content Search", inputOptions.ContentSearchWindow, Editor.ContentFinding.ShowSearch);
+ cm.AddButton("Visual Script Debugger", inputOptions.VisualScriptDebuggerWindow, Editor.Windows.VisualScriptDebuggerWin.FocusOrShow);
cm.AddSeparator();
cm.AddButton("Save window layout", Editor.Windows.SaveLayout);
_menuWindowApplyWindowLayout = cm.AddChildMenu("Window layouts");
diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs
index 5168e03d0..ff7971667 100644
--- a/Source/Editor/Options/InputOptions.cs
+++ b/Source/Editor/Options/InputOptions.cs
@@ -33,6 +33,25 @@ namespace FlaxEditor.Options
OpenPrefab,
}
+ ///
+ /// Shortcut availability in play mode.
+ ///
+ public enum PlayModeShortcutAvailability
+ {
+ ///
+ /// None of the window shortcuts will be available in play mode.
+ ///
+ None,
+ ///
+ /// Only the profiler window shortcut will be available in play mode.
+ ///
+ ProfilerOnly,
+ ///
+ /// All window shortcuts will be available in play mode.
+ ///
+ All,
+ }
+
///
/// Input editor options data container.
///
@@ -40,6 +59,16 @@ namespace FlaxEditor.Options
[HideInEditor]
public sealed class InputOptions
{
+ ///
+ /// Gets a value based on the current settings that indicates wether window shortcuts will be avaliable during play mode.
+ ///
+ public static bool WindowShortcutsAvaliable => !Editor.IsPlayMode || Editor.Instance.Options.Options.Input.PlayModeWindowShortcutAvaliability == PlayModeShortcutAvailability.All;
+
+ ///
+ /// Gets a value based on the current settings that indicates wether the profiler window shortcut will be avaliable during play mode.
+ ///
+ public static bool ProfilerShortcutAvaliable => WindowShortcutsAvaliable || Editor.Instance.Options.Options.Input.PlayModeWindowShortcutAvaliability == PlayModeShortcutAvailability.ProfilerOnly;
+
#region Common
[DefaultValue(typeof(InputBinding), "Ctrl+S")]
@@ -230,9 +259,9 @@ namespace FlaxEditor.Options
#region Profiler
- [DefaultValue(typeof(InputBinding), "None")]
+ [DefaultValue(typeof(InputBinding), "Ctrl+Alpha7")]
[EditorDisplay("Profiler", "Open Profiler Window"), EditorOrder(630)]
- public InputBinding ProfilerWindow = new InputBinding(KeyboardKeys.None);
+ public InputBinding ProfilerWindow = new InputBinding(KeyboardKeys.Alpha7, KeyboardKeys.Control);
[DefaultValue(typeof(InputBinding), "None")]
[EditorDisplay("Profiler", "Start/Stop Profiler"), EditorOrder(631)]
@@ -356,24 +385,267 @@ namespace FlaxEditor.Options
#endregion
+ #region Debug Views
+
+ [DefaultValue(typeof(InputBinding), "Alt+Alpha4")]
+ [EditorDisplay("Debug Views"), EditorOrder(2000)]
+ public InputBinding Default = new InputBinding(KeyboardKeys.Alpha4, KeyboardKeys.Alt);
+
+ [DefaultValue(typeof(InputBinding), "Alt+Alpha3")]
+ [EditorDisplay("Debug Views"), EditorOrder(2010)]
+ public InputBinding Unlit = new InputBinding(KeyboardKeys.Alpha3, KeyboardKeys.Alt);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2020)]
+ public InputBinding NoPostFX = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "Alt+Alpha2")]
+ [EditorDisplay("Debug Views"), EditorOrder(2030)]
+ public InputBinding Wireframe = new InputBinding(KeyboardKeys.Alpha2, KeyboardKeys.Alt);
+
+ [DefaultValue(typeof(InputBinding), "Alt+Alpha5")]
+ [EditorDisplay("Debug Views"), EditorOrder(2040)]
+ public InputBinding LightBuffer = new InputBinding(KeyboardKeys.Alpha5, KeyboardKeys.Alt);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2050)]
+ public InputBinding ReflectionsBuffer = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2060)]
+ public InputBinding DepthBuffer = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2070)]
+ public InputBinding MotionVectors = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2080)]
+ public InputBinding LightmapUVDensity = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2090)]
+ public InputBinding VertexColors = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "Alt+Alpha1")]
+ [EditorDisplay("Debug Views"), EditorOrder(2100)]
+ public InputBinding PhysicsColliders = new InputBinding(KeyboardKeys.Alpha1, KeyboardKeys.Alt);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2110)]
+ public InputBinding LODPreview = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2120)]
+ public InputBinding MaterialComplexity = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2130)]
+ public InputBinding QuadOverdraw = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2140)]
+ public InputBinding GloablSDF = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2150)]
+ public InputBinding GlobalSurfaceAtlas = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Debug Views"), EditorOrder(2160)]
+ public InputBinding GlobalIllumination = new InputBinding(KeyboardKeys.None);
+
+ #endregion
+
+ #region View Flags
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3000)]
+ public InputBinding AntiAliasing = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3010)]
+ public InputBinding Shadows = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha7")]
+ [EditorDisplay("View Flags"), EditorOrder(3020)]
+ public InputBinding EditorSprites = new InputBinding(KeyboardKeys.Alpha7, KeyboardKeys.Control, KeyboardKeys.Shift);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3030)]
+ public InputBinding Reflections = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3040)]
+ public InputBinding ScreenSpaceReflections = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3050)]
+ public InputBinding AmbientOcclusion = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha6")]
+ [EditorDisplay("View Flags", "Global Illumination"), EditorOrder(3060)]
+ public InputBinding GlobalIlluminationViewFlag = new InputBinding(KeyboardKeys.Alpha6, KeyboardKeys.Control, KeyboardKeys.Shift);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3070)]
+ public InputBinding DirectionalLights = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3080)]
+ public InputBinding PointLights = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3090)]
+ public InputBinding SpotLights = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3100)]
+ public InputBinding SkyLights = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3110)]
+ public InputBinding Sky = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3120)]
+ public InputBinding Fog = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3130)]
+ public InputBinding SpecularLight = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3140)]
+ public InputBinding Decals = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha3")]
+ [EditorDisplay("View Flags"), EditorOrder(3150)]
+ public InputBinding CustomPostProcess = new InputBinding(KeyboardKeys.Alpha3, KeyboardKeys.Control, KeyboardKeys.Shift);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3160)]
+ public InputBinding Bloom = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3170)]
+ public InputBinding ToneMapping = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha2")]
+ [EditorDisplay("View Flags"), EditorOrder(3180)]
+ public InputBinding EyeAdaptation = new InputBinding(KeyboardKeys.Alpha2, KeyboardKeys.Control, KeyboardKeys.Shift);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3190)]
+ public InputBinding CameraArtifacts = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3200)]
+ public InputBinding LensFlares = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3210)]
+ public InputBinding DepthOfField = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3220)]
+ public InputBinding MotionBlur = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("View Flags"), EditorOrder(3230)]
+ public InputBinding ContactShadows = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha1")]
+ [EditorDisplay("View Flags"), EditorOrder(3240)]
+ public InputBinding PhysicsDebug = new InputBinding(KeyboardKeys.Alpha1, KeyboardKeys.Control, KeyboardKeys.Shift);
+
+ [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha5")]
+ [EditorDisplay("View Flags"), EditorOrder(3250)]
+ public InputBinding LightsDebug = new InputBinding(KeyboardKeys.Alpha5, KeyboardKeys.Control, KeyboardKeys.Shift);
+
+ [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha4")]
+ [EditorDisplay("View Flags"), EditorOrder(3260)]
+ public InputBinding DebugDraw = new InputBinding(KeyboardKeys.Alpha4, KeyboardKeys.Control, KeyboardKeys.Shift);
+
+ #endregion
+
#region Interface
[DefaultValue(typeof(InputBinding), "Ctrl+W")]
- [EditorDisplay("Interface"), EditorOrder(2000)]
+ [EditorDisplay("Interface"), EditorOrder(3500)]
public InputBinding CloseTab = new InputBinding(KeyboardKeys.W, KeyboardKeys.Control);
[DefaultValue(typeof(InputBinding), "Ctrl+Tab")]
- [EditorDisplay("Interface"), EditorOrder(2010)]
+ [EditorDisplay("Interface"), EditorOrder(3510)]
public InputBinding NextTab = new InputBinding(KeyboardKeys.Tab, KeyboardKeys.Control);
[DefaultValue(typeof(InputBinding), "Shift+Ctrl+Tab")]
- [EditorDisplay("Interface"), EditorOrder(2020)]
+ [EditorDisplay("Interface"), EditorOrder(3520)]
public InputBinding PreviousTab = new InputBinding(KeyboardKeys.Tab, KeyboardKeys.Control, KeyboardKeys.Shift);
[DefaultValue(SceneNodeDoubleClick.Expand)]
- [EditorDisplay("Interface"), EditorOrder(2030)]
+ [EditorDisplay("Interface"), EditorOrder(3530)]
public SceneNodeDoubleClick DoubleClickSceneNode = SceneNodeDoubleClick.Expand;
#endregion
+
+ #region Windows
+
+ ///
+ /// Gets or sets a value indicating what window shortcuts will be available during play mode.
+ ///
+ [DefaultValue(PlayModeShortcutAvailability.ProfilerOnly)]
+ [EditorDisplay("Windows", "Avaliability in Play Mode"), EditorOrder(3000)]
+ public PlayModeShortcutAvailability PlayModeWindowShortcutAvaliability { get; set; } = PlayModeShortcutAvailability.ProfilerOnly;
+
+ [DefaultValue(typeof(InputBinding), "Ctrl+Alpha5")]
+ [EditorDisplay("Windows"), EditorOrder(3010)]
+ public InputBinding ContentWindow = new InputBinding(KeyboardKeys.Alpha5, KeyboardKeys.Control);
+
+ [DefaultValue(typeof(InputBinding), "Ctrl+Alpha4")]
+ [EditorDisplay("Windows"), EditorOrder(3020)]
+ public InputBinding SceneWindow = new InputBinding(KeyboardKeys.Alpha4, KeyboardKeys.Control);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Windows"), EditorOrder(3030)]
+ public InputBinding ToolboxWindow = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "Ctrl+Alpha3")]
+ [EditorDisplay("Windows"), EditorOrder(3040)]
+ public InputBinding PropertiesWindow = new InputBinding(KeyboardKeys.Alpha3, KeyboardKeys.Control);
+
+ [DefaultValue(typeof(InputBinding), "Ctrl+Alpha2")]
+ [EditorDisplay("Windows"), EditorOrder(3050)]
+ public InputBinding GameWindow = new InputBinding(KeyboardKeys.Alpha2, KeyboardKeys.Control);
+
+ [DefaultValue(typeof(InputBinding), "Ctrl+Alpha1")]
+ [EditorDisplay("Windows"), EditorOrder(3060)]
+ public InputBinding EditorWindow = new InputBinding(KeyboardKeys.Alpha1, KeyboardKeys.Control);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Windows"), EditorOrder(3070)]
+ public InputBinding DebugLogWindow = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Windows"), EditorOrder(3080)]
+ public InputBinding OutputLogWindow = new InputBinding(KeyboardKeys.C, KeyboardKeys.Control, KeyboardKeys.Shift);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Windows"), EditorOrder(3090)]
+ public InputBinding GraphicsQualityWindow = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Windows"), EditorOrder(4000)]
+ public InputBinding GameCookerWindow = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Windows"), EditorOrder(4010)]
+ public InputBinding ContentSearchWindow = new InputBinding(KeyboardKeys.None);
+
+ [DefaultValue(typeof(InputBinding), "None")]
+ [EditorDisplay("Windows"), EditorOrder(4020)]
+ public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None);
+
+ #endregion
}
}
diff --git a/Source/Editor/SceneGraph/Actors/VideoPlayerEditor.cs b/Source/Editor/SceneGraph/Actors/VideoPlayerEditor.cs
index 7e50de084..e5f120e04 100644
--- a/Source/Editor/SceneGraph/Actors/VideoPlayerEditor.cs
+++ b/Source/Editor/SceneGraph/Actors/VideoPlayerEditor.cs
@@ -28,7 +28,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
_infoLabel = playbackGroup.Label(string.Empty).Label;
_infoLabel.AutoHeight = true;
- var grid = playbackGroup.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = Button.DefaultHeight;
diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
index bf3f4b830..5f56e918a 100644
--- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
+++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
@@ -320,7 +320,7 @@ namespace FlaxEditor.SceneGraph.GUI
if (noFilter && actor != null)
{
// Pick the correct id when inside a prefab window.
- var id = actor.HasPrefabLink && actor.Scene.Scene == null ? actor.PrefabObjectID : actor.ID;
+ var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID;
isExpanded = Editor.Instance.ProjectCache.IsExpandedActor(ref id);
}
diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs
index 43a699bf3..f71f50596 100644
--- a/Source/Editor/Surface/Archetypes/Animation.cs
+++ b/Source/Editor/Surface/Archetypes/Animation.cs
@@ -573,11 +573,11 @@ namespace FlaxEditor.Surface.Archetypes
"Blend animation poses (with additive mode)" +
"\n" +
"\nNote: " +
- "\nOrder of nodes matters, because Additive animation is appplayed on top of curent frame." +
+ "\nThe order of the nodes is important, because additive animation is applied on top of current frame." +
"\n" +
"\nTip for blender users:" +
- "\nInside NLA the the order is bottom (first node in flax) to the top (last node in flax)" +
- "\n​u need to place it in this order to get correct resoults",
+ "\nInside NLA the the order is bottom (first node in flax) to the top (last node in flax)." +
+ "\nYou need to place animations in this order to get correct results.",
Flags = NodeFlags.AnimGraph,
Size = new Float2(170, 80),
DefaultValues = new object[]
diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs
index 831ce8d9a..37d6801a4 100644
--- a/Source/Editor/Surface/Archetypes/Material.cs
+++ b/Source/Editor/Surface/Archetypes/Material.cs
@@ -607,14 +607,14 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Terrain Layer Weight",
Description = "Terrain layer weight mask used for blending terrain layers",
Flags = NodeFlags.MaterialGraph,
- Size = new Float2(220, 30),
+ Size = new Float2(200, 30),
DefaultValues = new object[]
{
0,
},
Elements = new[]
{
- NodeElementArchetype.Factory.ComboBox(0, 0, 70.0f, 0, LayersAndTagsSettings.GetCurrentTerrainLayers()),
+ NodeElementArchetype.Factory.ComboBox(0, 0, 175.0f, 0, LayersAndTagsSettings.GetCurrentTerrainLayers()),
NodeElementArchetype.Factory.Output(0, "", typeof(float), 0),
}
},
diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs
index 59f379bde..8a42a7a92 100644
--- a/Source/Editor/Surface/SurfaceNode.cs
+++ b/Source/Editor/Surface/SurfaceNode.cs
@@ -409,7 +409,7 @@ namespace FlaxEditor.Surface
///
/// Called after adding the control to the surface after paste.
///
- /// The nodes IDs mapping (original node ID to pasted node ID). Can be sued to update internal node's data after paste operation from the original data.
+ /// The nodes IDs mapping (original node ID to pasted node ID). Can be used to update internal node's data after paste operation from the original data.
public virtual void OnPasted(System.Collections.Generic.Dictionary idsMapping)
{
}
diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs
index e49bbc047..95610b73c 100644
--- a/Source/Editor/Surface/VisjectSurface.Input.cs
+++ b/Source/Editor/Surface/VisjectSurface.Input.cs
@@ -369,24 +369,14 @@ namespace FlaxEditor.Surface
}
// Change scale (disable scaling during selecting nodes)
- if (IsMouseOver && !_leftMouseDown && !IsPrimaryMenuOpened)
+ if (IsMouseOver && !_leftMouseDown && !_rightMouseDown && !IsPrimaryMenuOpened)
{
var nextViewScale = ViewScale + delta * 0.1f;
- if (delta > 0 && !_rightMouseDown)
- {
- // Scale towards mouse when zooming in
- var nextCenterPosition = ViewPosition + location / ViewScale;
- ViewScale = nextViewScale;
- ViewPosition = nextCenterPosition - (location / ViewScale);
- }
- else
- {
- // Scale while keeping center position when zooming out or when dragging view
- var viewCenter = ViewCenterPosition;
- ViewScale = nextViewScale;
- ViewCenterPosition = viewCenter;
- }
+ // Scale towards/ away from mouse when zooming in/ out
+ var nextCenterPosition = ViewPosition + location / ViewScale;
+ ViewScale = nextViewScale;
+ ViewPosition = nextCenterPosition - (location / ViewScale);
return true;
}
diff --git a/Source/Editor/Utilities/Constants.cs b/Source/Editor/Utilities/Constants.cs
index cbdb22a66..40fbbb5d7 100644
--- a/Source/Editor/Utilities/Constants.cs
+++ b/Source/Editor/Utilities/Constants.cs
@@ -20,5 +20,7 @@ namespace FlaxEditor.Utilities
#else
public const string ShowInExplorer = "Show in explorer";
#endif
+
+ public const float UIMargin = 3.0f;
}
}
diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs
index b4666b213..179e50ebb 100644
--- a/Source/Editor/Utilities/Utils.cs
+++ b/Source/Editor/Utilities/Utils.cs
@@ -402,7 +402,7 @@ namespace FlaxEditor.Utilities
///
/// Creates an Import path ui that show the asset import path and adds a button to show the folder in the file system.
///
- /// The parent layout container.
+ /// The parent layout element.
/// The asset item to get the import path of.
public static void CreateImportPathUI(CustomEditors.LayoutElementsContainer parentLayout, Content.BinaryAssetItem assetItem)
{
@@ -413,21 +413,16 @@ namespace FlaxEditor.Utilities
///
/// Creates an Import path ui that show the import path and adds a button to show the folder in the file system.
///
- /// The parent layout container.
+ /// The parent layout element.
/// The import path.
- /// Whether to use an initial layout space of 5 for separation.
- public static void CreateImportPathUI(CustomEditors.LayoutElementsContainer parentLayout, string path, bool useInitialSpacing = true)
+ public static void CreateImportPathUI(CustomEditors.LayoutElementsContainer parentLayout, string path)
{
if (!string.IsNullOrEmpty(path))
{
- if (useInitialSpacing)
- parentLayout.Space(5);
- parentLayout.Label("Import Path:").Label.TooltipText = "Source asset path (can be relative or absolute to the project)";
var textBox = parentLayout.TextBox().TextBox;
- textBox.TooltipText = "Path is not editable here.";
+ textBox.TooltipText = "Source asset path. Can be relative or absolute to the project. Path is not editable here.";
textBox.IsReadOnly = true;
textBox.Text = path;
- parentLayout.Space(2);
var button = parentLayout.Button(Constants.ShowInExplorer).Button;
button.Clicked += () => FileSystem.ShowFileExplorer(Path.GetDirectoryName(path));
}
@@ -1505,7 +1500,6 @@ namespace FlaxEditor.Utilities
inputActions.Add(options => options.BuildNav, Editor.Instance.BuildNavMesh);
inputActions.Add(options => options.BuildSDF, Editor.Instance.BuildAllMeshesSDF);
inputActions.Add(options => options.TakeScreenshot, Editor.Instance.Windows.TakeScreenshot);
- inputActions.Add(options => options.ProfilerWindow, () => Editor.Instance.Windows.ProfilerWin.FocusOrShow());
#if USE_PROFILER
inputActions.Add(options => options.ProfilerStartStop, () =>
{
diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs
index 668dc26af..738e468b8 100644
--- a/Source/Editor/Viewport/EditorViewport.cs
+++ b/Source/Editor/Viewport/EditorViewport.cs
@@ -916,7 +916,7 @@ namespace FlaxEditor.Viewport
for (int i = 0; i < ViewFlagsValues.Length; i++)
{
var v = ViewFlagsValues[i];
- var button = viewFlags.AddButton(v.Name);
+ var button = viewFlags.AddButton(v.Name, v.InputBinding.ToString());
button.CloseMenuOnClick = false;
button.Tag = v.Mode;
}
@@ -965,7 +965,7 @@ namespace FlaxEditor.Viewport
}
else
{
- var button = debugView.AddButton(v.Name);
+ var button = debugView.AddButton(v.Name, v.InputBinding.ToString());
button.CloseMenuOnClick = false;
button.Tag = v.Mode;
}
@@ -1011,16 +1011,64 @@ namespace FlaxEditor.Viewport
#endregion View mode widget
}
+ // Viewpoints
InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Top").Orientation)));
InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation)));
InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Front").Orientation)));
InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Back").Orientation)));
InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Right").Orientation)));
InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Left").Orientation)));
+ // Editor camera
InputActions.Add(options => options.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown);
InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1));
InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1));
InputActions.Add(options => options.ToggleOrthographic, () => OnOrthographicModeToggled(null));
+ // Debug views
+ InputActions.Add(options => options.Default, () => Task.ViewMode = ViewMode.Default);
+ InputActions.Add(options => options.Unlit, () => Task.ViewMode = ViewMode.Unlit);
+ InputActions.Add(options => options.NoPostFX, () => Task.ViewMode = ViewMode.NoPostFx);
+ InputActions.Add(options => options.Wireframe, () => Task.ViewMode = ViewMode.Wireframe);
+ InputActions.Add(options => options.LightBuffer, () => Task.ViewMode = ViewMode.LightBuffer);
+ InputActions.Add(options => options.ReflectionsBuffer, () => Task.ViewMode = ViewMode.Reflections);
+ InputActions.Add(options => options.DepthBuffer, () => Task.ViewMode = ViewMode.Depth);
+ InputActions.Add(options => options.MotionVectors, () => Task.ViewMode = ViewMode.MotionVectors);
+ InputActions.Add(options => options.LightmapUVDensity, () => Task.ViewMode = ViewMode.LightmapUVsDensity);
+ InputActions.Add(options => options.VertexColors, () => Task.ViewMode = ViewMode.VertexColors);
+ InputActions.Add(options => options.PhysicsColliders, () => Task.ViewMode = ViewMode.PhysicsColliders);
+ InputActions.Add(options => options.LODPreview, () => Task.ViewMode = ViewMode.LODPreview);
+ InputActions.Add(options => options.MaterialComplexity, () => Task.ViewMode = ViewMode.MaterialComplexity);
+ InputActions.Add(options => options.QuadOverdraw, () => Task.ViewMode = ViewMode.QuadOverdraw);
+ InputActions.Add(options => options.GloablSDF, () => Task.ViewMode = ViewMode.GlobalSDF);
+ InputActions.Add(options => options.GlobalSurfaceAtlas, () => Task.ViewMode = ViewMode.GlobalSurfaceAtlas);
+ InputActions.Add(options => options.GlobalIllumination, () => Task.ViewMode = ViewMode.GlobalIllumination);
+ // View flags
+ InputActions.Add(options => options.AntiAliasing, () => Task.ViewFlags ^= ViewFlags.AntiAliasing);
+ InputActions.Add(options => options.Shadows, () => Task.ViewFlags ^= ViewFlags.Shadows);
+ InputActions.Add(options => options.EditorSprites, () => Task.ViewFlags ^= ViewFlags.EditorSprites);
+ InputActions.Add(options => options.Reflections, () => Task.ViewFlags ^= ViewFlags.Reflections);
+ InputActions.Add(options => options.ScreenSpaceReflections, () => Task.ViewFlags ^= ViewFlags.SSR);
+ InputActions.Add(options => options.AmbientOcclusion, () => Task.ViewFlags ^= ViewFlags.AO);
+ InputActions.Add(options => options.GlobalIllumination, () => Task.ViewFlags ^= ViewFlags.GI);
+ InputActions.Add(options => options.DirectionalLights, () => Task.ViewFlags ^= ViewFlags.DirectionalLights);
+ InputActions.Add(options => options.PointLights, () => Task.ViewFlags ^= ViewFlags.PointLights);
+ InputActions.Add(options => options.SpotLights, () => Task.ViewFlags ^= ViewFlags.SpotLights);
+ InputActions.Add(options => options.SkyLights, () => Task.ViewFlags ^= ViewFlags.SkyLights);
+ InputActions.Add(options => options.Sky, () => Task.ViewFlags ^= ViewFlags.Sky);
+ InputActions.Add(options => options.Fog, () => Task.ViewFlags ^= ViewFlags.Fog);
+ InputActions.Add(options => options.SpecularLight, () => Task.ViewFlags ^= ViewFlags.SpecularLight);
+ InputActions.Add(options => options.Decals, () => Task.ViewFlags ^= ViewFlags.Decals);
+ InputActions.Add(options => options.CustomPostProcess, () => Task.ViewFlags ^= ViewFlags.CustomPostProcess);
+ InputActions.Add(options => options.Bloom, () => Task.ViewFlags ^= ViewFlags.Bloom);
+ InputActions.Add(options => options.ToneMapping, () => Task.ViewFlags ^= ViewFlags.ToneMapping);
+ InputActions.Add(options => options.EyeAdaptation, () => Task.ViewFlags ^= ViewFlags.EyeAdaptation);
+ InputActions.Add(options => options.CameraArtifacts, () => Task.ViewFlags ^= ViewFlags.CameraArtifacts);
+ InputActions.Add(options => options.LensFlares, () => Task.ViewFlags ^= ViewFlags.LensFlares);
+ InputActions.Add(options => options.DepthOfField, () => Task.ViewFlags ^= ViewFlags.DepthOfField);
+ InputActions.Add(options => options.MotionBlur, () => Task.ViewFlags ^= ViewFlags.MotionBlur);
+ InputActions.Add(options => options.ContactShadows, () => Task.ViewFlags ^= ViewFlags.ContactShadows);
+ InputActions.Add(options => options.PhysicsDebug, () => Task.ViewFlags ^= ViewFlags.PhysicsDebug);
+ InputActions.Add(options => options.LightsDebug, () => Task.ViewFlags ^= ViewFlags.LightsDebug);
+ InputActions.Add(options => options.DebugDraw, () => Task.ViewFlags ^= ViewFlags.DebugDraw);
// Link for task event
task.Begin += OnRenderBegin;
@@ -1965,8 +2013,17 @@ namespace FlaxEditor.Viewport
{
public readonly string Name;
public readonly ViewMode Mode;
+ public readonly InputBinding InputBinding;
public readonly ViewModeOptions[] Options;
+ public ViewModeOptions(ViewMode mode, string name, InputBinding inputBinding)
+ {
+ Mode = mode;
+ Name = name;
+ InputBinding = inputBinding;
+ Options = null;
+ }
+
public ViewModeOptions(ViewMode mode, string name)
{
Mode = mode;
@@ -1984,13 +2041,13 @@ namespace FlaxEditor.Viewport
private static readonly ViewModeOptions[] ViewModeValues =
{
- new ViewModeOptions(ViewMode.Default, "Default"),
- new ViewModeOptions(ViewMode.Unlit, "Unlit"),
- new ViewModeOptions(ViewMode.NoPostFx, "No PostFx"),
- new ViewModeOptions(ViewMode.Wireframe, "Wireframe"),
- new ViewModeOptions(ViewMode.LightBuffer, "Light Buffer"),
- new ViewModeOptions(ViewMode.Reflections, "Reflections Buffer"),
- new ViewModeOptions(ViewMode.Depth, "Depth Buffer"),
+ new ViewModeOptions(ViewMode.Default, "Default", Editor.Instance.Options.Options.Input.Default),
+ new ViewModeOptions(ViewMode.Unlit, "Unlit", Editor.Instance.Options.Options.Input.Unlit),
+ new ViewModeOptions(ViewMode.NoPostFx, "No PostFx", Editor.Instance.Options.Options.Input.NoPostFX),
+ new ViewModeOptions(ViewMode.Wireframe, "Wireframe", Editor.Instance.Options.Options.Input.Wireframe),
+ new ViewModeOptions(ViewMode.LightBuffer, "Light Buffer", Editor.Instance.Options.Options.Input.LightBuffer),
+ new ViewModeOptions(ViewMode.Reflections, "Reflections Buffer", Editor.Instance.Options.Options.Input.ReflectionsBuffer),
+ new ViewModeOptions(ViewMode.Depth, "Depth Buffer", Editor.Instance.Options.Options.Input.DepthBuffer),
new ViewModeOptions("GBuffer", new[]
{
new ViewModeOptions(ViewMode.Diffuse, "Diffuse"),
@@ -2004,16 +2061,16 @@ namespace FlaxEditor.Viewport
new ViewModeOptions(ViewMode.Normals, "Normals"),
new ViewModeOptions(ViewMode.AmbientOcclusion, "Ambient Occlusion"),
}),
- new ViewModeOptions(ViewMode.MotionVectors, "Motion Vectors"),
- new ViewModeOptions(ViewMode.LightmapUVsDensity, "Lightmap UVs Density"),
- new ViewModeOptions(ViewMode.VertexColors, "Vertex Colors"),
- new ViewModeOptions(ViewMode.PhysicsColliders, "Physics Colliders"),
- new ViewModeOptions(ViewMode.LODPreview, "LOD Preview"),
- new ViewModeOptions(ViewMode.MaterialComplexity, "Material Complexity"),
- new ViewModeOptions(ViewMode.QuadOverdraw, "Quad Overdraw"),
- new ViewModeOptions(ViewMode.GlobalSDF, "Global SDF"),
- new ViewModeOptions(ViewMode.GlobalSurfaceAtlas, "Global Surface Atlas"),
- new ViewModeOptions(ViewMode.GlobalIllumination, "Global Illumination"),
+ new ViewModeOptions(ViewMode.MotionVectors, "Motion Vectors", Editor.Instance.Options.Options.Input.MotionVectors),
+ new ViewModeOptions(ViewMode.LightmapUVsDensity, "Lightmap UVs Density", Editor.Instance.Options.Options.Input.LightmapUVDensity),
+ new ViewModeOptions(ViewMode.VertexColors, "Vertex Colors", Editor.Instance.Options.Options.Input.VertexColors),
+ new ViewModeOptions(ViewMode.PhysicsColliders, "Physics Colliders", Editor.Instance.Options.Options.Input.PhysicsColliders),
+ new ViewModeOptions(ViewMode.LODPreview, "LOD Preview", Editor.Instance.Options.Options.Input.LODPreview),
+ new ViewModeOptions(ViewMode.MaterialComplexity, "Material Complexity", Editor.Instance.Options.Options.Input.MaterialComplexity),
+ new ViewModeOptions(ViewMode.QuadOverdraw, "Quad Overdraw", Editor.Instance.Options.Options.Input.QuadOverdraw),
+ new ViewModeOptions(ViewMode.GlobalSDF, "Global SDF", Editor.Instance.Options.Options.Input.GloablSDF),
+ new ViewModeOptions(ViewMode.GlobalSurfaceAtlas, "Global Surface Atlas", Editor.Instance.Options.Options.Input.GlobalSurfaceAtlas),
+ new ViewModeOptions(ViewMode.GlobalIllumination, "Global Illumination", Editor.Instance.Options.Options.Input.GlobalIllumination),
};
private void WidgetViewModeShowHideClicked(ContextMenuButton button)
@@ -2046,43 +2103,45 @@ namespace FlaxEditor.Viewport
{
public readonly ViewFlags Mode;
public readonly string Name;
+ public readonly InputBinding InputBinding;
- public ViewFlagOptions(ViewFlags mode, string name)
+ public ViewFlagOptions(ViewFlags mode, string name, InputBinding inputBinding)
{
Mode = mode;
Name = name;
+ InputBinding = inputBinding;
}
}
private static readonly ViewFlagOptions[] ViewFlagsValues =
{
- new ViewFlagOptions(ViewFlags.AntiAliasing, "Anti Aliasing"),
- new ViewFlagOptions(ViewFlags.Shadows, "Shadows"),
- new ViewFlagOptions(ViewFlags.EditorSprites, "Editor Sprites"),
- new ViewFlagOptions(ViewFlags.Reflections, "Reflections"),
- new ViewFlagOptions(ViewFlags.SSR, "Screen Space Reflections"),
- new ViewFlagOptions(ViewFlags.AO, "Ambient Occlusion"),
- new ViewFlagOptions(ViewFlags.GI, "Global Illumination"),
- new ViewFlagOptions(ViewFlags.DirectionalLights, "Directional Lights"),
- new ViewFlagOptions(ViewFlags.PointLights, "Point Lights"),
- new ViewFlagOptions(ViewFlags.SpotLights, "Spot Lights"),
- new ViewFlagOptions(ViewFlags.SkyLights, "Sky Lights"),
- new ViewFlagOptions(ViewFlags.Sky, "Sky"),
- new ViewFlagOptions(ViewFlags.Fog, "Fog"),
- new ViewFlagOptions(ViewFlags.SpecularLight, "Specular Light"),
- new ViewFlagOptions(ViewFlags.Decals, "Decals"),
- new ViewFlagOptions(ViewFlags.CustomPostProcess, "Custom Post Process"),
- new ViewFlagOptions(ViewFlags.Bloom, "Bloom"),
- new ViewFlagOptions(ViewFlags.ToneMapping, "Tone Mapping"),
- new ViewFlagOptions(ViewFlags.EyeAdaptation, "Eye Adaptation"),
- new ViewFlagOptions(ViewFlags.CameraArtifacts, "Camera Artifacts"),
- new ViewFlagOptions(ViewFlags.LensFlares, "Lens Flares"),
- new ViewFlagOptions(ViewFlags.DepthOfField, "Depth of Field"),
- new ViewFlagOptions(ViewFlags.MotionBlur, "Motion Blur"),
- new ViewFlagOptions(ViewFlags.ContactShadows, "Contact Shadows"),
- new ViewFlagOptions(ViewFlags.PhysicsDebug, "Physics Debug"),
- new ViewFlagOptions(ViewFlags.LightsDebug, "Lights Debug"),
- new ViewFlagOptions(ViewFlags.DebugDraw, "Debug Draw"),
+ new ViewFlagOptions(ViewFlags.AntiAliasing, "Anti Aliasing", Editor.Instance.Options.Options.Input.AntiAliasing),
+ new ViewFlagOptions(ViewFlags.Shadows, "Shadows", Editor.Instance.Options.Options.Input.Shadows),
+ new ViewFlagOptions(ViewFlags.EditorSprites, "Editor Sprites", Editor.Instance.Options.Options.Input.EditorSprites),
+ new ViewFlagOptions(ViewFlags.Reflections, "Reflections", Editor.Instance.Options.Options.Input.Reflections),
+ new ViewFlagOptions(ViewFlags.SSR, "Screen Space Reflections", Editor.Instance.Options.Options.Input.ScreenSpaceReflections),
+ new ViewFlagOptions(ViewFlags.AO, "Ambient Occlusion", Editor.Instance.Options.Options.Input.AmbientOcclusion),
+ new ViewFlagOptions(ViewFlags.GI, "Global Illumination", Editor.Instance.Options.Options.Input.GlobalIlluminationViewFlag),
+ new ViewFlagOptions(ViewFlags.DirectionalLights, "Directional Lights", Editor.Instance.Options.Options.Input.DirectionalLights),
+ new ViewFlagOptions(ViewFlags.PointLights, "Point Lights", Editor.Instance.Options.Options.Input.PointLights),
+ new ViewFlagOptions(ViewFlags.SpotLights, "Spot Lights", Editor.Instance.Options.Options.Input.SpotLights),
+ new ViewFlagOptions(ViewFlags.SkyLights, "Sky Lights", Editor.Instance.Options.Options.Input.SkyLights),
+ new ViewFlagOptions(ViewFlags.Sky, "Sky", Editor.Instance.Options.Options.Input.Sky),
+ new ViewFlagOptions(ViewFlags.Fog, "Fog", Editor.Instance.Options.Options.Input.Fog),
+ new ViewFlagOptions(ViewFlags.SpecularLight, "Specular Light", Editor.Instance.Options.Options.Input.SpecularLight),
+ new ViewFlagOptions(ViewFlags.Decals, "Decals", Editor.Instance.Options.Options.Input.Decals),
+ new ViewFlagOptions(ViewFlags.CustomPostProcess, "Custom Post Process", Editor.Instance.Options.Options.Input.CustomPostProcess),
+ new ViewFlagOptions(ViewFlags.Bloom, "Bloom", Editor.Instance.Options.Options.Input.Bloom),
+ new ViewFlagOptions(ViewFlags.ToneMapping, "Tone Mapping", Editor.Instance.Options.Options.Input.ToneMapping),
+ new ViewFlagOptions(ViewFlags.EyeAdaptation, "Eye Adaptation", Editor.Instance.Options.Options.Input.EyeAdaptation),
+ new ViewFlagOptions(ViewFlags.CameraArtifacts, "Camera Artifacts", Editor.Instance.Options.Options.Input.CameraArtifacts),
+ new ViewFlagOptions(ViewFlags.LensFlares, "Lens Flares", Editor.Instance.Options.Options.Input.LensFlares),
+ new ViewFlagOptions(ViewFlags.DepthOfField, "Depth of Field", Editor.Instance.Options.Options.Input.DepthOfField),
+ new ViewFlagOptions(ViewFlags.MotionBlur, "Motion Blur", Editor.Instance.Options.Options.Input.MotionBlur),
+ new ViewFlagOptions(ViewFlags.ContactShadows, "Contact Shadows", Editor.Instance.Options.Options.Input.ContactShadows),
+ new ViewFlagOptions(ViewFlags.PhysicsDebug, "Physics Debug", Editor.Instance.Options.Options.Input.PhysicsDebug),
+ new ViewFlagOptions(ViewFlags.LightsDebug, "Lights Debug", Editor.Instance.Options.Options.Input.LightsDebug),
+ new ViewFlagOptions(ViewFlags.DebugDraw, "Debug Draw", Editor.Instance.Options.Options.Input.DebugDraw),
};
private void WidgetViewFlagsShowHide(Control cm)
diff --git a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs
index 995f510a9..a678e868a 100644
--- a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs
+++ b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs
@@ -185,7 +185,7 @@ namespace FlaxEditor.Viewport.Previews
{
UseTimeScale = false,
UpdateWhenOffscreen = true,
- BoundsScale = 100.0f,
+ BoundsScale = 1.0f,
UpdateMode = AnimatedModel.AnimationUpdateMode.Manual,
};
Task.AddCustomActor(_previewModel);
diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs
index 1235bf62d..41fdfde65 100644
--- a/Source/Editor/Windows/Assets/AnimationWindow.cs
+++ b/Source/Editor/Windows/Assets/AnimationWindow.cs
@@ -231,7 +231,8 @@ namespace FlaxEditor.Windows.Assets
group.Object(importSettingsValues);
// Creates the import path UI
- Utilities.Utils.CreateImportPathUI(layout, proxy.Window.Item as BinaryAssetItem);
+ group = layout.Group("Import Path");
+ Utilities.Utils.CreateImportPathUI(group, proxy.Window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = layout.Button("Reimport");
diff --git a/Source/Editor/Windows/Assets/AudioClipWindow.cs b/Source/Editor/Windows/Assets/AudioClipWindow.cs
index 1c15ee96f..250a7eb77 100644
--- a/Source/Editor/Windows/Assets/AudioClipWindow.cs
+++ b/Source/Editor/Windows/Assets/AudioClipWindow.cs
@@ -6,6 +6,7 @@ using FlaxEditor.Content.Import;
using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.GUI;
+using FlaxEditor.Scripting;
using FlaxEditor.Viewport.Previews;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -76,7 +77,8 @@ namespace FlaxEditor.Windows.Assets
{
public override void Initialize(LayoutElementsContainer layout)
{
- var window = ((PropertiesProxy)Values[0])._window;
+ var proxy = ((PropertiesProxy)Values[0]);
+ var window = proxy._window;
if (window == null)
{
layout.Label("Loading...", TextAlignment.Center);
@@ -101,7 +103,8 @@ namespace FlaxEditor.Windows.Assets
base.Initialize(layout);
// Creates the import path UI
- Utilities.Utils.CreateImportPathUI(layout, window.Item as BinaryAssetItem);
+ var pathGroup = layout.Group("Import Path");
+ Utilities.Utils.CreateImportPathUI(pathGroup, window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = layout.Button("Reimport");
diff --git a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs
index 2fd71d860..2b86664f2 100644
--- a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs
+++ b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs
@@ -635,5 +635,11 @@ namespace FlaxEditor.Windows.Assets
public void Select(List nodes)
{
}
+
+ ///
+ public List Selection => new List();
+
+ ///
+ public bool LockSelection { get; set; }
}
}
diff --git a/Source/Editor/Windows/Assets/CollisionDataWindow.cs b/Source/Editor/Windows/Assets/CollisionDataWindow.cs
index 5158f6046..3f265eea9 100644
--- a/Source/Editor/Windows/Assets/CollisionDataWindow.cs
+++ b/Source/Editor/Windows/Assets/CollisionDataWindow.cs
@@ -102,6 +102,8 @@ namespace FlaxEditor.Windows.Assets
private class CookData : CreateFileEntry
{
+ public override bool CanBeCreated => true;
+
public PropertiesProxy Proxy;
public CollisionDataType Type;
public ModelBase Model;
diff --git a/Source/Editor/Windows/Assets/CubeTextureWindow.cs b/Source/Editor/Windows/Assets/CubeTextureWindow.cs
index c27061630..c4d7ab053 100644
--- a/Source/Editor/Windows/Assets/CubeTextureWindow.cs
+++ b/Source/Editor/Windows/Assets/CubeTextureWindow.cs
@@ -55,10 +55,11 @@ namespace FlaxEditor.Windows.Assets
base.Initialize(layout);
// Creates the import path UI
- Utilities.Utils.CreateImportPathUI(layout, window.Item as BinaryAssetItem);
+ var pathGroup = layout.Group("Import Path");
+ Utilities.Utils.CreateImportPathUI(pathGroup, window.Item as BinaryAssetItem);
- layout.Space(5);
- var reimportButton = layout.Button("Reimport");
+ pathGroup.Space(5);
+ var reimportButton = pathGroup.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
}
}
diff --git a/Source/Editor/Windows/Assets/ModelBaseWindow.cs b/Source/Editor/Windows/Assets/ModelBaseWindow.cs
index 3431dfd7f..1214e9427 100644
--- a/Source/Editor/Windows/Assets/ModelBaseWindow.cs
+++ b/Source/Editor/Windows/Assets/ModelBaseWindow.cs
@@ -754,17 +754,17 @@ namespace FlaxEditor.Windows.Assets
if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return;
- var group = layout.Group("Import Settings");
+ var importSettingsGroup = layout.Group("Import Settings");
var importSettingsField = typeof(ImportPropertiesProxyBase).GetField(nameof(ImportSettings), BindingFlags.NonPublic | BindingFlags.Instance);
var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings };
- group.Object(importSettingsValues);
+ importSettingsGroup.Object(importSettingsValues);
// Creates the import path UI
- Utilities.Utils.CreateImportPathUI(layout, proxy.Window.Item as BinaryAssetItem);
+ var group = layout.Group("Import Path");
+ Utilities.Utils.CreateImportPathUI(group, proxy.Window.Item as BinaryAssetItem);
- layout.Space(5);
- var reimportButton = group.Button("Reimport");
+ var reimportButton = importSettingsGroup.Button("Reimport");
reimportButton.Button.Clicked += () => ((ImportPropertiesProxyBase)Values[0]).Reimport();
}
}
diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs
index d28c0141b..9a0b1ad82 100644
--- a/Source/Editor/Windows/Assets/ModelWindow.cs
+++ b/Source/Editor/Windows/Assets/ModelWindow.cs
@@ -114,7 +114,7 @@ namespace FlaxEditor.Windows.Assets
lodIndex.IntValue.Value = sdf.Texture != null ? sdf.LOD : 6;
_sdfModelLodIndex = lodIndex;
- var buttons = group.CustomContainer();
+ var buttons = layout.UniformGrid();
var gridControl = buttons.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = Button.DefaultHeight;
diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs
index 95e6b7a46..24854afb9 100644
--- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs
+++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs
@@ -56,6 +56,7 @@ namespace FlaxEditor.Windows.Assets
public PrefabTree()
: base(true)
{
+ DrawRootTreeLine = false;
}
}
diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs b/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs
index f6d3f1669..03e2a9652 100644
--- a/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs
+++ b/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs
@@ -51,7 +51,7 @@ namespace FlaxEditor.Windows.Assets
/// The selection before the change.
public void OnSelectionChanged(SceneGraphNode[] before)
{
- if (LockSelectedObjects)
+ if (LockSelection)
return;
Undo.AddAction(new SelectionChangeAction(before, Selection.ToArray(), OnSelectionUndo));
diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs
index aa78c06a2..44c21d863 100644
--- a/Source/Editor/Windows/Assets/PrefabWindow.cs
+++ b/Source/Editor/Windows/Assets/PrefabWindow.cs
@@ -78,7 +78,7 @@ namespace FlaxEditor.Windows.Assets
///
/// Indication of if the prefab window selection is locked on specific objects.
///
- public bool LockSelectedObjects
+ public bool LockSelection
{
get => _lockSelection;
set
@@ -160,6 +160,7 @@ namespace FlaxEditor.Windows.Assets
AnchorPreset = AnchorPresets.HorizontalStretchMiddle,
Parent = headerPanel,
Bounds = new Rectangle(4, 4, headerPanel.Width - 8, 18),
+ TooltipText = "Search the prefab.\n\nYou can prefix your search with different search operators:\ns: -> Actor with script of type\na: -> Actor type\nc: -> Control type",
};
_searchBox.TextChanged += OnSearchBoxTextChanged;
diff --git a/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs b/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs
index 44016e57a..94deb18c0 100644
--- a/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs
+++ b/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs
@@ -119,9 +119,10 @@ namespace FlaxEditor.Windows.Assets
}
base.Initialize(layout);
-
+
// Creates the import path UI
- Utilities.Utils.CreateImportPathUI(layout, proxy._window.Item as BinaryAssetItem);
+ var group = layout.Group("Import Path");
+ Utilities.Utils.CreateImportPathUI(group, proxy._window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = layout.Button("Reimport");
diff --git a/Source/Editor/Windows/Assets/TextureWindow.cs b/Source/Editor/Windows/Assets/TextureWindow.cs
index 06c6120ad..865099aea 100644
--- a/Source/Editor/Windows/Assets/TextureWindow.cs
+++ b/Source/Editor/Windows/Assets/TextureWindow.cs
@@ -143,7 +143,8 @@ namespace FlaxEditor.Windows.Assets
base.Initialize(layout);
// Creates the import path UI
- Utilities.Utils.CreateImportPathUI(layout, proxy._window.Item as BinaryAssetItem);
+ var group = layout.Group("Import Path");
+ Utilities.Utils.CreateImportPathUI(group, proxy._window.Item as BinaryAssetItem);
// Reimport
layout.Space(5);
diff --git a/Source/Editor/Windows/Assets/VisualScriptWindow.cs b/Source/Editor/Windows/Assets/VisualScriptWindow.cs
index b38fa277a..756daf66a 100644
--- a/Source/Editor/Windows/Assets/VisualScriptWindow.cs
+++ b/Source/Editor/Windows/Assets/VisualScriptWindow.cs
@@ -413,7 +413,7 @@ namespace FlaxEditor.Windows.Assets
var group = layout.Group("Functions");
var nodes = window.VisjectSurface.Nodes;
- var grid = group.CustomContainer();
+ var grid = layout.UniformGrid();
var gridControl = grid.CustomControl;
gridControl.ClipChildren = false;
gridControl.Height = Button.DefaultHeight;
diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs
index f63db04bd..cf33d0b63 100644
--- a/Source/Editor/Windows/ContentWindow.cs
+++ b/Source/Editor/Windows/ContentWindow.cs
@@ -204,6 +204,7 @@ namespace FlaxEditor.Windows
// Content structure tree
_tree = new Tree(false)
{
+ DrawRootTreeLine = false,
Parent = _contentTreePanel,
};
_tree.SelectedChanged += OnTreeSelectionChanged;
diff --git a/Source/Editor/Windows/EditGameWindow.cs b/Source/Editor/Windows/EditGameWindow.cs
index 3600acbee..888dd4250 100644
--- a/Source/Editor/Windows/EditGameWindow.cs
+++ b/Source/Editor/Windows/EditGameWindow.cs
@@ -140,6 +140,7 @@ namespace FlaxEditor.Windows
: base(editor, true, ScrollBars.None)
{
Title = "Editor";
+ Icon = editor.Icons.Grid32;
// Create viewport
Viewport = new MainEditorGizmoViewport(editor)
diff --git a/Source/Editor/Windows/EditorWindow.cs b/Source/Editor/Windows/EditorWindow.cs
index f96ab0a78..f61f5cce6 100644
--- a/Source/Editor/Windows/EditorWindow.cs
+++ b/Source/Editor/Windows/EditorWindow.cs
@@ -2,6 +2,7 @@
using System;
using FlaxEditor.Content;
+using FlaxEditor.Options;
using FlaxEngine;
using FlaxEngine.GUI;
using DockWindow = FlaxEditor.GUI.Docking.DockWindow;
@@ -49,6 +50,73 @@ namespace FlaxEditor.Windows
}
});
+ // Set up editor window shortcuts
+ InputActions.Add(options => options.ContentWindow, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.Windows.ContentWin.FocusOrShow();
+ });
+ InputActions.Add(options => options.SceneWindow, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.Windows.SceneWin.FocusOrShow();
+ });
+ InputActions.Add(options => options.ToolboxWindow, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.Windows.ToolboxWin.FocusOrShow();
+ });
+ InputActions.Add(options => options.PropertiesWindow, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.Windows.PropertiesWin.FocusOrShow();
+ });
+ InputActions.Add(options => options.GameWindow, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.Windows.GameWin.FocusOrShow();
+ });
+ InputActions.Add(options => options.EditorWindow, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.Windows.EditWin.FocusOrShow();
+ });
+ InputActions.Add(options => options.DebugLogWindow, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.Windows.DebugLogWin.FocusOrShow();
+ });
+ InputActions.Add(options => options.OutputLogWindow, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.Windows.OutputLogWin.FocusOrShow();
+ });
+ InputActions.Add(options => options.GraphicsQualityWindow, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.Windows.GraphicsQualityWin.FocusOrShow();
+ });
+ InputActions.Add(options => options.GameCookerWindow, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.Windows.GameCookerWin.FocusOrShow();
+ });
+ InputActions.Add(options => options.ProfilerWindow, () =>
+ {
+ if (InputOptions.ProfilerShortcutAvaliable)
+ Editor.Windows.ProfilerWin.FocusOrShow();
+ });
+ InputActions.Add(options => options.ContentFinder, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.ContentFinding.ShowSearch();
+ });
+ InputActions.Add(options => options.VisualScriptDebuggerWindow, () =>
+ {
+ if (InputOptions.WindowShortcutsAvaliable)
+ Editor.Windows.VisualScriptDebuggerWin.FocusOrShow();
+ });
+
// Register
Editor.Windows.OnWindowAdd(this);
}
diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs
index 3558eecd0..2d86c86f2 100644
--- a/Source/Editor/Windows/GameWindow.cs
+++ b/Source/Editor/Windows/GameWindow.cs
@@ -292,7 +292,7 @@ namespace FlaxEditor.Windows
private class GameRoot : UIEditorRoot
{
internal bool Editable = true;
- public override bool EnableInputs => !Time.GamePaused && Editor.IsPlayMode && Editable;
+ public override bool EnableInputs => !Time.GamePaused && Editor.IsPlayMode;
public override bool EnableSelecting => (!Editor.IsPlayMode || Time.GamePaused) && Editable;
public override TransformGizmo TransformGizmo => Editor.Instance.MainTransformGizmo;
}
@@ -305,6 +305,7 @@ namespace FlaxEditor.Windows
: base(editor, true, ScrollBars.None)
{
Title = "Game";
+ Icon = editor.Icons.Play64;
AutoFocus = true;
var task = MainRenderTask.Instance;
diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs
index b087947dc..6fc0659d7 100644
--- a/Source/Editor/Windows/OutputLogWindow.cs
+++ b/Source/Editor/Windows/OutputLogWindow.cs
@@ -482,6 +482,7 @@ namespace FlaxEditor.Windows
: base(editor, true, ScrollBars.None)
{
Title = "Output Log";
+ Icon = editor.Icons.Info64;
ClipChildren = false;
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
@@ -983,6 +984,10 @@ namespace FlaxEditor.Windows
var cachedOutputTargetViewOffset = _output.TargetViewOffset;
var isBottomScroll = _vScroll.Value >= _vScroll.Maximum - (_scrollSize * 2) || wasEmpty;
_output.Text = _textBuffer.ToString();
+ if (_hScroll.Maximum <= 0.0)
+ cachedOutputTargetViewOffset.X = 0;
+ if (_vScroll.Maximum <= 0.0)
+ cachedOutputTargetViewOffset.Y = 0;
_output.TargetViewOffset = cachedOutputTargetViewOffset;
_textBufferCount = _entries.Count;
if (!_vScroll.IsThumbClicked)
diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs
index 811c34600..d034a058b 100644
--- a/Source/Editor/Windows/Profiler/CPU.cs
+++ b/Source/Editor/Windows/Profiler/CPU.cs
@@ -492,7 +492,7 @@ namespace FlaxEditor.Windows.Profiler
{
break;
}
- subEventsMemoryTotal += sub.ManagedMemoryAllocation + e.NativeMemoryAllocation;
+ subEventsMemoryTotal += sub.ManagedMemoryAllocation + sub.NativeMemoryAllocation;
}
string name = e.Name.Replace("::", ".");
diff --git a/Source/Editor/Windows/PropertiesWindow.cs b/Source/Editor/Windows/PropertiesWindow.cs
index 87474fc3a..e90003038 100644
--- a/Source/Editor/Windows/PropertiesWindow.cs
+++ b/Source/Editor/Windows/PropertiesWindow.cs
@@ -45,7 +45,7 @@ namespace FlaxEditor.Windows
///
/// Indication of if the properties window is locked on specific objects.
///
- public bool LockObjects
+ public bool LockSelection
{
get => _lockObjects;
set
@@ -66,6 +66,7 @@ namespace FlaxEditor.Windows
: base(editor, true, ScrollBars.Vertical)
{
Title = "Properties";
+ Icon = editor.Icons.Build64;
AutoFocus = true;
Presenter = new CustomEditorPresenter(editor.Undo, null, this);
@@ -86,9 +87,9 @@ namespace FlaxEditor.Windows
if (Level.ScenesCount > 1)
return;
_actorScrollValues.Clear();
- if (LockObjects)
+ if (LockSelection)
{
- LockObjects = false;
+ LockSelection = false;
Presenter.Deselect();
}
}
@@ -121,7 +122,7 @@ namespace FlaxEditor.Windows
private void OnSelectionChanged()
{
- if (LockObjects)
+ if (LockSelection)
return;
// Update selected objects
diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs
index 93774e514..f852b63c4 100644
--- a/Source/Editor/Windows/SceneTreeWindow.cs
+++ b/Source/Editor/Windows/SceneTreeWindow.cs
@@ -47,6 +47,7 @@ namespace FlaxEditor.Windows
: base(editor, true, ScrollBars.None)
{
Title = "Scene";
+ Icon = editor.Icons.Globe32;
// Scene searching query input box
var headerPanel = new ContainerControl
@@ -83,6 +84,7 @@ namespace FlaxEditor.Windows
{
Margin = new Margin(0.0f, 0.0f, -16.0f, _sceneTreePanel.ScrollBarsSize), // Hide root node
IsScrollable = true,
+ DrawRootTreeLine = false,
};
_tree.AddChild(root.TreeNode);
_tree.SelectedChanged += Tree_OnSelectedChanged;
diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs
index 81f7b94d4..3c9c605a4 100644
--- a/Source/Editor/Windows/ToolboxWindow.cs
+++ b/Source/Editor/Windows/ToolboxWindow.cs
@@ -455,6 +455,7 @@ namespace FlaxEditor.Windows
: base(editor, true, ScrollBars.None)
{
Title = "Toolbox";
+ Icon = editor.Icons.Toolbox96;
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
}
diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp
index c128a33a0..4130993eb 100644
--- a/Source/Engine/Content/Asset.cpp
+++ b/Source/Engine/Content/Asset.cpp
@@ -15,6 +15,8 @@
#if USE_EDITOR
+#include "Engine/Engine/Globals.h"
+
ThreadLocal ContentDeprecatedFlags;
void ContentDeprecated::Mark()
@@ -467,11 +469,13 @@ void Asset::CancelStreaming()
{
// Cancel loading task but go over asset locker to prevent case if other load threads still loads asset while it's reimported on other thread
Locker.Lock();
- auto loadTask = (ContentLoadTask*)Platform::AtomicRead(&_loadingTask);
+ auto loadingTask = (ContentLoadTask*)Platform::AtomicRead(&_loadingTask);
Locker.Unlock();
- if (loadTask)
+ if (loadingTask)
{
- loadTask->Cancel();
+ Platform::AtomicStore(&_loadingTask, 0);
+ LOG(Warning, "Cancel loading task for \'{0}\'", ToString());
+ loadingTask->Cancel();
}
}
@@ -590,7 +594,7 @@ bool Asset::onLoad(LoadAssetTask* task)
#if USE_EDITOR
// Auto-save deprecated assets to get rid of data in an old format
- if (isDeprecated && isLoaded)
+ if (isDeprecated && isLoaded && !IsVirtual() && !GetPath().StartsWith(StringUtils::GetDirectoryName(Globals::TemporaryFolder)))
{
PROFILE_CPU_NAMED("Asset.Save");
LOG(Info, "Resaving asset '{}' that uses deprecated data format", ToString());
@@ -632,18 +636,11 @@ void Asset::onUnload_MainThread()
ASSERT(IsInMainThread());
+ // Cancel any streaming before calling OnUnloaded event
+ CancelStreaming();
+
// Send event
OnUnloaded(this);
-
- // Check if is during loading
- auto loadingTask = (ContentLoadTask*)Platform::AtomicRead(&_loadingTask);
- if (loadingTask != nullptr)
- {
- // Cancel loading
- Platform::AtomicStore(&_loadingTask, 0);
- LOG(Warning, "Cancel loading task for \'{0}\'", ToString());
- loadingTask->Cancel();
- }
}
#if USE_EDITOR
diff --git a/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h b/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h
index de0c503bf..5c9ab5604 100644
--- a/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h
+++ b/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h
@@ -34,6 +34,10 @@ public:
public:
// [ContentLoadTask]
+ String ToString() const override
+ {
+ return String::Format(TEXT("Load Asset Data Task ({}, {}, {})"), (int32)GetState(), _chunks, _asset ? _asset->GetPath() : String::Empty);
+ }
bool HasReference(Object* obj) const override
{
return obj == _asset;
diff --git a/Source/Engine/ContentImporters/CreateSkeletonMask.h b/Source/Engine/ContentImporters/CreateSkeletonMask.h
index 3a88d4ca3..d785a789f 100644
--- a/Source/Engine/ContentImporters/CreateSkeletonMask.h
+++ b/Source/Engine/ContentImporters/CreateSkeletonMask.h
@@ -22,7 +22,7 @@ public:
static CreateAssetResult Create(CreateAssetContext& context)
{
// Base
- IMPORT_SETUP(SkeletonMask, 1);
+ IMPORT_SETUP(SkeletonMask, 2);
// Chunk 0
if (context.AllocateChunk(0))
diff --git a/Source/Engine/Core/Math/Double4x4.h b/Source/Engine/Core/Math/Double4x4.h
index b7c06e5c5..b8a20bf67 100644
--- a/Source/Engine/Core/Math/Double4x4.h
+++ b/Source/Engine/Core/Math/Double4x4.h
@@ -93,6 +93,9 @@ public:
// Calculates the inverse of the specified matrix.
static void Invert(const Double4x4& value, Double4x4& result);
+ // Creates a left-handed, look-at matrix.
+ static void LookAt(const Double3& eye, const Double3& target, const Double3& up, Double4x4& result);
+
// Calculates the product of two matrices.
static void Multiply(const Double4x4& left, const Double4x4& right, Double4x4& result);
diff --git a/Source/Engine/Core/Math/Matrix.cpp b/Source/Engine/Core/Math/Matrix.cpp
index 87d73ff59..dea1d77d2 100644
--- a/Source/Engine/Core/Math/Matrix.cpp
+++ b/Source/Engine/Core/Math/Matrix.cpp
@@ -1001,6 +1001,37 @@ void Double4x4::Invert(const Double4x4& value, Double4x4& result)
result.M44 = +d44 * det;
}
+void Double4x4::LookAt(const Double3& eye, const Double3& target, const Double3& up, Double4x4& result)
+{
+ Double3 xaxis, yaxis, zaxis;
+ Double3::Subtract(target, eye, zaxis);
+ zaxis.Normalize();
+ Double3::Cross(up, zaxis, xaxis);
+ xaxis.Normalize();
+ Double3::Cross(zaxis, xaxis, yaxis);
+
+ result.M11 = xaxis.X;
+ result.M21 = xaxis.Y;
+ result.M31 = xaxis.Z;
+
+ result.M12 = yaxis.X;
+ result.M22 = yaxis.Y;
+ result.M32 = yaxis.Z;
+
+ result.M13 = zaxis.X;
+ result.M23 = zaxis.Y;
+ result.M33 = zaxis.Z;
+
+ result.M14 = 0.0f;
+ result.M24 = 0.0f;
+ result.M34 = 0.0f;
+
+ result.M41 = -Double3::Dot(xaxis, eye);
+ result.M42 = -Double3::Dot(yaxis, eye);
+ result.M43 = -Double3::Dot(zaxis, eye);
+ result.M44 = 1.0f;
+}
+
void Double4x4::Multiply(const Double4x4& left, const Double4x4& right, Double4x4& result)
{
result.M11 = left.M11 * right.M11 + left.M12 * right.M21 + left.M13 * right.M31 + left.M14 * right.M41;
diff --git a/Source/Engine/Core/Math/Quaternion.cs b/Source/Engine/Core/Math/Quaternion.cs
index 935354354..9029ed365 100644
--- a/Source/Engine/Core/Math/Quaternion.cs
+++ b/Source/Engine/Core/Math/Quaternion.cs
@@ -1149,7 +1149,7 @@ namespace FlaxEngine
}
///
- /// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized.
+ /// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis. The input vectors don't need to be normalized.
///
/// The source vector.
/// The destination vector.
@@ -1179,7 +1179,7 @@ namespace FlaxEngine
}
///
- /// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized.
+ /// Gets the quaternion that will rotate the from into vector to, around their plan perpendicular axis. The input vectors don't need to be normalized.
///
/// The source vector.
/// The destination vector.
diff --git a/Source/Engine/Core/ScopeExit.h b/Source/Engine/Core/ScopeExit.h
new file mode 100644
index 000000000..37068a2ff
--- /dev/null
+++ b/Source/Engine/Core/ScopeExit.h
@@ -0,0 +1,36 @@
+// Copyright (c) Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#include "Core.h"
+
+template
+struct ScopeExit
+{
+ explicit ScopeExit(FuncType&& func)
+ : _func((FuncType&&)func)
+ {
+ }
+
+ ~ScopeExit()
+ {
+ _func();
+ }
+
+private:
+ FuncType _func;
+};
+
+namespace THelpers
+{
+ struct ScopeExitInternal
+ {
+ template
+ ScopeExit operator*(FuncType&& func)
+ {
+ return ScopeExit((FuncType&&)func);
+ }
+ };
+}
+
+#define SCOPE_EXIT const auto CONCAT_MACROS(__scopeExit, __LINE__) = THelpers::ScopeExitInternal() * [&]()
diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp
index a026267cc..2bb921f70 100644
--- a/Source/Engine/Debug/DebugDraw.cpp
+++ b/Source/Engine/Debug/DebugDraw.cpp
@@ -99,6 +99,7 @@ struct DebugGeometryBuffer
{
GPUBuffer* Buffer;
float TimeLeft;
+ bool Lines;
Matrix Transform;
};
@@ -234,6 +235,14 @@ void TeleportList(const Float3& delta, Array& list)
}
}
+void TeleportList(const Float3& delta, Array& list)
+{
+ for (auto& v : list)
+ {
+ v.Transform.SetTranslation(v.Transform.GetTranslation() + delta);
+ }
+}
+
struct DebugDrawData
{
Array GeometryBuffers;
@@ -302,6 +311,7 @@ struct DebugDrawData
void Teleport(const Float3& delta)
{
+ TeleportList(delta, GeometryBuffers);
TeleportList(delta, DefaultLines);
TeleportList(delta, OneFrameLines);
TeleportList(delta, DefaultTriangles);
@@ -810,6 +820,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
defaultWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultWireTriangles, Context->DebugDrawDefault.OneFrameWireTriangles);
{
PROFILE_CPU_NAMED("Flush");
+ ZoneValue(DebugDrawVB->Data.Count() / 1024); // Size in kB
DebugDrawVB->Flush(context);
}
}
@@ -869,8 +880,8 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
Matrix mvp;
Matrix::Multiply(geometry.Transform, vp, mvp);
Matrix::Transpose(mvp, tmp.ViewProjection);
+ auto state = data.EnableDepthTest ? (geometry.Lines ? &DebugDrawPsLinesDepthTest : &DebugDrawPsTrianglesDepthTest) : (geometry.Lines ? &DebugDrawPsLinesDefault : &DebugDrawPsTrianglesDefault);
context->UpdateCB(cb, &tmp);
- auto state = data.EnableDepthTest ? &DebugDrawPsLinesDepthTest : &DebugDrawPsLinesDefault;
context->SetState(state->Get(enableDepthWrite, true));
context->BindVB(ToSpan(&geometry.Buffer, 1));
context->Draw(0, geometry.Buffer->GetElementsCount());
@@ -918,8 +929,9 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
Matrix mvp;
Matrix::Multiply(geometry.Transform, vp, mvp);
Matrix::Transpose(mvp, tmp.ViewProjection);
+ auto state = geometry.Lines ? &DebugDrawPsLinesDefault : &DebugDrawPsTrianglesDefault;
context->UpdateCB(cb, &tmp);
- context->SetState(DebugDrawPsLinesDefault.Get(false, false));
+ context->SetState(state->Get(false, false));
context->BindVB(ToSpan(&geometry.Buffer, 1));
context->Draw(0, geometry.Buffer->GetElementsCount());
}
@@ -1164,6 +1176,7 @@ void DebugDraw::DrawLines(GPUBuffer* lines, const Matrix& transform, float durat
auto& geometry = debugDrawData.GeometryBuffers.AddOne();
geometry.Buffer = lines;
geometry.TimeLeft = duration;
+ geometry.Lines = true;
geometry.Transform = transform * Matrix::Translation(-Context->Origin);
}
@@ -1520,6 +1533,23 @@ void DebugDraw::DrawTriangles(const Span& vertices, const Matrix& transf
}
}
+void DebugDraw::DrawTriangles(GPUBuffer* triangles, const Matrix& transform, float duration, bool depthTest)
+{
+ if (triangles == nullptr || triangles->GetSize() == 0)
+ return;
+ if (triangles->GetSize() % (sizeof(Vertex) * 3) != 0)
+ {
+ DebugLog::ThrowException("Cannot draw debug lines with incorrect amount of items in array");
+ return;
+ }
+ auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
+ auto& geometry = debugDrawData.GeometryBuffers.AddOne();
+ geometry.Buffer = triangles;
+ geometry.TimeLeft = duration;
+ geometry.Lines = false;
+ geometry.Transform = transform * Matrix::Translation(-Context->Origin);
+}
+
void DebugDraw::DrawTriangles(const Array& vertices, const Color& color, float duration, bool depthTest)
{
DrawTriangles(Span(vertices.Get(), vertices.Count()), color, duration, depthTest);
diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h
index aa3836105..3b51c0e13 100644
--- a/Source/Engine/Debug/DebugDraw.h
+++ b/Source/Engine/Debug/DebugDraw.h
@@ -74,7 +74,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
API_FUNCTION() static bool CanClear(void* context = nullptr);
#endif
- // Gets the last view position when rendering the current context. Can be sued for custom culling or LODing when drawing more complex shapes.
+ // Gets the last view position when rendering the current context. Can be used for custom culling or LODing when drawing more complex shapes.
static Vector3 GetViewPos();
///
@@ -296,12 +296,21 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// Draws the triangles.
///
/// The triangle vertices list (must have multiple of 3 elements).
- /// The custom matrix used to transform all line vertices.
+ /// The custom matrix used to transform all triangle vertices.
/// 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 DrawTriangles(const Span& vertices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
+ ///
+ /// Draws the triangles using the provided vertex buffer that contains groups of 3 Vertex elements per-triangle.
+ ///
+ /// The GPU buffer with vertices for triangles (must have multiple of 3 elements).
+ /// The custom matrix used to transform all triangle vertices.
+ /// 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 DrawTriangles(GPUBuffer* triangles, const Matrix& transform, float duration = 0.0f, bool depthTest = true);
+
///
/// Draws the triangles.
///
@@ -315,7 +324,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// Draws the triangles.
///
/// The triangle vertices list (must have multiple of 3 elements).
- /// The custom matrix used to transform all line vertices.
+ /// The custom matrix used to transform all triangle vertices.
/// 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.
@@ -336,7 +345,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
///
/// The triangle vertices list.
/// The triangle indices list (must have multiple of 3 elements).
- /// The custom matrix used to transform all line vertices.
+ /// The custom matrix used to transform all triangle vertices.
/// 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.
@@ -357,7 +366,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
///
/// The triangle vertices list.
/// The triangle indices list (must have multiple of 3 elements).
- /// The custom matrix used to transform all line vertices.
+ /// The custom matrix used to transform all triangle vertices.
/// 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.
@@ -376,7 +385,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// Draws the triangles.
///
/// The triangle vertices list (must have multiple of 3 elements).
- /// The custom matrix used to transform all line vertices.
+ /// The custom matrix used to transform all triangle vertices.
/// 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.
@@ -395,7 +404,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// Draws the triangles.
///
/// The triangle vertices list (must have multiple of 3 elements).
- /// The custom matrix used to transform all line vertices.
+ /// The custom matrix used to transform all triangle vertices.
/// 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.
@@ -416,7 +425,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
///
/// The triangle vertices list.
/// The triangle indices list (must have multiple of 3 elements).
- /// The custom matrix used to transform all line vertices.
+ /// The custom matrix used to transform all triangle vertices.
/// 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.
@@ -437,7 +446,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
///
/// The triangle vertices list.
/// The triangle indices list (must have multiple of 3 elements).
- /// The custom matrix used to transform all line vertices.
+ /// The custom matrix used to transform all triangle vertices.
/// 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.
diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp
index 778b87090..3f1a7035e 100644
--- a/Source/Engine/Engine/Engine.cpp
+++ b/Source/Engine/Engine/Engine.cpp
@@ -443,6 +443,15 @@ bool Engine::IsEditor()
#endif
}
+bool Engine::IsPlayMode()
+{
+#if USE_EDITOR
+ return Editor::IsPlayMode;
+#else
+ return true;
+#endif
+}
+
int32 Engine::GetFramesPerSecond()
{
return EngineImpl::Fps;
diff --git a/Source/Engine/Engine/Engine.h b/Source/Engine/Engine/Engine.h
index e8b6fef2c..1af16a930 100644
--- a/Source/Engine/Engine/Engine.h
+++ b/Source/Engine/Engine/Engine.h
@@ -178,6 +178,11 @@ public:
///
API_PROPERTY() static bool IsEditor();
+ ///
+ /// Returns whether the editor is in play mode or will always return true in a shipped applications.
+ ///
+ API_PROPERTY() static bool IsPlayMode();
+
///
/// Gets the amount of frames rendered during last second known as Frames Per Second. User scripts updates or fixed updates for physics may run at a different frequency than scene rendering. Use this property to get an accurate amount of frames rendered during the last second.
///
diff --git a/Source/Engine/Engine/EngineService.cpp b/Source/Engine/Engine/EngineService.cpp
index 265509e6a..433cf089f 100644
--- a/Source/Engine/Engine/EngineService.cpp
+++ b/Source/Engine/Engine/EngineService.cpp
@@ -37,6 +37,21 @@ DEFINE_ENGINE_SERVICE_EVENT(LateFixedUpdate);
DEFINE_ENGINE_SERVICE_EVENT(Draw);
DEFINE_ENGINE_SERVICE_EVENT_INVERTED(BeforeExit);
+#if TRACY_ENABLE
+
+StringView FillEventNameBuffer(Char* buffer, StringView name, StringView postfix)
+{
+ int32 size = 0;
+ for (int32 j = 0; j < name.Length(); j++)
+ if (name[j] != ' ')
+ buffer[size++] = name[j];
+ Platform::MemoryCopy(buffer + size, postfix.Get(), (postfix.Length() + 1) * sizeof(Char));
+ size += postfix.Length();
+ return StringView(buffer, size);
+}
+
+#endif
+
EngineService::EngineServicesArray& EngineService::GetServices()
{
static EngineServicesArray Services;
@@ -78,14 +93,9 @@ void EngineService::OnInit()
const StringView name(service->Name);
#if TRACY_ENABLE
ZoneScoped;
- int32 nameBufferLength = 0;
Char nameBuffer[100];
- for (int32 j = 0; j < name.Length(); j++)
- if (name[j] != ' ')
- nameBuffer[nameBufferLength++] = name[j];
- Platform::MemoryCopy(nameBuffer + nameBufferLength, TEXT("::Init"), 7 * sizeof(Char));
- nameBufferLength += 7;
- ZoneName(nameBuffer, nameBufferLength);
+ StringView zoneName = FillEventNameBuffer(nameBuffer, name, StringView(TEXT("::Init"), 6));
+ ZoneName(zoneName.Get(), zoneName.Length());
#endif
LOG(Info, "Initialize {0}...", name);
service->IsInitialized = true;
@@ -114,15 +124,10 @@ void EngineService::OnDispose()
{
#if TRACY_ENABLE
ZoneScoped;
- const StringView name(service->Name);
- int32 nameBufferLength = 0;
Char nameBuffer[100];
- for (int32 j = 0; j < name.Length(); j++)
- if (name[j] != ' ')
- nameBuffer[nameBufferLength++] = name[j];
- Platform::MemoryCopy(nameBuffer + nameBufferLength, TEXT("::Dispose"), 10 * sizeof(Char));
- nameBufferLength += 10;
- ZoneName(nameBuffer, nameBufferLength);
+ const StringView name(service->Name);
+ StringView zoneName = FillEventNameBuffer(nameBuffer, name, StringView(TEXT("::Dispose"), 9));
+ ZoneName(zoneName.Get(), zoneName.Length());
#endif
service->IsInitialized = false;
service->Dispose();
diff --git a/Source/Engine/Graphics/Async/GPUTask.h b/Source/Engine/Graphics/Async/GPUTask.h
index d32b4a10a..261eda6f7 100644
--- a/Source/Engine/Graphics/Async/GPUTask.h
+++ b/Source/Engine/Graphics/Async/GPUTask.h
@@ -67,6 +67,14 @@ public:
return _type;
}
+ ///
+ /// Gets work synchronization start point
+ ///
+ FORCE_INLINE GPUSyncPoint GetSyncStart() const
+ {
+ return _syncPoint;
+ }
+
///
/// Gets work finish synchronization point
///
diff --git a/Source/Engine/Graphics/Async/GPUTasksContext.cpp b/Source/Engine/Graphics/Async/GPUTasksContext.cpp
index 887ee54be..fe2198865 100644
--- a/Source/Engine/Graphics/Async/GPUTasksContext.cpp
+++ b/Source/Engine/Graphics/Async/GPUTasksContext.cpp
@@ -36,7 +36,7 @@ GPUTasksContext::~GPUTasksContext()
if (task->GetSyncPoint() <= _currentSyncPoint && task->GetState() != TaskState::Finished)
{
if (!Engine::IsRequestingExit)
- LOG(Warning, "{0} has been canceled before a sync", task->ToString());
+ LOG(Warning, "'{0}' has been canceled before a sync", task->ToString());
task->CancelSync();
}
}
@@ -51,18 +51,15 @@ void GPUTasksContext::Run(GPUTask* task)
ASSERT(task != nullptr);
task->Execute(this);
- if (task->IsSyncing())
+ //if (task->GetSyncStart() != 0)
_tasksSyncing.Add(task);
}
void GPUTasksContext::OnCancelSync(GPUTask* task)
{
- ASSERT(task != nullptr);
-
_tasksSyncing.Remove(task);
-
if (!Engine::IsRequestingExit)
- LOG(Warning, "{0} has been canceled before a sync", task->ToString());
+ LOG(Warning, "'{0}' has been canceled before a sync", task->ToString());
}
void GPUTasksContext::OnFrameBegin()
diff --git a/Source/Engine/Graphics/Async/GPUTasksContext.h b/Source/Engine/Graphics/Async/GPUTasksContext.h
index 9afb9b575..57c4673e7 100644
--- a/Source/Engine/Graphics/Async/GPUTasksContext.h
+++ b/Source/Engine/Graphics/Async/GPUTasksContext.h
@@ -18,7 +18,7 @@ protected:
CriticalSection _locker;
GPUSyncPoint _currentSyncPoint;
int32 _totalTasksDoneCount = 0;
- Array> _tasksSyncing;
+ Array _tasksSyncing;
public:
///
diff --git a/Source/Engine/Graphics/Async/GPUTasksManager.cpp b/Source/Engine/Graphics/Async/GPUTasksManager.cpp
index e393d96ef..d7c9b6f76 100644
--- a/Source/Engine/Graphics/Async/GPUTasksManager.cpp
+++ b/Source/Engine/Graphics/Async/GPUTasksManager.cpp
@@ -54,9 +54,9 @@ void GPUTask::OnCancel()
if (IsSyncing())
{
// Task has been performed but is waiting for a CPU/GPU sync so we have to cancel that
- ASSERT(_context != nullptr);
_context->OnCancelSync(this);
_context = nullptr;
+ SetState(TaskState::Canceled);
}
// Base
diff --git a/Source/Engine/Graphics/Models/MeshBase.cpp b/Source/Engine/Graphics/Models/MeshBase.cpp
index 315d01251..62e65ee61 100644
--- a/Source/Engine/Graphics/Models/MeshBase.cpp
+++ b/Source/Engine/Graphics/Models/MeshBase.cpp
@@ -475,6 +475,12 @@ bool MeshBase::Init(uint32 vertices, uint32 triangles, const Array(vertices, triangles, (const Float3*)vbData[0], (const uint32*)ibData);
#endif
+ // Free old buffers
+ SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[0]);
+ SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[1]);
+ SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[2]);
+ SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
+
// Initialize
_vertexBuffers[0] = vertexBuffer0;
_vertexBuffers[1] = vertexBuffer1;
diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp
index 4d1e8bb14..87378ef9d 100644
--- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp
+++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp
@@ -322,15 +322,17 @@ class StreamTextureMipTask : public GPUUploadTextureMipTask
{
private:
StreamingTexture* _streamingTexture;
+ Task* _rootTask;
FlaxStorage::LockData _dataLock;
public:
- StreamTextureMipTask(StreamingTexture* texture, int32 mipIndex)
+ StreamTextureMipTask(StreamingTexture* texture, int32 mipIndex, Task* rootTask)
: GPUUploadTextureMipTask(texture->GetTexture(), mipIndex, Span(nullptr, 0), 0, 0, false)
, _streamingTexture(texture)
+ , _rootTask(rootTask ? rootTask : this)
, _dataLock(_streamingTexture->GetOwner()->LockData())
{
- _streamingTexture->_streamingTasks.Add(this);
+ _streamingTexture->_streamingTasks.Add(_rootTask);
_texture.Released.Bind(this);
}
@@ -341,7 +343,7 @@ private:
if (_streamingTexture)
{
ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker());
- _streamingTexture->_streamingTasks.Remove(this);
+ _streamingTexture->_streamingTasks.Remove(_rootTask);
_streamingTexture = nullptr;
}
}
@@ -393,7 +395,7 @@ protected:
if (_streamingTexture)
{
ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker());
- _streamingTexture->_streamingTasks.Remove(this);
+ _streamingTexture->_streamingTasks.Remove(_rootTask);
_streamingTexture = nullptr;
}
@@ -443,7 +445,7 @@ Task* StreamingTexture::CreateStreamingTask(int32 residency)
// Add upload data task
const int32 allocatedMipIndex = TotalIndexToTextureMipIndex(mipIndex);
- task = New(this, allocatedMipIndex);
+ task = New(this, allocatedMipIndex, result);
if (result)
result->ContinueWith(task);
else
diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp
index af40a5458..7863578c6 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.cpp
+++ b/Source/Engine/Level/Actors/AnimatedModel.cpp
@@ -766,7 +766,7 @@ void AnimatedModel::UpdateBounds()
// Apply margin based on model dimensions
const Vector3 modelBoxSize = modelBox.GetSize();
const Vector3 center = box.GetCenter();
- const Vector3 sizeHalf = Vector3::Max(box.GetSize() + modelBoxSize * 0.2f, modelBoxSize) * 0.5f;
+ const Vector3 sizeHalf = Vector3::Max(box.GetSize() + modelBoxSize * 0.2f, modelBoxSize) * (0.5f * BoundsScale);
_box = BoundingBox(center - sizeHalf, center + sizeHalf);
}
else
diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h
index ff84125fc..89124cb87 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.h
+++ b/Source/Engine/Level/Actors/AnimatedModel.h
@@ -96,7 +96,7 @@ public:
bool PerBoneMotionBlur = true;
///
- /// If true, animation speed will be affected by the global time scale parameter.
+ /// If true, animation speed will be affected by the global timescale parameter.
///
API_FIELD(Attributes="EditorOrder(30), DefaultValue(true), EditorDisplay(\"Skinned Model\")")
bool UseTimeScale = true;
@@ -108,7 +108,7 @@ public:
bool UpdateWhenOffscreen = false;
///
- /// The animation update delta time scale. Can be used to speed up animation playback or create slow motion effect.
+ /// The animation update delta timescale. Can be used to speed up animation playback or create slow motion effect.
///
API_FIELD(Attributes="EditorOrder(45), EditorDisplay(\"Skinned Model\")")
float UpdateSpeed = 1.0f;
@@ -120,7 +120,7 @@ public:
AnimationUpdateMode UpdateMode = AnimationUpdateMode::Auto;
///
- /// The master scale parameter for the actor bounding box. Helps reducing mesh flickering effect on screen edges.
+ /// The master scale parameter for the actor bounding box. Helps to reduce mesh flickering effect on screen edges.
///
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0), EditorDisplay(\"Skinned Model\")")
float BoundsScale = 1.5f;
@@ -388,7 +388,7 @@ public:
API_FUNCTION() void PauseSlotAnimation(const StringView& slotName, Animation* anim);
///
- /// Checks if the any animation playback is active on the any slot in Anim Graph (not paused).
+ /// Checks if any animation playback is active on any slot in Anim Graph (not paused).
///
API_FUNCTION() bool IsPlayingSlotAnimation();
diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp
index b059b9f40..ef8bd812a 100644
--- a/Source/Engine/Level/Actors/Camera.cpp
+++ b/Source/Engine/Level/Actors/Camera.cpp
@@ -3,6 +3,7 @@
#include "Camera.h"
#include "Engine/Level/SceneObjectsFactory.h"
#include "Engine/Core/Math/Matrix.h"
+#include "Engine/Core/Math/Double4x4.h"
#include "Engine/Core/Math/Viewport.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Content.h"
@@ -238,7 +239,7 @@ Ray Camera::ConvertMouseToRay(const Float2& mousePosition, const Viewport& viewp
viewport.Unproject(farPoint, ivp, farPoint);
Vector3 dir = Vector3::Normalize(farPoint - nearPoint);
- if (dir.IsZero())
+ if (dir.IsZero() || dir.IsNanOrInfinity())
return Ray::Identity;
return Ray(nearPoint, dir);
}
@@ -302,12 +303,15 @@ void Camera::GetMatrices(Matrix& view, Matrix& projection, const Viewport& viewp
}
// Create view matrix
- const Float3 direction = GetDirection();
- const Float3 position = _transform.Translation - origin;
- const Float3 target = position + direction;
- Float3 up;
- Float3::Transform(Float3::Up, GetOrientation(), up);
- Matrix::LookAt(position, target, up, view);
+ const Vector3 direction = Vector3::Transform(Vector3::Forward, GetOrientation());
+ const Vector3 position = _transform.Translation - origin;
+ const Vector3 target = position + direction;
+
+ Vector3 up;
+ Vector3::Transform(Vector3::Up, GetOrientation(), up);
+ Real4x4 viewResult;
+ Real4x4::LookAt(position, target, up, viewResult);
+ view = Matrix(viewResult);
}
#if USE_EDITOR
diff --git a/Source/Engine/Level/MeshReference.cs b/Source/Engine/Level/MeshReference.cs
new file mode 100644
index 000000000..14ef0c72b
--- /dev/null
+++ b/Source/Engine/Level/MeshReference.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Wojciech Figat. All rights reserved.
+
+
+using FlaxEngine.Json;
+
+namespace FlaxEngine
+{
+ partial class ModelInstanceActor
+ {
+ partial struct MeshReference : ICustomValueEquals
+ {
+ ///
+ public bool ValueEquals(object other)
+ {
+ var o = (MeshReference)other;
+ return JsonSerializer.ValueEquals(Actor, o.Actor) &&
+ LODIndex == o.LODIndex &&
+ MeshIndex == o.MeshIndex;
+ }
+ }
+ }
+}
diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp
index 0df0723a2..4409ae161 100644
--- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp
+++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp
@@ -1090,7 +1090,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
root = dynamic_cast(sceneObjects.Value->At(targetActorIdx));
}
- // Try using the first actor without a parent as a new ro0t
+ // Try using the first actor without a parent as a new root
for (int32 i = 1; i < sceneObjects->Count(); i++)
{
SceneObject* obj = sceneObjects.Value->At(i);
diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp
index f198a3b8f..39f87d561 100644
--- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp
+++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp
@@ -223,7 +223,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa
#endif
}
#endif
- ASSERT(!isnan(sphere.Radius) && !isinf(sphere.Radius) && !sphere.Center.IsNanOrInfinity());
+ CHECK_RETURN(!isnan(sphere.Radius) && !isinf(sphere.Radius) && !sphere.Center.IsNanOrInfinity(), false);
// Expand sphere based on the render modules rules (sprite or mesh size)
for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.RenderModules.Count(); moduleIndex++)
@@ -244,7 +244,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa
Vector2::Max(*((Vector2*)spriteSize), maxSpriteSize, maxSpriteSize);
spriteSize += stride;
}
- ASSERT(!maxSpriteSize.IsNanOrInfinity());
+ CHECK_RETURN(!maxSpriteSize.IsNanOrInfinity(), false);
// Enlarge the emitter bounds sphere
sphere.Radius += maxSpriteSize.MaxValue();
@@ -267,7 +267,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa
if (radius > maxRadius)
maxRadius = radius;
}
- ASSERT(!isnan(maxRadius) && !isinf(maxRadius));
+ CHECK_RETURN(!isnan(maxRadius) && !isinf(maxRadius), false);
// Enlarge the emitter bounds sphere
sphere.Radius += maxRadius;
@@ -315,7 +315,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa
maxRibbonWidth = Math::Max(*((float*)ribbonWidth), maxRibbonWidth);
ribbonWidth += stride;
}
- ASSERT(!isnan(maxRibbonWidth) && !isinf(maxRibbonWidth));
+ CHECK_RETURN(!isnan(maxRibbonWidth) && !isinf(maxRibbonWidth), false);
// Enlarge the emitter bounds sphere
sphere.Radius += maxRibbonWidth * 0.5f;
@@ -335,7 +335,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa
maxRadius = Math::Max(*((float*)radius), maxRadius);
radius += stride;
}
- ASSERT(!isnan(maxRadius) && !isinf(maxRadius));
+ CHECK_RETURN(!isnan(maxRadius) && !isinf(maxRadius), false);
}
else
{
diff --git a/Source/Engine/Physics/Actors/Cloth.cpp b/Source/Engine/Physics/Actors/Cloth.cpp
index 889a0874e..b184bfbda 100644
--- a/Source/Engine/Physics/Actors/Cloth.cpp
+++ b/Source/Engine/Physics/Actors/Cloth.cpp
@@ -335,6 +335,7 @@ void Cloth::DrawPhysicsDebug(RenderView& view)
#if WITH_CLOTH && COMPILE_WITH_DEBUG_DRAW
if (_cloth)
{
+ PROFILE_CPU();
const ModelInstanceActor::MeshReference mesh = GetMesh();
if (mesh.Actor == nullptr)
return;
diff --git a/Source/Engine/Physics/PhysicsSettings.h b/Source/Engine/Physics/PhysicsSettings.h
index 897d2d633..cfb1bc75e 100644
--- a/Source/Engine/Physics/PhysicsSettings.h
+++ b/Source/Engine/Physics/PhysicsSettings.h
@@ -59,7 +59,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class
public:
///
- /// The default gravity force value (in cm^2/s).
+ /// The default gravity value (in cm/(s^2)).
///
API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Simulation\")")
Vector3 DefaultGravity = Vector3(0, -981.0f, 0);
diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp
index 737cf7106..cf478abda 100644
--- a/Source/Engine/Render2D/Font.cpp
+++ b/Source/Engine/Render2D/Font.cpp
@@ -119,7 +119,7 @@ void Font::Invalidate()
_characters.Clear();
}
-void Font::ProcessText(const StringView& text, Array& outputLines, const TextLayoutOptions& layout)
+void Font::ProcessText(const StringView& text, Array>& outputLines, const TextLayoutOptions& layout)
{
int32 textLength = text.Length();
if (textLength == 0)
@@ -311,7 +311,7 @@ Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout
return Float2::Zero;
// Process text
- Array lines;
+ Array> lines;
ProcessText(text, lines, layout);
// Calculate bounds
@@ -332,7 +332,7 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te
return 0;
// Process text
- Array lines;
+ Array> lines;
ProcessText(text, lines, layout);
ASSERT(lines.HasItems());
float scale = layout.Scale / FontManager::FontScale;
@@ -417,7 +417,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
}
// Process text
- Array lines;
+ Array> lines;
ProcessText(text, lines, layout);
ASSERT(lines.HasItems());
float scale = layout.Scale / FontManager::FontScale;
diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h
index 4f8e3a347..fc40cc16c 100644
--- a/Source/Engine/Render2D/Font.h
+++ b/Source/Engine/Render2D/Font.h
@@ -344,7 +344,7 @@ public:
/// The input text.
/// The layout properties.
/// The output lines list.
- void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout);
+ void ProcessText(const StringView& text, Array>& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout);
///
/// Processes text to get cached lines for rendering.
@@ -352,9 +352,9 @@ public:
/// The input text.
/// The layout properties.
/// The output lines list.
- API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout)
+ API_FUNCTION() Array> ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout)
{
- Array lines;
+ Array> lines;
ProcessText(text, lines, layout);
return lines;
}
@@ -366,9 +366,9 @@ public:
/// The input text range (substring range of the input text parameter).
/// The layout properties.
/// The output lines list.
- API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout)
+ API_FUNCTION() Array> ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout)
{
- Array lines;
+ Array> lines;
ProcessText(textRange.Substring(text), lines, layout);
return lines;
}
@@ -378,7 +378,7 @@ public:
///
/// The input text.
/// The output lines list.
- API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text)
+ API_FUNCTION() FORCE_INLINE Array> ProcessText(const StringView& text)
{
return ProcessText(text, TextLayoutOptions());
}
@@ -389,7 +389,7 @@ public:
/// The input text.
/// The input text range (substring range of the input text parameter).
/// The output lines list.
- API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange)
+ API_FUNCTION() FORCE_INLINE Array> ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange)
{
return ProcessText(textRange.Substring(text), TextLayoutOptions());
}
diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp
index 285428f7a..057265aae 100644
--- a/Source/Engine/Render2D/Render2D.cpp
+++ b/Source/Engine/Render2D/Render2D.cpp
@@ -194,7 +194,7 @@ namespace
// Drawing
Array DrawCalls;
- Array Lines;
+ Array> Lines;
Array Lines2;
bool IsScissorsRectEmpty;
bool IsScissorsRectEnabled;
diff --git a/Source/Engine/Renderer/ForwardPass.cpp b/Source/Engine/Renderer/ForwardPass.cpp
index 42796764f..caf624609 100644
--- a/Source/Engine/Renderer/ForwardPass.cpp
+++ b/Source/Engine/Renderer/ForwardPass.cpp
@@ -72,7 +72,7 @@ void ForwardPass::Dispose()
_shader = nullptr;
}
-void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output)
+void ForwardPass::Render(RenderContext& renderContext, GPUTexture*& input, GPUTexture*& output)
{
PROFILE_GPU_CPU("Forward");
auto context = GPUDevice::Instance->GetMainContext();
@@ -91,6 +91,16 @@ void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTex
// Check if there is no objects to render or no resources ready
auto& forwardList = mainCache->DrawCallsLists[(int32)DrawCallsListType::Forward];
auto& distortionList = mainCache->DrawCallsLists[(int32)DrawCallsListType::Distortion];
+ if ((forwardList.IsEmpty() && distortionList.IsEmpty())
+#if USE_EDITOR
+ || renderContext.View.Mode == ViewMode::PhysicsColliders
+#endif
+ )
+ {
+ // Skip rendering
+ Swap(input, output);
+ return;
+ }
if (distortionList.IsEmpty() || checkIfSkipPass())
{
// Copy frame
diff --git a/Source/Engine/Renderer/ForwardPass.h b/Source/Engine/Renderer/ForwardPass.h
index 552052eb1..be3126e0e 100644
--- a/Source/Engine/Renderer/ForwardPass.h
+++ b/Source/Engine/Renderer/ForwardPass.h
@@ -31,7 +31,7 @@ public:
/// The rendering context.
/// Target with renderer frame ready for further processing.
/// The output frame.
- void Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output);
+ void Render(RenderContext& renderContext, GPUTexture*& input, GPUTexture*& output);
private:
diff --git a/Source/Engine/Renderer/PostProcessingPass.cpp b/Source/Engine/Renderer/PostProcessingPass.cpp
index 734699511..9192c8ca4 100644
--- a/Source/Engine/Renderer/PostProcessingPass.cpp
+++ b/Source/Engine/Renderer/PostProcessingPass.cpp
@@ -276,11 +276,10 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input,
int32 h2 = h1 >> 1;
int32 h4 = h2 >> 1;
int32 h8 = h4 >> 1;
-
int32 bloomMipCount = CalculateBloomMipCount(w1, h1);
// Ensure to have valid data and if at least one effect should be applied
- if (!(useBloom || useToneMapping || useCameraArtifacts) || checkIfSkipPass() || w8 == 0 || h8 == 0)
+ if (!(useBloom || useToneMapping || useCameraArtifacts) || checkIfSkipPass() || w8 <= 1 || h8 <= 1)
{
// Resources are missing. Do not perform rendering. Just copy raw frame
context->SetViewportAndScissors((float)output->Width(), (float)output->Height());
diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp
index 26f3a636e..7fd12ba73 100644
--- a/Source/Engine/Renderer/Renderer.cpp
+++ b/Source/Engine/Renderer/Renderer.cpp
@@ -237,6 +237,12 @@ void Renderer::Render(SceneRenderTask* task)
| ViewFlags::ContactShadows
| ViewFlags::DepthOfField);
}
+
+ // Force Debug Draw usage in some specific views that depend on it
+ if (renderContext.View.Mode == ViewMode::PhysicsColliders)
+ {
+ renderContext.View.Flags |= ViewFlags::DebugDraw;
+ }
#endif
// Perform the actual rendering
diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs
index de1556577..4321ffa36 100644
--- a/Source/Engine/Serialization/JsonSerializer.cs
+++ b/Source/Engine/Serialization/JsonSerializer.cs
@@ -27,6 +27,11 @@ namespace FlaxEngine.Json
}
}
+ internal interface ICustomValueEquals
+ {
+ bool ValueEquals(object other);
+ }
+
partial class JsonSerializer
{
internal class SerializerCache
@@ -262,7 +267,7 @@ namespace FlaxEngine.Json
return true;
if (objA == null || objB == null)
return false;
-
+
// Special case when saving reference to prefab object and the objects are different but the point to the same prefab object
// In that case, skip saving reference as it's defined in prefab (will be populated via IdsMapping during deserialization)
if (objA is SceneObject sceneA && objB is SceneObject sceneB && sceneA && sceneB && sceneA.HasPrefabLink && sceneB.HasPrefabLink)
@@ -311,6 +316,8 @@ namespace FlaxEngine.Json
return !bEnumerator.MoveNext();
}
+ if (objA is ICustomValueEquals customValueEquals && objA.GetType() == objB.GetType())
+ return customValueEquals.ValueEquals(objB);
return objA.Equals(objB);
#endif
}
diff --git a/Source/Engine/Serialization/JsonTools.cpp b/Source/Engine/Serialization/JsonTools.cpp
index 25ce8ff5b..32d229a08 100644
--- a/Source/Engine/Serialization/JsonTools.cpp
+++ b/Source/Engine/Serialization/JsonTools.cpp
@@ -28,7 +28,7 @@ void ChangeIds(rapidjson_flax::Value& obj, rapidjson_flax::Document& document, c
else if (obj.IsString() && obj.GetStringLength() == 32)
{
auto value = JsonTools::GetGuid(obj);
- if (mapping.TryGet(value, value))
+ if (value.IsValid() && mapping.TryGet(value, value))
{
// Unoptimized version:
//obj.SetString(value.ToString(Guid::FormatType::N).ToSTD().c_str(), 32, document.GetAllocator());
@@ -255,9 +255,8 @@ BoundingBox JsonTools::GetBoundingBox(const Value& value)
Guid JsonTools::GetGuid(const Value& value)
{
- if (!value.IsString())
+ if (!value.IsString() || value.GetStringLength() != 32)
return Guid::Empty;
- CHECK_RETURN(value.GetStringLength() == 32, Guid::Empty);
// Split
const char* a = value.GetString();
@@ -267,10 +266,12 @@ Guid JsonTools::GetGuid(const Value& value)
// Parse
Guid result;
- StringUtils::ParseHex(a, 8, &result.A);
- StringUtils::ParseHex(b, 8, &result.B);
- StringUtils::ParseHex(c, 8, &result.C);
- StringUtils::ParseHex(d, 8, &result.D);
+ bool failed = StringUtils::ParseHex(a, 8, &result.A);
+ failed |= StringUtils::ParseHex(b, 8, &result.B);
+ failed |= StringUtils::ParseHex(c, 8, &result.C);
+ failed |= StringUtils::ParseHex(d, 8, &result.D);
+ if (failed)
+ return Guid::Empty;
return result;
}
diff --git a/Source/Engine/Serialization/JsonTools.h b/Source/Engine/Serialization/JsonTools.h
index 0e1e3850f..0e807c784 100644
--- a/Source/Engine/Serialization/JsonTools.h
+++ b/Source/Engine/Serialization/JsonTools.h
@@ -214,7 +214,7 @@ public:
const auto member = node.FindMember(name);
if (member != node.MemberEnd() && member->value.IsInt())
{
- result = member->value.GetInt();
+ result = (byte)member->value.GetInt();
}
}
@@ -232,7 +232,7 @@ public:
const auto member = node.FindMember(name);
if (member != node.MemberEnd() && member->value.IsInt())
{
- result = member->value.GetInt();
+ result = (uint32)member->value.GetInt();
}
}
@@ -241,7 +241,7 @@ public:
const auto member = node.FindMember(name);
if (member != node.MemberEnd() && member->value.IsInt())
{
- result = member->value.GetInt();
+ result = (int16)member->value.GetInt();
}
}
@@ -250,7 +250,7 @@ public:
const auto member = node.FindMember(name);
if (member != node.MemberEnd() && member->value.IsInt())
{
- result = member->value.GetInt();
+ result = (uint16)member->value.GetInt();
}
}
diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp
index b39b7ef2f..f8c9bc22e 100644
--- a/Source/Engine/Terrain/Terrain.cpp
+++ b/Source/Engine/Terrain/Terrain.cpp
@@ -240,6 +240,7 @@ void Terrain::DrawChunk(const RenderContext& renderContext, const Int2& patchCoo
void Terrain::DrawPhysicsDebug(RenderView& view)
{
+ PROFILE_CPU();
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
_patches[pathIndex]->DrawPhysicsDebug(view);
diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp
index b2f668a9d..1c754d843 100644
--- a/Source/Engine/Terrain/TerrainPatch.cpp
+++ b/Source/Engine/Terrain/TerrainPatch.cpp
@@ -104,6 +104,8 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z)
#endif
#if USE_EDITOR
_collisionTriangles.Resize(0);
+ SAFE_DELETE_GPU_RESOURCE(_collisionTrianglesBuffer);
+ _collisionTrianglesBufferDirty = true;
#endif
_collisionVertices.Resize(0);
}
@@ -120,6 +122,9 @@ TerrainPatch::~TerrainPatch()
#if TERRAIN_USE_PHYSICS_DEBUG
SAFE_DELETE_GPU_RESOURCE(_debugLines);
#endif
+#if USE_EDITOR
+ SAFE_DELETE_GPU_RESOURCE(_collisionTrianglesBuffer);
+#endif
}
RawDataAsset* TerrainPatch::GetHeightfield() const
@@ -2225,6 +2230,8 @@ void TerrainPatch::DestroyCollision()
#endif
#if USE_EDITOR
_collisionTriangles.Resize(0);
+ SAFE_DELETE(_collisionTrianglesBuffer);
+ _collisionTrianglesBufferDirty = true;
#endif
_collisionVertices.Resize(0);
}
@@ -2317,7 +2324,32 @@ void TerrainPatch::DrawPhysicsDebug(RenderView& view)
return;
if (view.Mode == ViewMode::PhysicsColliders)
{
- DEBUG_DRAW_TRIANGLES(GetCollisionTriangles(), Color::DarkOliveGreen, 0, true);
+ const auto& triangles = GetCollisionTriangles();
+ typedef DebugDraw::Vertex Vertex;
+ if (!_collisionTrianglesBuffer)
+ _collisionTrianglesBuffer = GPUDevice::Instance->CreateBuffer(TEXT("Terrain.CollisionTriangles"));
+ const uint32 count = triangles.Count();
+ if (_collisionTrianglesBuffer->GetElementsCount() != count)
+ {
+ if (_collisionTrianglesBuffer->Init(GPUBufferDescription::Vertex(Vertex::GetLayout(), sizeof(Vertex), count)))
+ return;
+ _collisionTrianglesBufferDirty = true;
+ }
+ if (_collisionTrianglesBufferDirty)
+ {
+ const Color32 color(Color::DarkOliveGreen);
+ Array vertices;
+ vertices.Resize((int32)count);
+ const Vector3* src = triangles.Get();
+ Vertex* dst = vertices.Get();
+ for (uint32 i = 0; i < count; i++)
+ {
+ dst[i] = { (Float3)src[i], color };
+ }
+ _collisionTrianglesBuffer->SetData(vertices.Get(), _collisionTrianglesBuffer->GetSize());
+ _collisionTrianglesBufferDirty = false;
+ }
+ DebugDraw::DrawTriangles(_collisionTrianglesBuffer, Matrix::Identity, 0, true);
}
else
{
@@ -2351,6 +2383,7 @@ const Array& TerrainPatch::GetCollisionTriangles()
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
_collisionTriangles.Resize((rows - 1) * (cols - 1) * 6);
+ _collisionTrianglesBufferDirty = true;
Vector3* data = _collisionTriangles.Get();
#define GET_VERTEX(x, y) Vector3 v##x##y((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, row + (x), col + (y)) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))); Vector3::Transform(v##x##y, world, v##x##y)
diff --git a/Source/Engine/Terrain/TerrainPatch.h b/Source/Engine/Terrain/TerrainPatch.h
index 8e4277068..7d85c5b1c 100644
--- a/Source/Engine/Terrain/TerrainPatch.h
+++ b/Source/Engine/Terrain/TerrainPatch.h
@@ -49,6 +49,8 @@ private:
#endif
#if USE_EDITOR
Array _collisionTriangles; // TODO: large-worlds
+ class GPUBuffer* _collisionTrianglesBuffer = nullptr;
+ bool _collisionTrianglesBufferDirty = true;
#endif
Array _collisionVertices; // TODO: large-worlds
diff --git a/Source/Engine/Tests/TestMain.cpp b/Source/Engine/Tests/TestMain.cpp
index a490f6d1c..88db6144c 100644
--- a/Source/Engine/Tests/TestMain.cpp
+++ b/Source/Engine/Tests/TestMain.cpp
@@ -44,9 +44,9 @@ void TestsRunnerService::Update()
LOG(Info, "Running Flax Tests...");
const int result = Catch::Session().run();
if (result == 0)
- LOG(Info, "Result: {0}", result);
+ LOG(Info, "Flax Tests result: {0}", result);
else
- LOG(Error, "Result: {0}", result);
+ LOG(Error, "Flax Tests result: {0}", result);
Log::Logger::WriteFloor();
Engine::RequestExit(result);
}
diff --git a/Source/Engine/Tests/TestPrefabs.cpp b/Source/Engine/Tests/TestPrefabs.cpp
index a265a9d93..5e19a5fa0 100644
--- a/Source/Engine/Tests/TestPrefabs.cpp
+++ b/Source/Engine/Tests/TestPrefabs.cpp
@@ -3,6 +3,7 @@
#include "Engine/Content/Content.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Core/Log.h"
+#include "Engine/Core/ScopeExit.h"
#include "Engine/Level/Actor.h"
#include "Engine/Level/Actors/EmptyActor.h"
#include "Engine/Level/Actors/DirectionalLight.h"
@@ -27,6 +28,7 @@ TEST_CASE("Prefabs")
// Create Prefab B with two children attached to the root
AssetReference prefabB = Content::CreateVirtualAsset();
REQUIRE(prefabB);
+ SCOPE_EXIT{ Content::DeleteAsset(prefabB); };
Guid id;
Guid::Parse("665bb01c49a3370f14a023b5395de261", id);
prefabB->ChangeID(id);
@@ -55,6 +57,7 @@ TEST_CASE("Prefabs")
// Create Prefab A with nested Prefab B attached to the root
AssetReference prefabA = Content::CreateVirtualAsset();
REQUIRE(prefabA);
+ SCOPE_EXIT{ Content::DeleteAsset(prefabA); };
Guid::Parse("02524a044184af56b6c664a0f98bd761", id);
prefabA->ChangeID(id);
auto prefabAInit = prefabA->Init(Prefab::TypeName,
@@ -123,8 +126,6 @@ TEST_CASE("Prefabs")
// Cleanup
instanceA->DeleteObject();
instanceB->DeleteObject();
- Content::DeleteAsset(prefabA);
- Content::DeleteAsset(prefabB);
}
SECTION("Test Adding Object in Nested Prefab")
{
@@ -133,6 +134,7 @@ TEST_CASE("Prefabs")
// Create Prefab B with just root object
AssetReference prefabB = Content::CreateVirtualAsset();
REQUIRE(prefabB);
+ SCOPE_EXIT{ Content::DeleteAsset(prefabB); };
Guid id;
Guid::Parse("25dbe4b0416be0777a6ce59e8788b10f", id);
prefabB->ChangeID(id);
@@ -149,6 +151,7 @@ TEST_CASE("Prefabs")
// Create Prefab A with two nested Prefab B attached to the root
AssetReference prefabA = Content::CreateVirtualAsset();
REQUIRE(prefabA);
+ SCOPE_EXIT{ Content::DeleteAsset(prefabA); };
Guid::Parse("4cb744714f746e31855f41815612d14b", id);
prefabA->ChangeID(id);
auto prefabAInit = prefabA->Init(Prefab::TypeName,
@@ -243,8 +246,6 @@ TEST_CASE("Prefabs")
// Cleanup
instanceA->DeleteObject();
instanceB->DeleteObject();
- Content::DeleteAsset(prefabA);
- Content::DeleteAsset(prefabB);
}
SECTION("Test Syncing Changes In Nested Prefab Instance")
{
@@ -253,6 +254,7 @@ TEST_CASE("Prefabs")
// Create TestActor prefab with just root object
AssetReference testActorPrefab = Content::CreateVirtualAsset();
REQUIRE(testActorPrefab);
+ SCOPE_EXIT{ Content::DeleteAsset(testActorPrefab); };
Guid id;
Guid::Parse("7691e981482f2a486e10cfae149e07d3", id);
testActorPrefab->ChangeID(id);
@@ -269,6 +271,7 @@ TEST_CASE("Prefabs")
// Create NestedActor prefab that inherits from TestActor prefab
AssetReference nestedActorPrefab = Content::CreateVirtualAsset();
REQUIRE(nestedActorPrefab);
+ SCOPE_EXIT{ Content::DeleteAsset(nestedActorPrefab); };
Guid::Parse("1d521df4465ad849e274748c6d14b703", id);
nestedActorPrefab->ChangeID(id);
auto nestedActorPrefabInit = nestedActorPrefab->Init(Prefab::TypeName,
@@ -328,8 +331,6 @@ TEST_CASE("Prefabs")
// Cleanup
nestedActor->DeleteObject();
testActor->DeleteObject();
- Content::DeleteAsset(nestedActorPrefab);
- Content::DeleteAsset(testActorPrefab);
}
SECTION("Test Loading Nested Prefab After Changing Root")
{
@@ -338,6 +339,7 @@ TEST_CASE("Prefabs")
// Create base prefab with 3 objects
AssetReference prefabBase = Content::CreateVirtualAsset();
REQUIRE(prefabBase);
+ SCOPE_EXIT{ Content::DeleteAsset(prefabBase); };
Guid id;
Guid::Parse("2b3334524c696dcfa93cabacd2a4f404", id);
prefabBase->ChangeID(id);
@@ -366,6 +368,7 @@ TEST_CASE("Prefabs")
// Create nested prefab but with 'old' state where root object is different
AssetReference prefabNested = Content::CreateVirtualAsset();
REQUIRE(prefabNested);
+ SCOPE_EXIT{ Content::DeleteAsset(prefabNested); };
Guid::Parse("a71447e947cbd2deea018a8377636ce6", id);
prefabNested->ChangeID(id);
auto prefabNestedInit = prefabNested->Init(Prefab::TypeName,
@@ -411,8 +414,6 @@ TEST_CASE("Prefabs")
// Cleanup
instanceNested->DeleteObject();
instanceBase->DeleteObject();
- Content::DeleteAsset(prefabNested);
- Content::DeleteAsset(prefabBase);
}
SECTION("Test Loading Nested Prefab After Changing and Deleting Root")
{
@@ -421,6 +422,7 @@ TEST_CASE("Prefabs")
// Create base prefab with 1 object
AssetReference prefabBase = Content::CreateVirtualAsset();
REQUIRE(prefabBase);
+ SCOPE_EXIT{ Content::DeleteAsset(prefabBase); };
Guid id;
Guid::Parse("3b3334524c696dcfa93cabacd2a4f404", id);
prefabBase->ChangeID(id);
@@ -455,6 +457,7 @@ TEST_CASE("Prefabs")
// Create nested prefab but with 'old' state where root object is different
AssetReference prefabNested1 = Content::CreateVirtualAsset();
REQUIRE(prefabNested1);
+ SCOPE_EXIT{ Content::DeleteAsset(prefabNested1); };
Guid::Parse("671447e947cbd2deea018a8377636ce6", id);
prefabNested1->ChangeID(id);
auto prefabNestedInit1 = prefabNested1->Init(Prefab::TypeName,
@@ -491,6 +494,7 @@ TEST_CASE("Prefabs")
// Create nested prefab but with 'old' state where root object is different and doesn't exist anymore
AssetReference prefabNested2 = Content::CreateVirtualAsset();
REQUIRE(prefabNested2);
+ SCOPE_EXIT{ Content::DeleteAsset(prefabNested2); };
Guid::Parse("b71447e947cbd2deea018a8377636ce6", id);
prefabNested2->ChangeID(id);
auto prefabNestedInit2 = prefabNested2->Init(Prefab::TypeName,
@@ -555,9 +559,6 @@ TEST_CASE("Prefabs")
instanceNested2->DeleteObject();
instanceNested1->DeleteObject();
instanceBase->DeleteObject();
- Content::DeleteAsset(prefabNested2);
- Content::DeleteAsset(prefabNested1);
- Content::DeleteAsset(prefabBase);
}
SECTION("Test Applying Prefab Change To Object References")
{
@@ -566,6 +567,7 @@ TEST_CASE("Prefabs")
// Create Prefab
AssetReference prefab = Content::CreateVirtualAsset();
REQUIRE(prefab);
+ SCOPE_EXIT{ Content::DeleteAsset(prefab); };
Guid id;
Guid::Parse("690e55514cd6fdc2a269429a2bf84133", id);
prefab->ChangeID(id);
@@ -612,7 +614,6 @@ TEST_CASE("Prefabs")
// Cleanup
instanceA->DeleteObject();
instanceB->DeleteObject();
- Content::DeleteAsset(prefab);
}
SECTION("Test Applying Prefab With Missing Nested Prefab")
{
@@ -637,6 +638,7 @@ TEST_CASE("Prefabs")
// Create Prefab A with nested Prefab B attached to the root
AssetReference prefabA = Content::CreateVirtualAsset();
REQUIRE(prefabA);
+ SCOPE_EXIT{ Content::DeleteAsset(prefabA); };
Guid::Parse("4cb744714f746e31855f41815612d14b", id);
prefabA->ChangeID(id);
auto prefabAInit = prefabA->Init(Prefab::TypeName,
@@ -685,7 +687,6 @@ TEST_CASE("Prefabs")
instanceA->DeleteObject();
instanceB->DeleteObject();
instanceC->DeleteObject();
- Content::DeleteAsset(prefabA);
}
}
diff --git a/Source/Engine/Threading/Task.cpp b/Source/Engine/Threading/Task.cpp
index b4bd5f025..601079e85 100644
--- a/Source/Engine/Threading/Task.cpp
+++ b/Source/Engine/Threading/Task.cpp
@@ -40,7 +40,7 @@ void Task::Cancel()
bool Task::Wait(double timeoutMilliseconds) const
{
PROFILE_CPU();
- double startTime = Platform::GetTimeSeconds() * 0.001;
+ const double startTime = Platform::GetTimeSeconds();
// TODO: no active waiting! use a semaphore!
@@ -54,7 +54,7 @@ bool Task::Wait(double timeoutMilliseconds) const
// Wait for child if has
if (_continueWith)
{
- auto spendTime = Platform::GetTimeSeconds() * 0.001 - startTime;
+ const auto spendTime = (Platform::GetTimeSeconds() - startTime) * 1000.0;
return _continueWith->Wait(timeoutMilliseconds - spendTime);
}
@@ -66,7 +66,7 @@ bool Task::Wait(double timeoutMilliseconds) const
return true;
Platform::Sleep(1);
- } while (timeoutMilliseconds <= 0.0 || Platform::GetTimeSeconds() * 0.001 - startTime < timeoutMilliseconds);
+ } while (timeoutMilliseconds <= 0.0 || (Platform::GetTimeSeconds() - startTime) * 1000.0 < timeoutMilliseconds);
// Timeout reached!
LOG(Warning, "\'{0}\' has timed out. Wait time: {1} ms", ToString(), timeoutMilliseconds);
@@ -208,8 +208,8 @@ void Task::OnCancel()
if (IsRunning())
{
// Wait for it a little bit
- const double timeout = 2000.0;
- LOG(Warning, "Cannot cancel \'{0}\' because it's still running, waiting for end with timeout: {1} ms", ToString(), timeout);
+ constexpr double timeout = 10000.0; // 10s
+ LOG(Warning, "Cannot cancel \'{0}\' because it's still running, waiting for end with timeout: {1}ms", ToString(), timeout);
Wait(timeout);
}
diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs
index ff9d3df2d..0bfa799c2 100644
--- a/Source/Engine/UI/GUI/Panels/DropPanel.cs
+++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs
@@ -51,6 +51,11 @@ namespace FlaxEngine.GUI
///
protected float _cachedHeight = 16.0f;
+ ///
+ /// The items spacing.
+ ///
+ protected float _itemsSpacing = 2.0f;
+
///
/// The items margin.
///
@@ -168,9 +173,9 @@ namespace FlaxEngine.GUI
}
///
- /// Gets or sets the item slots margin (the space between items).
+ /// Gets or sets the item slots margin (the space around items).
///
- [EditorOrder(10), Tooltip("The item slots margin (the space between items).")]
+ [EditorOrder(10)]
public Margin ItemsMargin
{
get => _itemsMargin;
@@ -184,6 +189,23 @@ namespace FlaxEngine.GUI
}
}
+ ///
+ /// Gets or sets the item slots spacing (the margin between items).
+ ///
+ [EditorOrder(11)]
+ public float ItemsSpacing
+ {
+ get => _itemsSpacing;
+ set
+ {
+ if (!Mathf.NearEqual(_itemsSpacing, value))
+ {
+ _itemsSpacing = value;
+ PerformLayout();
+ }
+ }
+ }
+
///
/// Gets or sets the panel close/open animation duration (in seconds).
///
@@ -563,25 +585,27 @@ namespace FlaxEngine.GUI
var slotsLeft = clientArea.Left + slotsMargin.Left;
var slotsWidth = clientArea.Width - slotsMargin.Width;
float minHeight = HeaderHeight;
- float y = clientArea.Top;
- float height = clientArea.Top + dropOffset;
+ float y = clientArea.Top + slotsMargin.Top;
+ bool anyAdded = false;
for (int i = 0; i < _children.Count; i++)
{
Control c = _children[i];
if (c.IsScrollable && c.Visible)
{
var h = c.Height;
- y += slotsMargin.Top;
-
c.Bounds = new Rectangle(slotsLeft, y, slotsWidth, h);
-
- h += slotsMargin.Bottom;
+ h += _itemsSpacing;
y += h;
- height += h + slotsMargin.Top;
+ anyAdded = true;
}
}
// Update panel height
+ if (anyAdded)
+ y -= _itemsSpacing;
+ if (anyAdded)
+ y += slotsMargin.Bottom;
+ float height = dropOffset + y;
_cachedHeight = height;
if (_animationProgress >= 1.0f && _isClosed)
y = minHeight;
diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs
index 1ffae12f8..ff5483d0f 100644
--- a/Source/Engine/UI/GUI/Panels/Panel.cs
+++ b/Source/Engine/UI/GUI/Panels/Panel.cs
@@ -20,6 +20,7 @@ namespace FlaxEngine.GUI
private Color _scrollbarTrackColor;
private Color _scrollbarThumbColor;
private Color _scrollbarThumbSelectedColor;
+ private Rectangle _controlsBoundsBeforeLayout;
///
/// The cached scroll area bounds. Used to scroll contents of the panel control. Cached during performing layout.
@@ -530,8 +531,25 @@ namespace FlaxEngine.GUI
{
// Arrange controls and get scroll bounds
ArrangeAndGetBounds();
+ UpdateScrollBars();
+ _controlsBoundsBeforeLayout = _controlsBounds;
+ }
- // Update scroll bars
+ ///
+ protected override void PerformLayoutAfterChildren()
+ {
+ // If controls area changed during layout then update scroll bars again
+ ArrangeAndGetBounds();
+ if (_controlsBoundsBeforeLayout != _controlsBounds)
+ {
+ UpdateScrollBars();
+ }
+
+ base.PerformLayoutAfterChildren();
+ }
+
+ private void UpdateScrollBars()
+ {
var controlsBounds = _controlsBounds;
var scrollBounds = controlsBounds;
_scrollMargin.ExpandRectangle(ref scrollBounds);
@@ -689,7 +707,7 @@ namespace FlaxEngine.GUI
}
viewOffset.Y = Mathf.Clamp(viewOffset.Y, VScrollBar.Minimum, VScrollBar.Maximum);
- VScrollBar.Value = viewOffset.Y;
+ VScrollBar.TargetValue = viewOffset.Y;
}
if (HScrollBar != null && HScrollBar.Enabled && width > MinSize)
@@ -704,7 +722,7 @@ namespace FlaxEngine.GUI
}
viewOffset.X = Mathf.Clamp(viewOffset.X, HScrollBar.Minimum, HScrollBar.Maximum);
- HScrollBar.Value = viewOffset.X;
+ HScrollBar.TargetValue = viewOffset.X;
}
viewOffset *= -1;
diff --git a/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs b/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs
index 6292a3e81..b8d59c4d7 100644
--- a/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs
+++ b/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs
@@ -72,8 +72,11 @@ namespace FlaxEngine.GUI
get => _slotSpacing;
set
{
- _slotSpacing = value;
- PerformLayout();
+ if (!Float2.NearEqual(ref _slotSpacing, ref value))
+ {
+ _slotSpacing = value;
+ PerformLayout();
+ }
}
}
@@ -89,11 +92,11 @@ namespace FlaxEngine.GUI
/// Initializes a new instance of the class.
///
/// The slot padding.
- public UniformGridPanel(float slotPadding = 0)
+ public UniformGridPanel(float slotPadding)
{
AutoFocus = false;
- SlotPadding = new Margin(slotPadding);
- SlotSpacing = new Float2(2);
+ _slotPadding = new Margin(slotPadding);
+ _slotSpacing = new Float2(2);
_slotsH = _slotsV = 5;
}
@@ -105,25 +108,32 @@ namespace FlaxEngine.GUI
int slotsV = _slotsV;
int slotsH = _slotsH;
Float2 slotSize;
+ Float2 size = Size;
+ bool applySpacing = true;
+ APPLY_SPACING:
if (_slotsV + _slotsH == 0)
{
slotSize = HasChildren ? Children[0].Size : new Float2(32);
- slotsH = Mathf.CeilToInt(Width / slotSize.X);
- slotsV = Mathf.CeilToInt(Height / slotSize.Y);
+ slotsH = Mathf.CeilToInt(size.X / slotSize.X);
+ slotsV = Mathf.CeilToInt(size.Y / slotSize.Y);
}
else if (slotsH == 0)
{
- float size = Height / slotsV;
- slotSize = new Float2(size);
+ slotSize = new Float2(size.Y / slotsV);
}
else if (slotsV == 0)
{
- float size = Width / slotsH;
- slotSize = new Float2(size);
+ slotSize = new Float2(size.X / slotsH);
}
else
{
- slotSize = new Float2(Width / slotsH, Height / slotsV);
+ slotSize = new Float2(size.X / slotsH, size.Y / slotsV);
+ }
+ if (applySpacing && _slotSpacing != Float2.Zero)
+ {
+ applySpacing = false;
+ size -= _slotSpacing * new Float2(slotsH > 1 ? slotsH - 1 : 0, slotsV > 1 ? slotsV - 1 : 0);
+ goto APPLY_SPACING;
}
int i = 0;
@@ -135,45 +145,9 @@ namespace FlaxEngine.GUI
for (int x = 0; x < end; x++)
{
- var slotBounds = new Rectangle(slotSize.X * x, slotSize.Y * y, slotSize.X, slotSize.Y);
+ var slotBounds = new Rectangle((slotSize + _slotSpacing) * new Float2(x, y), slotSize);
_slotPadding.ShrinkRectangle(ref slotBounds);
- if (slotsV > 1)
- {
- if (y == 0)
- {
- slotBounds.Height -= _slotSpacing.Y * 0.5f;
- }
- else if (y == slotsV - 1)
- {
- slotBounds.Height -= _slotSpacing.Y * 0.5f;
- slotBounds.Y += _slotSpacing.Y * 0.5f;
- }
- else
- {
- slotBounds.Height -= _slotSpacing.Y;
- slotBounds.Y += _slotSpacing.Y * 0.5f;
- }
- }
-
- if (slotsH > 1)
- {
- if (x == 0)
- {
- slotBounds.Width -= _slotSpacing.X * 0.5f;
- }
- else if (x == slotsH - 1)
- {
- slotBounds.Width -= _slotSpacing.X * 0.5f;
- slotBounds.X += _slotSpacing.X * 0.5f;
- }
- else
- {
- slotBounds.Width -= _slotSpacing.X;
- slotBounds.X += _slotSpacing.X * 0.5f;
- }
- }
-
var c = _children[i++];
c.Bounds = slotBounds;
}
diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp
index 232354155..bc7b88647 100644
--- a/Source/Engine/UI/TextRender.cpp
+++ b/Source/Engine/UI/TextRender.cpp
@@ -176,7 +176,7 @@ void TextRender::UpdateLayout()
int32 kerning;
// Perform layout
- Array lines;
+ Array> lines;
font->ProcessText(text, lines, _layoutOptions);
// Prepare buffers capacity
diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp
index 3ce197a18..ef4f53cf9 100644
--- a/Source/Engine/Visject/ShaderGraph.cpp
+++ b/Source/Engine/Visject/ShaderGraph.cpp
@@ -372,8 +372,8 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value)
// Atan2
case 41:
{
- Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
- Value v2 = tryGetValue(node->GetBox(1), Value::Zero);
+ Value v1 = tryGetValue(node->GetBox(0), 0, Value::Zero);
+ Value v2 = tryGetValue(node->GetBox(1), 1, Value::Zero);
value = writeFunction2(node, v1, v2, TEXT("atan2"));
break;
}
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
index fd3f3810b..54329634f 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
@@ -1629,8 +1629,6 @@ namespace Flax.Build.Bindings
}
else
originalType = type = GenerateCSharpNativeToManaged(buildData, marshalType, structureInfo);
- if (apiType != null && apiType.MarshalAs != null)
- Log.Error("marshal as into type: " + type);
structContents.Append(structIndent).Append("public ");
string internalTypeMarshaller = "";
if (marshalType.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs
index aff928ad2..bf3c61f7e 100644
--- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs
+++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs
@@ -283,8 +283,10 @@ namespace Flax.Build
Log.Verbose($"Found the following .NET SDK versions: {string.Join(", ", dotnetSdkVersions)}");
Log.Verbose($"Found the following .NET runtime versions: {string.Join(", ", dotnetRuntimeVersions)}");
+ string configuredDotnetVersion = Configuration.Dotnet;
var dotnetSdkVersion = GetVersion(dotnetSdkVersions);
var dotnetRuntimeVersion = GetVersion(dotnetRuntimeVersions);
+
if (!string.IsNullOrEmpty(dotnetSdkVersion) && !string.IsNullOrEmpty(dotnetRuntimeVersion) && ParseVersion(dotnetRuntimeVersion).Major > ParseVersion(dotnetSdkVersion).Major)
{
// Make sure the reference assemblies are not newer than the SDK itself
@@ -296,21 +298,24 @@ namespace Flax.Build
} while (!string.IsNullOrEmpty(dotnetRuntimeVersion) && ParseVersion(dotnetRuntimeVersion).Major > ParseVersion(dotnetSdkVersion).Major);
}
- var minVer = string.IsNullOrEmpty(Configuration.Dotnet) ? MinimumVersion.ToString() : Configuration.Dotnet;
if (string.IsNullOrEmpty(dotnetSdkVersion))
{
- if (dotnetSdkVersions.Any())
- Log.Warning($"Unsupported .NET SDK versions found: {string.Join(", ", dotnetSdkVersions)}. Minimum version required is .NET {minVer}.");
- else
- Log.Warning($"Missing .NET SDK. Minimum version required is .NET {minVer}.");
+ string installedVersionsText = dotnetSdkVersions.Any()
+ ? $"{string.Join(", ", dotnetSdkVersions)}"
+ : "None";
+ Log.Warning(!string.IsNullOrEmpty(configuredDotnetVersion)
+ ? $"Configured .NET SDK '{configuredDotnetVersion}' not found. Installed versions: {installedVersionsText}."
+ : $"No compatible .NET SDK found within the supported range: .NET {MinimumVersion.ToString()} - {MaximumVersion.ToString()}. Installed versions: {installedVersionsText}.");
return;
}
if (string.IsNullOrEmpty(dotnetRuntimeVersion))
{
- if (dotnetRuntimeVersions.Any())
- Log.Warning($"Unsupported .NET runtime versions found: {string.Join(", ", dotnetRuntimeVersions)}. Minimum version required is .NET {minVer}.");
- else
- Log.Warning($"Missing .NET runtime. Minimum version required is .NET {minVer}.");
+ string installedRuntimeVersionsText = dotnetRuntimeVersions.Any()
+ ? $"{string.Join(", ", dotnetRuntimeVersions)}"
+ : "None";
+ Log.Warning(!string.IsNullOrEmpty(configuredDotnetVersion)
+ ? $"Configured .NET runtime version '{configuredDotnetVersion}' not found. Installed versions: {installedRuntimeVersionsText}."
+ : $"No compatible .NET runtime found within the supported range: .NET {MinimumVersion.ToString()} - {MaximumVersion.ToString()}. Installed versions: {installedRuntimeVersionsText}.");
return;
}
RootPath = dotnetPath;
diff --git a/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs b/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs
index 0f2fc517c..a1018368b 100644
--- a/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs
+++ b/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs
@@ -87,6 +87,13 @@ namespace Flax.Build.Platforms
if (lines.Length > 1)
{
var ver = lines[1].Substring(lines[1].IndexOf(" = ", StringComparison.Ordinal) + 2);
+ if (ver.Contains('-'))
+ {
+ // Ignore any beta tags (eg. '29.0.13113456-beta1')
+ var parts = ver.Split('-');
+ if (parts.Length > 1)
+ ver = parts[0];
+ }
if (Version.TryParse(ver, out var v))
{
Version = v;