diff --git a/.gitignore b/.gitignore
index 1920b3eea..54907892f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@ Cache/
Binaries/
Output/
Logs/
+Source/*.Gen.*
Source/*.csproj
/Package_*/
!Source/Engine/Debug
diff --git a/Content/Shaders/Lights.flax b/Content/Shaders/Lights.flax
index 34e8c9a8c..ea30cd9f7 100644
--- a/Content/Shaders/Lights.flax
+++ b/Content/Shaders/Lights.flax
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b405f5698304e87e6f510c49a0cea3817c9a1a00d7ba63ef4027ac49ba7cc7a4
-size 5299
+oid sha256:83cb261770aba2813f85063f987032f9914387f4011474b0fe347c42522116b4
+size 5122
diff --git a/Content/Shaders/VolumetricFog.flax b/Content/Shaders/VolumetricFog.flax
index 18eddae16..eb3ca9f2c 100644
--- a/Content/Shaders/VolumetricFog.flax
+++ b/Content/Shaders/VolumetricFog.flax
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:72415ba69a685e9a739d1a08d08c6eb9efd3cab31d8cc0c949cb187a9bad19a5
-size 13841
+oid sha256:4ab2c2551ce9cce0355c92f4f6499858e04e4bf764900b305273a7d57b887596
+size 13633
diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs
index 7eb2e0950..6065ca9f8 100644
--- a/Source/Editor/Content/GUI/ContentView.cs
+++ b/Source/Editor/Content/GUI/ContentView.cs
@@ -692,10 +692,13 @@ namespace FlaxEditor.Content.GUI
c = char.ToLowerInvariant(c);
for (int i = 0; i < _items.Count; i++)
{
- var name = _items[i].ShortName;
+ var item = _items[i];
+ var name = item.ShortName;
if (!string.IsNullOrEmpty(name) && char.ToLowerInvariant(name[0]) == c)
{
- Select(_items[i]);
+ Select(item);
+ if (Parent is Panel panel)
+ panel.ScrollViewTo(item, true);
break;
}
}
diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs
index 4fe75d2c2..66825fb42 100644
--- a/Source/Editor/Content/Items/ContentItem.cs
+++ b/Source/Editor/Content/Items/ContentItem.cs
@@ -360,7 +360,7 @@ namespace FlaxEditor.Content
}
///
- /// Updates the tooltip text.
+ /// Updates the tooltip text text.
///
public virtual void UpdateTooltipText()
{
@@ -384,7 +384,8 @@ namespace FlaxEditor.Content
protected virtual void OnBuildTooltipText(StringBuilder sb)
{
sb.Append("Type: ").Append(TypeDescription).AppendLine();
- sb.Append("Size: ").Append(Utilities.Utils.FormatBytesCount((int)new FileInfo(Path).Length)).AppendLine();
+ if (File.Exists(Path))
+ sb.Append("Size: ").Append(Utilities.Utils.FormatBytesCount((int)new FileInfo(Path).Length)).AppendLine();
sb.Append("Path: ").Append(Utilities.Utils.GetAssetNamePathWithExt(Path)).AppendLine();
}
@@ -718,7 +719,7 @@ namespace FlaxEditor.Content
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
Focus();
-
+
// Open
(Parent as ContentView).OnItemDoubleClick(this);
diff --git a/Source/Editor/Content/Items/NewItem.cs b/Source/Editor/Content/Items/NewItem.cs
index 94d95f15b..7040adf44 100644
--- a/Source/Editor/Content/Items/NewItem.cs
+++ b/Source/Editor/Content/Items/NewItem.cs
@@ -1,5 +1,6 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+using System.Text;
using FlaxEngine;
namespace FlaxEditor.Content
@@ -47,5 +48,11 @@ namespace FlaxEditor.Content
///
protected override bool DrawShadow => true;
+
+ ///
+ public override void UpdateTooltipText()
+ {
+ TooltipText = null;
+ }
}
}
diff --git a/Source/Editor/Content/Proxy/ScriptProxy.cs b/Source/Editor/Content/Proxy/ScriptProxy.cs
index 3196ea833..f688f37df 100644
--- a/Source/Editor/Content/Proxy/ScriptProxy.cs
+++ b/Source/Editor/Content/Proxy/ScriptProxy.cs
@@ -45,7 +45,7 @@ namespace FlaxEditor.Content
}
///
- public override string NewItemName => "Script";
+ public override string NewItemName => "MyScript";
///
public override bool CanCreate(ContentFolder targetLocation)
@@ -72,6 +72,8 @@ namespace FlaxEditor.Content
// Scripts cannot start with digit.
if (Char.IsDigit(filename[0]))
return false;
+ if (filename.Equals("Script"))
+ return false;
return true;
}
diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs
index 1ece31f4d..03b21e12b 100644
--- a/Source/Editor/CustomEditors/CustomEditor.cs
+++ b/Source/Editor/CustomEditors/CustomEditor.cs
@@ -148,7 +148,7 @@ namespace FlaxEditor.CustomEditors
return;
// Special case for root objects to run normal layout build
- if (_presenter.Selection == Values)
+ if (_presenter != null && _presenter.Selection == Values)
{
_presenter.BuildLayout();
return;
@@ -159,7 +159,7 @@ namespace FlaxEditor.CustomEditors
var layout = _layout;
var control = layout.ContainerControl;
var parent = _parent;
- var parentScrollV = (_presenter.Panel.Parent as Panel)?.VScrollBar?.Value ?? -1;
+ var parentScrollV = (_presenter?.Panel.Parent as Panel)?.VScrollBar?.Value ?? -1;
control.IsLayoutLocked = true;
control.DisposeChildren();
@@ -249,6 +249,28 @@ namespace FlaxEditor.CustomEditors
internal virtual void RefreshRootChild()
{
+ // Check if need to update value
+ if (_hasValueDirty)
+ {
+ IsSettingValue = true;
+ try
+ {
+ // Cleanup (won't retry update in case of exception)
+ object val = _valueToSet;
+ _hasValueDirty = false;
+ _valueToSet = null;
+
+ // Assign value
+ for (int i = 0; i < _values.Count; i++)
+ _values[i] = val;
+ }
+ finally
+ {
+ OnUnDirty();
+ IsSettingValue = false;
+ }
+ }
+
Refresh();
for (int i = 0; i < _children.Count; i++)
diff --git a/Source/Editor/CustomEditors/CustomEditorsUtil.cs b/Source/Editor/CustomEditors/CustomEditorsUtil.cs
index 4c664031f..5f76b07e2 100644
--- a/Source/Editor/CustomEditors/CustomEditorsUtil.cs
+++ b/Source/Editor/CustomEditors/CustomEditorsUtil.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
+using FlaxEditor.CustomEditors.Dedicated;
using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.Scripting;
using FlaxEngine;
@@ -110,7 +111,7 @@ namespace FlaxEditor.CustomEditors
// Select default editor (based on type)
if (targetType.IsEnum)
return new EnumEditor();
- if (targetType.IsGenericType)
+ if (targetType.IsGenericType)
{
if (targetTypeType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
return new DictionaryEditor();
@@ -121,6 +122,8 @@ namespace FlaxEditor.CustomEditors
if (customEditorType != null)
return (CustomEditor)Activator.CreateInstance(customEditorType);
}
+ if (typeof(FlaxEngine.Object).IsAssignableFrom(targetTypeType))
+ return new ScriptingObjectEditor();
// The most generic editor
return new GenericEditor();
diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
index 6a2986d2b..f9325fb26 100644
--- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
@@ -21,7 +21,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
///
///
[CustomEditor(typeof(Actor)), DefaultEditor]
- public class ActorEditor : GenericEditor
+ public class ActorEditor : ScriptingObjectEditor
{
private Guid _linkedPrefabId;
diff --git a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs
index 75fec4eba..ec71348cf 100644
--- a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs
@@ -1,8 +1,10 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+using System;
using System.Linq;
using FlaxEditor.Surface;
using FlaxEngine;
+using FlaxEngine.GUI;
namespace FlaxEditor.CustomEditors.Dedicated
{
@@ -13,6 +15,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
[CustomEditor(typeof(ParticleEffect)), DefaultEditor]
public class ParticleEffectEditor : ActorEditor
{
+ private Label _infoLabel;
private bool _isValid;
private bool _isActive;
private uint _parametersVersion;
@@ -48,6 +51,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
return null;
}
+ private void Foreach(Action func)
+ {
+ foreach (var value in Values)
+ {
+ if (value is ParticleEffect player)
+ func(player);
+ }
+ }
+
///
public override void Initialize(LayoutElementsContainer layout)
{
@@ -60,6 +72,26 @@ namespace FlaxEditor.CustomEditors.Dedicated
_parametersVersion = effect.ParametersVersion;
_isActive = effect.IsActive;
+ // Show playback options during simulation
+ if (Editor.IsPlayMode)
+ {
+ var playbackGroup = layout.Group("Playback");
+ playbackGroup.Panel.Open();
+
+ _infoLabel = playbackGroup.Label(string.Empty).Label;
+ _infoLabel.AutoHeight = true;
+
+ var grid = playbackGroup.CustomContainer();
+ var gridControl = grid.CustomControl;
+ gridControl.ClipChildren = false;
+ gridControl.Height = Button.DefaultHeight;
+ gridControl.SlotsHorizontally = 3;
+ gridControl.SlotsVertically = 1;
+ grid.Button("Play").Button.Clicked += () => Foreach(x => x.Play());
+ grid.Button("Pause").Button.Clicked += () => Foreach(x => x.Pause());
+ grid.Button("Stop").Button.Clicked += () => Foreach(x => x.Stop());
+ }
+
// Show all effect parameters grouped by the emitter track name
var groups = layout.Group("Parameters");
groups.Panel.Open();
@@ -99,7 +131,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
base.RefreshRootChild();
return;
}
-
+
for (int i = 0; i < ChildrenEditors.Count; i++)
{
if (_isActive != effect.IsActive || _parametersVersion != effect.ParametersVersion)
diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptingObjectEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptingObjectEditor.cs
new file mode 100644
index 000000000..477deb708
--- /dev/null
+++ b/Source/Editor/CustomEditors/Dedicated/ScriptingObjectEditor.cs
@@ -0,0 +1,29 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+using FlaxEditor.CustomEditors.Editors;
+using FlaxEngine.Networking;
+
+namespace FlaxEditor.CustomEditors.Dedicated
+{
+ ///
+ /// Custom editor for .
+ ///
+ public class ScriptingObjectEditor : GenericEditor
+ {
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ // Network objects debugging
+ var obj = Values[0] as FlaxEngine.Object;
+ if (Editor.IsPlayMode && NetworkManager.IsConnected && NetworkReplicator.HasObject(obj))
+ {
+ var group = layout.Group("Network");
+ group.Panel.Open();
+ group.Label("Role", Utilities.Utils.GetPropertyNameUI(NetworkReplicator.GetObjectRole(obj).ToString()));
+ group.Label("Owner Client Id", NetworkReplicator.GetObjectOwnerClientId(obj).ToString());
+ }
+
+ base.Initialize(layout);
+ }
+ }
+}
diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
index 41dc1dc08..bf82e19da 100644
--- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using System.Linq;
using FlaxEditor.Actions;
using FlaxEditor.Content;
using FlaxEditor.GUI;
diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
index 2612440a4..076b94b0a 100644
--- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
@@ -78,7 +78,7 @@ namespace FlaxEditor.CustomEditors.Editors
///
public class ScaleEditor : Float3Editor
{
- private Image _linkImage;
+ private Button _linkButton;
///
public override void Initialize(LayoutElementsContainer layout)
@@ -87,19 +87,20 @@ namespace FlaxEditor.CustomEditors.Editors
LinkValues = Editor.Instance.Windows.PropertiesWin.ScaleLinked;
- _linkImage = new Image
+ // Add button with the link icon
+ _linkButton = new Button
{
+ BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Link32),
Parent = LinkedLabel,
Width = 18,
Height = 18,
- Brush = LinkValues ? new SpriteBrush(Editor.Instance.Icons.Link32) : new SpriteBrush(),
AnchorPreset = AnchorPresets.TopLeft,
- TooltipText = "Scale values are linked together.",
};
+ _linkButton.Clicked += ToggleLink;
+ SetLinkStyle();
var x = LinkedLabel.Text.Value.Length * 7 + 5;
- _linkImage.LocalX += x;
- _linkImage.LocalY += 1;
-
+ _linkButton.LocalX += x;
+ _linkButton.LocalY += 1;
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
{
menu.AddSeparator();
@@ -127,7 +128,16 @@ namespace FlaxEditor.CustomEditors.Editors
{
LinkValues = !LinkValues;
Editor.Instance.Windows.PropertiesWin.ScaleLinked = LinkValues;
- _linkImage.Brush = LinkValues ? new SpriteBrush(Editor.Instance.Icons.Link32) : new SpriteBrush();
+ SetLinkStyle();
+ }
+
+ private void SetLinkStyle()
+ {
+ var style = FlaxEngine.GUI.Style.Current;
+ var backgroundColor = LinkValues ? style.Foreground : style.ForegroundDisabled;
+ _linkButton.SetColors(backgroundColor);
+ _linkButton.BorderColor = _linkButton.BorderColorSelected = _linkButton.BorderColorHighlighted = Color.Transparent;
+ _linkButton.TooltipText = LinkValues ? "Unlinks scale components from uniform scaling" : "Links scale components for uniform scaling";
}
}
}
diff --git a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs
index f26bf25a4..73089ff04 100644
--- a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections;
-using System.Collections.Generic;
using System.Linq;
using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.CustomEditors.GUI;
@@ -46,8 +45,8 @@ namespace FlaxEditor.CustomEditors.Editors
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkedEditor)
{
- menu.AddSeparator();
-
+ if (menu.Items.Any())
+ menu.AddSeparator();
menu.AddButton("Remove", OnRemoveClicked).Enabled = !_editor._readOnly;
menu.AddButton("Edit", OnEditClicked).Enabled = _editor._canEditKeys;
}
@@ -62,6 +61,7 @@ namespace FlaxEditor.CustomEditors.Editors
var keyType = _editor.Values.Type.GetGenericArguments()[0];
if (keyType == typeof(string) || keyType.IsPrimitive)
{
+ // Edit as text
var popup = RenamePopup.Show(Parent, Rectangle.Margin(Bounds, Margin), Text, false);
popup.Validate += (renamePopup, value) =>
{
@@ -79,7 +79,6 @@ namespace FlaxEditor.CustomEditors.Editors
newKey = JsonSerializer.Deserialize(renamePopup.Text, keyType);
else
newKey = renamePopup.Text;
-
_editor.ChangeKey(_key, newKey);
_key = newKey;
Text = _key.ToString();
@@ -87,6 +86,7 @@ namespace FlaxEditor.CustomEditors.Editors
}
else if (keyType.IsEnum)
{
+ // Edit via enum picker
var popup = RenamePopup.Show(Parent, Rectangle.Margin(Bounds, Margin), Text, false);
var picker = new EnumComboBox(keyType)
{
@@ -109,7 +109,21 @@ namespace FlaxEditor.CustomEditors.Editors
}
else
{
- throw new NotImplementedException("Missing editing for dictionary key type " + keyType);
+ // Generic editor
+ var popup = ContextMenuBase.ShowEmptyMenu(Parent, Rectangle.Margin(Bounds, Margin));
+ var presenter = new CustomEditorPresenter(null);
+ presenter.Panel.AnchorPreset = AnchorPresets.StretchAll;
+ presenter.Panel.IsScrollable = false;
+ presenter.Panel.Parent = popup;
+ presenter.Select(_key);
+ presenter.Modified += () =>
+ {
+ popup.Hide();
+ object newKey = presenter.Selection[0];
+ _editor.ChangeKey(_key, newKey);
+ _key = newKey;
+ Text = _key?.ToString();
+ };
}
}
@@ -160,7 +174,7 @@ namespace FlaxEditor.CustomEditors.Editors
var argTypes = type.GetGenericArguments();
var keyType = argTypes[0];
var valueType = argTypes[1];
- _canEditKeys = keyType == typeof(string) || keyType.IsPrimitive || keyType.IsEnum;
+ _canEditKeys = keyType == typeof(string) || keyType.IsPrimitive || keyType.IsEnum || keyType.IsValueType;
_background = FlaxEngine.GUI.Style.Current.CollectionBackgroundColor;
_readOnly = false;
_notNullItems = false;
@@ -383,6 +397,7 @@ namespace FlaxEditor.CustomEditors.Editors
int newItemsLeft = newSize - oldSize;
while (newItemsLeft-- > 0)
{
+ object newKey = null;
if (keyType.IsPrimitive)
{
long uniqueKey = 0;
@@ -401,8 +416,7 @@ namespace FlaxEditor.CustomEditors.Editors
}
}
} while (!isUnique);
-
- newValues[Convert.ChangeType(uniqueKey, keyType)] = TypeUtils.GetDefaultValue(new ScriptType(valueType));
+ newKey = Convert.ChangeType(uniqueKey, keyType);
}
else if (keyType.IsEnum)
{
@@ -422,8 +436,7 @@ namespace FlaxEditor.CustomEditors.Editors
}
}
} while (!isUnique && uniqueKeyIndex < enumValues.Length);
-
- newValues[enumValues.GetValue(uniqueKeyIndex)] = TypeUtils.GetDefaultValue(new ScriptType(valueType));
+ newKey = enumValues.GetValue(uniqueKeyIndex);
}
else if (keyType == typeof(string))
{
@@ -442,13 +455,13 @@ namespace FlaxEditor.CustomEditors.Editors
}
}
} while (!isUnique);
-
- newValues[uniqueKey] = TypeUtils.GetDefaultValue(new ScriptType(valueType));
+ newKey = uniqueKey;
}
else
{
- throw new InvalidOperationException();
+ newKey = TypeUtils.GetDefaultValue(new ScriptType(keyType));
}
+ newValues[newKey] = TypeUtils.GetDefaultValue(new ScriptType(valueType));
}
SetValue(newValues);
diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs
index eb0c839ed..ab17c4ae1 100644
--- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs
@@ -394,11 +394,8 @@ namespace FlaxEditor.CustomEditors.Editors
if (_element != null)
{
_element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value.Type);
-
if (_element.CustomControl.Type == ScriptType.Object)
- {
_element.CustomControl.Type = Values.Type.Type != typeof(object) || Values[0] == null ? ScriptType.Object : TypeUtils.GetObjectType(Values[0]);
- }
}
}
@@ -408,9 +405,7 @@ namespace FlaxEditor.CustomEditors.Editors
base.Refresh();
if (!HasDifferentValues)
- {
_element.CustomControl.Value = new ScriptType(Values[0] as Type);
- }
}
}
@@ -426,9 +421,7 @@ namespace FlaxEditor.CustomEditors.Editors
base.Initialize(layout);
if (_element != null)
- {
_element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value);
- }
}
///
@@ -437,9 +430,32 @@ namespace FlaxEditor.CustomEditors.Editors
base.Refresh();
if (!HasDifferentValues)
- {
_element.CustomControl.Value = (ScriptType)Values[0];
- }
+ }
+ }
+
+ ///
+ /// Default implementation of the inspector used to edit reference to the . Used to pick classes.
+ ///
+ [CustomEditor(typeof(SoftTypeReference)), DefaultEditor]
+ public class SoftTypeReferenceEditor : TypeEditorBase
+ {
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ base.Initialize(layout);
+
+ if (_element != null)
+ _element.CustomControl.ValueChanged += () => SetValue(new SoftTypeReference(_element.CustomControl.ValueTypeName));
+ }
+
+ ///
+ public override void Refresh()
+ {
+ base.Refresh();
+
+ if (!HasDifferentValues)
+ _element.CustomControl.ValueTypeName = ((SoftTypeReference)Values[0]).TypeName;
}
}
diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp
index fb6d53c4c..62ef85f2c 100644
--- a/Source/Editor/Editor.cpp
+++ b/Source/Editor/Editor.cpp
@@ -259,13 +259,13 @@ bool Editor::CheckProjectUpgrade()
LOG(Warning, "Project layout upgraded!");
}
- // Check if last version was the same
+ // Check if last version was the same
else if (lastMajor == FLAXENGINE_VERSION_MAJOR && lastMinor == FLAXENGINE_VERSION_MINOR)
{
// Do nothing
IsOldProjectOpened = false;
}
- // Check if last version was older
+ // Check if last version was older
else if (lastMajor < FLAXENGINE_VERSION_MAJOR || (lastMajor == FLAXENGINE_VERSION_MAJOR && lastMinor < FLAXENGINE_VERSION_MINOR))
{
LOG(Warning, "The project was opened with the older editor version last time");
@@ -288,7 +288,7 @@ bool Editor::CheckProjectUpgrade()
return true;
}
}
- // Check if last version was newer
+ // Check if last version was newer
else if (lastMajor > FLAXENGINE_VERSION_MAJOR || (lastMajor == FLAXENGINE_VERSION_MAJOR && lastMinor > FLAXENGINE_VERSION_MINOR))
{
LOG(Warning, "The project was opened with the newer editor version last time");
@@ -312,6 +312,14 @@ bool Editor::CheckProjectUpgrade()
}
}
+ // When changing between major/minor version clear some caches to prevent possible issues
+ if (lastMajor != FLAXENGINE_VERSION_MAJOR || lastMinor != FLAXENGINE_VERSION_MINOR)
+ {
+ LOG(Info, "Cleaning cache files from different engine version");
+ FileSystem::DeleteDirectory(Globals::ProjectFolder / TEXT("Cache/Cooker"));
+ FileSystem::DeleteDirectory(Globals::ProjectFolder / TEXT("Cache/Intermediate"));
+ }
+
// Upgrade old 0.7 projects
// [Deprecated: 01.11.2020, expires 01.11.2021]
if (lastMajor == 0 && lastMinor == 7 && lastBuild <= 6197)
@@ -330,12 +338,11 @@ bool Editor::CheckProjectUpgrade()
file->WriteInt32(FLAXENGINE_VERSION_MAJOR);
file->WriteInt32(FLAXENGINE_VERSION_MINOR);
file->WriteInt32(FLAXENGINE_VERSION_BUILD);
-
Delete(file);
}
else
{
- LOG(Warning, "Failed to create version cache file");
+ LOG(Error, "Failed to create version cache file");
}
}
diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
index 27d47345d..7ecd197eb 100644
--- a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
+++ b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
@@ -110,6 +110,25 @@ namespace FlaxEditor.GUI.ContextMenu
_isSubMenu = true;
}
+ ///
+ /// Shows the empty menu popup o na screen.
+ ///
+ /// The target control.
+ /// The target control area to cover.
+ /// Created popup.
+ public static ContextMenuBase ShowEmptyMenu(Control control, Rectangle area)
+ {
+ // Calculate the control size in the window space to handle scaled controls
+ var upperLeft = control.PointToWindow(area.UpperLeft);
+ var bottomRight = control.PointToWindow(area.BottomRight);
+ var size = bottomRight - upperLeft;
+
+ var popup = new ContextMenuBase();
+ popup.Size = size;
+ popup.Show(control, area.Location + new Float2(0, (size.Y - popup.Height) * 0.5f));
+ return popup;
+ }
+
///
/// Show context menu over given control.
///
diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
index 8209b63cf..161b3f4ae 100644
--- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
+++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
@@ -25,6 +25,7 @@ namespace FlaxEditor.GUI.Dialogs
{
private const float ButtonsWidth = 60.0f;
private const float PickerMargin = 6.0f;
+ private const float EyedropperMargin = 8.0f;
private const float RGBAMargin = 12.0f;
private const float HSVMargin = 0.0f;
private const float ChannelsMargin = 4.0f;
@@ -34,6 +35,7 @@ namespace FlaxEditor.GUI.Dialogs
private Color _value;
private bool _disableEvents;
private bool _useDynamicEditing;
+ private bool _activeEyedropper;
private ColorValueBox.ColorPickerEvent _onChanged;
private ColorValueBox.ColorPickerClosedEvent _onClosed;
@@ -48,6 +50,7 @@ namespace FlaxEditor.GUI.Dialogs
private TextBox _cHex;
private Button _cCancel;
private Button _cOK;
+ private Button _cEyedropper;
///
/// Gets the selected color.
@@ -192,10 +195,44 @@ namespace FlaxEditor.GUI.Dialogs
};
_cOK.Clicked += OnSubmit;
+ // Eyedropper button
+ var style = Style.Current;
+ _cEyedropper = new Button(_cOK.X - EyedropperMargin, _cHex.Bottom + PickerMargin)
+ {
+ TooltipText = "Eyedropper tool to pick a color directly from the screen",
+ BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Search32),
+ BackgroundColor = style.Foreground,
+ BackgroundColorHighlighted = style.Foreground.RGBMultiplied(0.9f),
+ BorderColor = Color.Transparent,
+ BorderColorHighlighted = style.BorderSelected,
+ Parent = this,
+ };
+ _cEyedropper.Clicked += OnEyedropStart;
+ _cEyedropper.Height = (_cValue.Bottom - _cEyedropper.Y) * 0.5f;
+ _cEyedropper.Width = _cEyedropper.Height;
+ _cEyedropper.X -= _cEyedropper.Width;
+
// Set initial color
SelectedColor = initialValue;
}
+ private void OnColorPicked(Color32 colorPicked)
+ {
+ if (_activeEyedropper)
+ {
+ _activeEyedropper = false;
+ SelectedColor = colorPicked;
+ ScreenUtilities.PickColorDone -= OnColorPicked;
+ }
+ }
+
+ private void OnEyedropStart()
+ {
+ _activeEyedropper = true;
+ ScreenUtilities.PickColor();
+ ScreenUtilities.PickColorDone += OnColorPicked;
+ }
+
private void OnRGBAChanged()
{
if (_disableEvents)
@@ -221,6 +258,19 @@ namespace FlaxEditor.GUI.Dialogs
SelectedColor = color;
}
+ ///
+ public override void Update(float deltaTime)
+ {
+ base.Update(deltaTime);
+
+ // Update eye dropper tool
+ if (_activeEyedropper)
+ {
+ Float2 mousePosition = Platform.MousePosition;
+ SelectedColor = ScreenUtilities.GetColorAt(mousePosition);
+ }
+ }
+
///
public override void Draw()
{
@@ -274,6 +324,20 @@ namespace FlaxEditor.GUI.Dialogs
base.OnShow();
}
+ ///
+ public override bool OnKeyDown(KeyboardKeys key)
+ {
+ if (_activeEyedropper && key == KeyboardKeys.Escape)
+ {
+ // Cancel eye dropping
+ _activeEyedropper = false;
+ ScreenUtilities.PickColorDone -= OnColorPicked;
+ return true;
+ }
+
+ return base.OnKeyDown(key);
+ }
+
///
public override void OnSubmit()
{
diff --git a/Source/Editor/GUI/Input/ColorValueBox.cs b/Source/Editor/GUI/Input/ColorValueBox.cs
index bafa27c87..167cc65bb 100644
--- a/Source/Editor/GUI/Input/ColorValueBox.cs
+++ b/Source/Editor/GUI/Input/ColorValueBox.cs
@@ -57,6 +57,11 @@ namespace FlaxEditor.GUI.Input
///
protected Color _value;
+ ///
+ /// Enables live preview of the selected value from the picker. Otherwise will update the value only when user confirms it on dialog closing.
+ ///
+ public bool UseDynamicEditing = true;
+
///
/// Occurs when value gets changed.
///
@@ -143,7 +148,7 @@ namespace FlaxEditor.GUI.Input
base.OnSubmit();
// Show color picker dialog
- _currentDialog = ShowPickColorDialog?.Invoke(this, _value, OnColorChanged, OnPickerClosed);
+ _currentDialog = ShowPickColorDialog?.Invoke(this, _value, OnColorChanged, OnPickerClosed, UseDynamicEditing);
}
private void OnColorChanged(Color color, bool sliding)
diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
index 27b28596f..b8c920266 100644
--- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
+++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
@@ -726,8 +726,6 @@ namespace FlaxEditor.SceneGraph.GUI
{
DragData data;
var tree = ParentTree;
- if (tree.Selection.Count == 1)
- Select();
// Check if this node is selected
if (tree.Selection.Contains(this))
diff --git a/Source/Editor/Scripting/ScriptType.cs b/Source/Editor/Scripting/ScriptType.cs
index aa0ca5655..d174b7dff 100644
--- a/Source/Editor/Scripting/ScriptType.cs
+++ b/Source/Editor/Scripting/ScriptType.cs
@@ -832,7 +832,7 @@ namespace FlaxEditor.Scripting
get
{
if (_managed != null)
- return _managed.GetConstructor(Type.EmptyTypes) != null;
+ return _managed.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null) != null;
return _custom?.CanCreateInstance ?? false;
}
}
@@ -892,7 +892,12 @@ namespace FlaxEditor.Scripting
public object CreateInstance()
{
if (_managed != null)
- return Activator.CreateInstance(_managed);
+ {
+ var ctor = _managed.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
+ object value = RuntimeHelpers.GetUninitializedObject(_managed);
+ ctor.Invoke(value, null);
+ return value;
+ }
return _custom.CreateInstance();
}
diff --git a/Source/Editor/Surface/Elements/ColorValue.cs b/Source/Editor/Surface/Elements/ColorValue.cs
index 96069cfeb..19934581d 100644
--- a/Source/Editor/Surface/Elements/ColorValue.cs
+++ b/Source/Editor/Surface/Elements/ColorValue.cs
@@ -30,7 +30,7 @@ namespace FlaxEditor.Surface.Elements
{
ParentNode = parentNode;
Archetype = archetype;
-
+ UseDynamicEditing = false;
ParentNode.ValuesChanged += OnNodeValuesChanged;
}
diff --git a/Source/Editor/Utilities/EditorScene.cpp b/Source/Editor/Utilities/EditorScene.cpp
index 764b79184..7060baac5 100644
--- a/Source/Editor/Utilities/EditorScene.cpp
+++ b/Source/Editor/Utilities/EditorScene.cpp
@@ -20,4 +20,6 @@ void EditorScene::Update()
e.Call();
for (auto& e : Ticking.FixedUpdate.Ticks)
e.Call();
+ for (auto& e : Ticking.LateFixedUpdate.Ticks)
+ e.Call();
}
diff --git a/Source/Editor/Utilities/ScreenUtilities.cpp b/Source/Editor/Utilities/ScreenUtilities.cpp
new file mode 100644
index 000000000..44f52350e
--- /dev/null
+++ b/Source/Editor/Utilities/ScreenUtilities.cpp
@@ -0,0 +1,130 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+#include "ScreenUtilities.h"
+#include "Engine/Core/Math/Vector2.h"
+#include "Engine/Core/Delegate.h"
+#include "Engine/Core/Log.h"
+#include "Engine/Profiler/ProfilerCPU.h"
+
+Delegate ScreenUtilities::PickColorDone;
+
+#if PLATFORM_WINDOWS
+
+#include
+
+#pragma comment(lib, "Gdi32.lib")
+
+static HHOOK MouseCallbackHook;
+
+LRESULT CALLBACK OnScreenUtilsMouseCallback(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
+{
+ if (nCode >= 0 && wParam == WM_LBUTTONDOWN)
+ {
+ UnhookWindowsHookEx(MouseCallbackHook);
+
+ // Push event with the picked color
+ const Float2 cursorPos = Platform::GetMousePosition();
+ const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
+ ScreenUtilities::PickColorDone(colorPicked);
+ return 1;
+ }
+ return CallNextHookEx(NULL, nCode, wParam, lParam);
+}
+
+Color32 ScreenUtilities::GetColorAt(const Float2& pos)
+{
+ PROFILE_CPU();
+ HDC deviceContext = GetDC(NULL);
+ COLORREF color = GetPixel(deviceContext, (int)pos.X, (int)pos.Y);
+ ReleaseDC(NULL, deviceContext);
+ return Color32(GetRValue(color), GetGValue(color), GetBValue(color), 255);
+}
+
+void ScreenUtilities::PickColor()
+{
+ MouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, OnScreenUtilsMouseCallback, NULL, NULL);
+ if (MouseCallbackHook == NULL)
+ {
+ LOG(Warning, "Failed to set mouse hook.");
+ LOG(Warning, "Error: {0}", GetLastError());
+ }
+}
+
+#elif PLATFORM_LINUX
+
+#include "Engine/Platform/Linux/LinuxPlatform.h"
+#include "Engine/Platform/Linux/IncludeX11.h"
+
+Color32 ScreenUtilities::GetColorAt(const Float2& pos)
+{
+ X11::XColor color;
+
+ X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
+ int defaultScreen = X11::XDefaultScreen(display);
+
+ X11::XImage* image;
+ image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap);
+ color.pixel = XGetPixel(image, 0, 0);
+ X11::XFree(image);
+
+ X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
+
+ Color32 outputColor;
+ outputColor.R = color.red / 256;
+ outputColor.G = color.green / 256;
+ outputColor.B = color.blue / 256;
+ return outputColor;
+}
+
+void OnScreenUtilsXEventCallback(void* eventPtr)
+{
+ X11::XEvent* event = (X11::XEvent*) eventPtr;
+ X11::Display* display = (X11::Display*)LinuxPlatform::GetXDisplay();
+ if (event->type == ButtonPress)
+ {
+ const Float2 cursorPos = Platform::GetMousePosition();
+ const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
+ X11::XUngrabPointer(display, CurrentTime);
+ ScreenUtilities::PickColorDone(colorPicked);
+ LinuxPlatform::xEventRecieved.Unbind(OnScreenUtilsXEventCallback);
+ }
+}
+
+void ScreenUtilities::PickColor()
+{
+ PROFILE_CPU();
+ X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
+ X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
+
+ X11::Cursor cursor = XCreateFontCursor(display, 130);
+ int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
+ if (grabbedPointer != GrabSuccess)
+ {
+ LOG(Error, "Failed to grab cursor for events.");
+ X11::XFreeCursor(display, cursor);
+ return;
+ }
+
+ X11::XFreeCursor(display, cursor);
+ LinuxPlatform::xEventRecieved.Bind(OnScreenUtilsXEventCallback);
+}
+
+#elif PLATFORM_MAC
+
+#include
+#include
+
+Color32 ScreenUtilities::GetColorAt(const Float2& pos)
+{
+ // TODO: implement ScreenUtilities for macOS
+ return { 0, 0, 0, 255 };
+}
+
+void ScreenUtilities::PickColor()
+{
+ // This is what C# calls to start the color picking sequence
+ // This should stop mouse clicks from working for one click, and that click is on the selected color
+ // There is a class called NSColorSample that might implement that for you, but maybe not.
+}
+
+#endif
diff --git a/Source/Editor/Utilities/ScreenUtilities.h b/Source/Editor/Utilities/ScreenUtilities.h
new file mode 100644
index 000000000..506dc8634
--- /dev/null
+++ b/Source/Editor/Utilities/ScreenUtilities.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#include "Engine/Core/Types/BaseTypes.h"
+#include "Engine/Core/Math/Color32.h"
+#include "Engine/Core/Math/Vector2.h"
+#include "Engine/Core/Delegate.h"
+
+///
+/// Platform-dependent screen utilities.
+///
+API_CLASS(Static) class FLAXENGINE_API ScreenUtilities
+{
+ DECLARE_SCRIPTING_TYPE_MINIMAL(ScreenUtilities);
+
+ ///
+ /// Gets the pixel color at the specified coordinates.
+ ///
+ /// Screen-space coordinate to read.
+ /// Pixel color at the specified coordinates.
+ API_FUNCTION() static Color32 GetColorAt(const Float2& pos);
+
+ ///
+ /// Starts async color picking. Color will be returned through PickColorDone event when the actions ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value.
+ ///
+ API_FUNCTION() static void PickColor();
+
+ ///
+ /// Called when PickColor action is finished.
+ ///
+ API_EVENT() static Delegate PickColorDone;
+};
diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs
index 7aca2526a..a44c35cb3 100644
--- a/Source/Editor/Windows/ContentWindow.cs
+++ b/Source/Editor/Windows/ContentWindow.cs
@@ -612,7 +612,16 @@ namespace FlaxEditor.Windows
/// The files paths to import.
public void Paste(string[] files)
{
- Editor.ContentImporting.Import(files, CurrentViewFolder);
+ var importFiles = new List();
+ foreach (var sourcePath in files)
+ {
+ var item = Editor.ContentDatabase.Find(sourcePath);
+ if (item != null)
+ Editor.ContentDatabase.Copy(item, Path.Combine(CurrentViewFolder.Path, item.FileName));
+ else
+ importFiles.Add(sourcePath);
+ }
+ Editor.ContentImporting.Import(importFiles, CurrentViewFolder);
}
///
diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs
index 91da504e8..0b2249a33 100644
--- a/Source/Editor/Windows/OutputLogWindow.cs
+++ b/Source/Editor/Windows/OutputLogWindow.cs
@@ -542,7 +542,7 @@ namespace FlaxEditor.Windows
{
ref var line = ref lines[j];
textBlock.Range.StartIndex = startIndex + line.FirstCharIndex;
- textBlock.Range.EndIndex = startIndex + line.LastCharIndex;
+ textBlock.Range.EndIndex = startIndex + line.LastCharIndex + 1;
textBlock.Bounds = new Rectangle(new Float2(0.0f, prevBlockBottom), line.Size);
if (textBlock.Range.Length > 0)
@@ -551,7 +551,7 @@ namespace FlaxEditor.Windows
var regexStart = line.FirstCharIndex;
if (j == 0)
regexStart += prefixLength;
- var regexLength = line.LastCharIndex - regexStart;
+ var regexLength = line.LastCharIndex + 1 - regexStart;
if (regexLength > 0)
{
var match = _compileRegex.Match(entryText, regexStart, regexLength);
diff --git a/Source/Editor/Windows/Profiler/Network.cs b/Source/Editor/Windows/Profiler/Network.cs
index 662ccf445..f0b93a03c 100644
--- a/Source/Editor/Windows/Profiler/Network.cs
+++ b/Source/Editor/Windows/Profiler/Network.cs
@@ -52,7 +52,7 @@ namespace FlaxEditor.Windows.Profiler
private static string FormatSampleBytes(float v)
{
- return (uint)v + " bytes";
+ return Utilities.Utils.FormatBytesCount((ulong)v);
}
///
diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp
index e58aa458d..d57529b92 100644
--- a/Source/Engine/Content/Content.cpp
+++ b/Source/Engine/Content/Content.cpp
@@ -760,9 +760,14 @@ bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPat
// Change asset ID
{
auto storage = ContentStorageManager::GetStorage(tmpPath);
+ if (!storage)
+ {
+ LOG(Warning, "Cannot change asset ID.");
+ return true;
+ }
FlaxStorage::Entry e;
storage->GetEntry(0, e);
- if (!storage || storage->ChangeAssetID(e, dstId))
+ if (storage->ChangeAssetID(e, dstId))
{
LOG(Warning, "Cannot change asset ID.");
return true;
diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp
index 8608a34f6..52d7c34b5 100644
--- a/Source/Engine/Content/Storage/FlaxStorage.cpp
+++ b/Source/Engine/Content/Storage/FlaxStorage.cpp
@@ -211,6 +211,12 @@ FlaxStorage::~FlaxStorage()
CHECK(_chunksLock == 0);
CHECK(_refCount == 0);
ASSERT(_chunks.IsEmpty());
+
+#if USE_EDITOR
+ // Ensure to close any outstanding file handles to prevent file locking in case it failed to load
+ _file.DeleteAll();
+#endif
+
}
FlaxStorage::LockData FlaxStorage::LockSafe()
diff --git a/Source/Engine/Core/Collections/BitArray.h b/Source/Engine/Core/Collections/BitArray.h
index 6058dc8a5..01238d434 100644
--- a/Source/Engine/Core/Collections/BitArray.h
+++ b/Source/Engine/Core/Collections/BitArray.h
@@ -214,7 +214,7 @@ public:
///
/// The index of the item.
/// The value to set.
- void Set(int32 index, bool value) const
+ void Set(int32 index, bool value)
{
ASSERT(index >= 0 && index < _count);
const ItemType offset = index / sizeof(ItemType);
diff --git a/Source/Engine/Core/Delegate.h b/Source/Engine/Core/Delegate.h
index 21a4374ea..4fcf60390 100644
--- a/Source/Engine/Core/Delegate.h
+++ b/Source/Engine/Core/Delegate.h
@@ -203,23 +203,12 @@ public:
return _function != nullptr;
}
- ///
- /// Calls the binded function if any has been assigned.
- ///
- /// A list of parameters for the function invocation.
- /// Function result
- void TryCall(Params ... params) const
- {
- if (_function)
- _function(_callee, Forward(params)...);
- }
-
///
/// Calls the binded function (it must be assigned).
///
/// A list of parameters for the function invocation.
/// Function result
- ReturnType operator()(Params ... params) const
+ FORCE_INLINE ReturnType operator()(Params ... params) const
{
ASSERT(_function);
return _function(_callee, Forward(params)...);
diff --git a/Source/Engine/Core/ObjectsRemovalService.cpp b/Source/Engine/Core/ObjectsRemovalService.cpp
index 77ce686e0..35458595e 100644
--- a/Source/Engine/Core/ObjectsRemovalService.cpp
+++ b/Source/Engine/Core/ObjectsRemovalService.cpp
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "ObjectsRemovalService.h"
+#include "Utilities.h"
#include "Collections/Dictionary.h"
#include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h"
@@ -8,6 +9,11 @@
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/ScriptingObject.h"
+const Char* BytesSizesData[] = { TEXT("b"), TEXT("Kb"), TEXT("Mb"), TEXT("Gb"), TEXT("Tb"), TEXT("Pb"), TEXT("Eb"), TEXT("Zb"), TEXT("Yb") };
+const Char* HertzSizesData[] = { TEXT("Hz"), TEXT("KHz"), TEXT("MHz"), TEXT("GHz"), TEXT("THz"), TEXT("PHz"), TEXT("EHz"), TEXT("ZHz"), TEXT("YHz") };
+Span Utilities::Private::BytesSizes(BytesSizesData, ARRAY_COUNT(BytesSizesData));
+Span Utilities::Private::HertzSizes(HertzSizesData, ARRAY_COUNT(HertzSizesData));
+
namespace ObjectsRemovalServiceImpl
{
CriticalSection PoolLocker;
diff --git a/Source/Engine/Core/Utilities.h b/Source/Engine/Core/Utilities.h
index c65200077..36339baf5 100644
--- a/Source/Engine/Core/Utilities.h
+++ b/Source/Engine/Core/Utilities.h
@@ -4,12 +4,19 @@
#include "Types/BaseTypes.h"
#include "Types/String.h"
+#include "Types/Span.h"
#if _MSC_VER && PLATFORM_SIMD_SSE4_2
#include
#endif
namespace Utilities
{
+ struct Private
+ {
+ static FLAXENGINE_API Span BytesSizes;
+ static FLAXENGINE_API Span HertzSizes;
+ };
+
// Round floating point value up to 1 decimal place
template
FORCE_INLINE T RoundTo1DecimalPlace(T value)
@@ -31,20 +38,41 @@ namespace Utilities
return (T)round((double)value * 1000.0) / (T)1000;
}
+ // Converts units to the best fitting human-readable denominator
+ // @param units Units count
+ // @param divider Amount of units required for the next size
+ // @param sizes Array with human-readable sizes to convert from
+ // @return The best fitting string of the units
+ template
+ String UnitsToText(T units, int32 divider, const Span sizes)
+ {
+ if (sizes.Length() == 0)
+ return String::Format(TEXT("{0}"), units);
+ int32 i = 0;
+ double dblSUnits = static_cast(units);
+ for (; static_cast(units / static_cast(divider)) > 0; i++, units /= divider)
+ dblSUnits = units / static_cast(divider);
+ if (i >= sizes.Length())
+ i = 0;
+ return String::Format(TEXT("{0} {1}"), RoundTo2DecimalPlaces(dblSUnits), sizes[i]);
+ }
+
// Converts size of the file (in bytes) to the best fitting string
// @param bytes Size of the file in bytes
// @return The best fitting string of the file size
template
String BytesToText(T bytes)
{
- static const Char* sizes[] = { TEXT("B"), TEXT("KB"), TEXT("MB"), TEXT("GB"), TEXT("TB") };
- uint64 i = 0;
- double dblSByte = static_cast(bytes);
- for (; static_cast(bytes / 1024.0) > 0; i++, bytes /= 1024)
- dblSByte = bytes / 1024.0;
- if (i >= ARRAY_COUNT(sizes))
- return String::Empty;
- return String::Format(TEXT("{0} {1}"), RoundTo2DecimalPlaces(dblSByte), sizes[i]);
+ return UnitsToText(bytes, 1024, Private::BytesSizes);
+ }
+
+ // Converts hertz to the best fitting string
+ // @param hertz Hertz for convertion
+ // @return The best fitting string
+ template
+ String HertzToText(T hertz)
+ {
+ return UnitsToText(hertz, 1000, Private::HertzSizes);
}
// Returns the amount of set bits in 32-bit integer.
diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp
index 160e07a91..bafcd30b3 100644
--- a/Source/Engine/Engine/Engine.cpp
+++ b/Source/Engine/Engine/Engine.cpp
@@ -66,6 +66,7 @@ Action Engine::FixedUpdate;
Action Engine::Update;
TaskGraph* Engine::UpdateGraph = nullptr;
Action Engine::LateUpdate;
+Action Engine::LateFixedUpdate;
Action Engine::Draw;
Action Engine::Pause;
Action Engine::Unpause;
@@ -199,6 +200,7 @@ int32 Engine::Main(const Char* cmdLine)
if (Time::OnBeginPhysics())
{
OnFixedUpdate();
+ OnLateFixedUpdate();
Time::OnEndPhysics();
}
@@ -274,6 +276,17 @@ void Engine::OnFixedUpdate()
}
}
+void Engine::OnLateFixedUpdate()
+{
+ PROFILE_CPU_NAMED("Late Fixed Update");
+
+ // Call event
+ LateFixedUpdate();
+
+ // Update services
+ EngineService::OnLateFixedUpdate();
+}
+
void Engine::OnUpdate()
{
PROFILE_CPU_NAMED("Update");
diff --git a/Source/Engine/Engine/Engine.h b/Source/Engine/Engine/Engine.h
index 6f8c5fe6e..31d1f6ea6 100644
--- a/Source/Engine/Engine/Engine.h
+++ b/Source/Engine/Engine/Engine.h
@@ -54,6 +54,11 @@ public:
///
static Action LateUpdate;
+ ///
+ /// Event called after engine update.
+ ///
+ static Action LateFixedUpdate;
+
///
/// Event called during frame rendering and can be used to invoke custom rendering with GPUDevice.
///
@@ -107,6 +112,11 @@ public:
///
static void OnLateUpdate();
+ ///
+ /// Late fixed update callback.
+ ///
+ static void OnLateFixedUpdate();
+
///
/// Draw callback.
///
diff --git a/Source/Engine/Engine/EngineService.cpp b/Source/Engine/Engine/EngineService.cpp
index 9a01b08b5..7eea66853 100644
--- a/Source/Engine/Engine/EngineService.cpp
+++ b/Source/Engine/Engine/EngineService.cpp
@@ -33,6 +33,7 @@ static bool CompareEngineServices(EngineService* const& a, EngineService* const&
DEFINE_ENGINE_SERVICE_EVENT(FixedUpdate);
DEFINE_ENGINE_SERVICE_EVENT(Update);
DEFINE_ENGINE_SERVICE_EVENT(LateUpdate);
+DEFINE_ENGINE_SERVICE_EVENT(LateFixedUpdate);
DEFINE_ENGINE_SERVICE_EVENT(Draw);
DEFINE_ENGINE_SERVICE_EVENT_INVERTED(BeforeExit);
diff --git a/Source/Engine/Engine/EngineService.h b/Source/Engine/Engine/EngineService.h
index f4ae1b271..a142455d0 100644
--- a/Source/Engine/Engine/EngineService.h
+++ b/Source/Engine/Engine/EngineService.h
@@ -44,6 +44,7 @@ public:
DECLARE_ENGINE_SERVICE_EVENT(void, FixedUpdate);
DECLARE_ENGINE_SERVICE_EVENT(void, Update);
DECLARE_ENGINE_SERVICE_EVENT(void, LateUpdate);
+ DECLARE_ENGINE_SERVICE_EVENT(void, LateFixedUpdate);
DECLARE_ENGINE_SERVICE_EVENT(void, Draw);
DECLARE_ENGINE_SERVICE_EVENT(void, BeforeExit);
DECLARE_ENGINE_SERVICE_EVENT(void, Dispose);
diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs
index 41ace593b..538703d4e 100644
--- a/Source/Engine/Engine/NativeInterop.Managed.cs
+++ b/Source/Engine/Engine/NativeInterop.Managed.cs
@@ -16,6 +16,9 @@ namespace FlaxEngine.Interop
///
/// Wrapper for managed arrays which are passed to unmanaged code.
///
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
public unsafe class ManagedArray
{
private ManagedHandle _pinnedArrayHandle;
@@ -261,11 +264,14 @@ namespace FlaxEngine.Interop
return;
}
- throw new Exception("Tried to free non-pooled ManagedArray as pooled ManagedArray");
+ throw new NativeInteropException("Tried to free non-pooled ManagedArray as pooled ManagedArray");
}
}
}
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
internal static class ManagedString
{
internal static ManagedHandle EmptyStringHandle = ManagedHandle.Alloc(string.Empty);
@@ -315,6 +321,9 @@ namespace FlaxEngine.Interop
///
/// Handle to managed objects which can be stored in native code.
///
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
public struct ManagedHandle
{
private IntPtr handle;
@@ -499,7 +508,7 @@ namespace FlaxEngine.Interop
else if (weakPoolOther.TryGetValue(handle, out value))
return value;
- throw new Exception("Invalid ManagedHandle");
+ throw new NativeInteropException("Invalid ManagedHandle");
}
internal static void SetObject(IntPtr handle, object value)
@@ -527,7 +536,7 @@ namespace FlaxEngine.Interop
else if (weakPoolOther.ContainsKey(handle))
weakPoolOther[handle] = value;
- throw new Exception("Invalid ManagedHandle");
+ throw new NativeInteropException("Invalid ManagedHandle");
}
internal static void FreeHandle(IntPtr handle)
@@ -556,7 +565,7 @@ namespace FlaxEngine.Interop
else
return;
- throw new Exception("Invalid ManagedHandle");
+ throw new NativeInteropException("Invalid ManagedHandle");
}
}
}
diff --git a/Source/Engine/Engine/NativeInterop.Marshallers.cs b/Source/Engine/Engine/NativeInterop.Marshallers.cs
index 148f02539..a3531d11d 100644
--- a/Source/Engine/Engine/NativeInterop.Marshallers.cs
+++ b/Source/Engine/Engine/NativeInterop.Marshallers.cs
@@ -12,6 +12,9 @@ using System.Runtime.InteropServices.Marshalling;
namespace FlaxEngine.Interop
{
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
[CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedHandleMarshaller.ManagedToNative))]
[CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(ManagedHandleMarshaller.ManagedToNative))]
[CustomMarshaller(typeof(object), MarshalMode.ElementIn, typeof(ManagedHandleMarshaller.ManagedToNative))]
@@ -23,6 +26,9 @@ namespace FlaxEngine.Interop
[CustomMarshaller(typeof(object), MarshalMode.ElementRef, typeof(ManagedHandleMarshaller))]
public static class ManagedHandleMarshaller
{
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
public static class NativeToManaged
{
public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : ManagedHandle.FromIntPtr(unmanaged).Target;
@@ -33,6 +39,9 @@ namespace FlaxEngine.Interop
}
}
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
public static class ManagedToNative
{
public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
@@ -48,6 +57,9 @@ namespace FlaxEngine.Interop
}
}
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
public struct Bidirectional
{
object managed;
@@ -99,6 +111,9 @@ namespace FlaxEngine.Interop
}
}
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
[CustomMarshaller(typeof(Type), MarshalMode.Default, typeof(SystemTypeMarshaller))]
public static class SystemTypeMarshaller
{
@@ -118,6 +133,9 @@ namespace FlaxEngine.Interop
}
}
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
[CustomMarshaller(typeof(Exception), MarshalMode.Default, typeof(ExceptionMarshaller))]
public static class ExceptionMarshaller
{
@@ -126,6 +144,9 @@ namespace FlaxEngine.Interop
public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged);
}
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedIn, typeof(ObjectMarshaller.ManagedToNative))]
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedOut, typeof(ObjectMarshaller.ManagedToNative))]
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementIn, typeof(ObjectMarshaller.ManagedToNative))]
@@ -134,17 +155,26 @@ namespace FlaxEngine.Interop
[CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementOut, typeof(ObjectMarshaller.NativeToManaged))]
public static class ObjectMarshaller
{
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
public static class NativeToManaged
{
public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As(ManagedHandle.FromIntPtr(unmanaged).Target) : null;
}
+#if FLAX_EDITOR
+ [HideInEditor]
+#endif
public static class ManagedToNative
{
public static IntPtr ConvertToUnmanaged(FlaxEngine.Object managed) => Unsafe.As