diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index 2cbd9cd85..c42bd9d86 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -490,6 +490,60 @@ namespace FlaxEditor.Modules return; } + var projects = Editor.ContentDatabase.Projects; + var contentPaths = new List(); + var sourcePaths = new List(); + foreach (var project in projects) + { + if (project.Content != null) + contentPaths.Add(project.Content.Path); + if (project.Source != null) + sourcePaths.Add(project.Source.Path); + } + + // Check if moving from content to source folder. Item may lose reference in Asset Database. Warn user. + foreach (var contentPath in contentPaths) + { + if (item.Path.Contains(contentPath, StringComparison.Ordinal)) + { + bool isFound = false; + foreach (var sourcePath in sourcePaths) + { + if (newParent.Path.Contains(sourcePath, StringComparison.Ordinal)) + { + isFound = true; + var result = MessageBox.Show(Editor.Windows.MainWindow, "Moving item from \"Content\" to \"Source\" folder may lose asset database reference.\nDo you want to continue?", "Moving item", MessageBoxButtons.OKCancel); + if (result == DialogResult.Cancel) + return; + break; + } + } + if (isFound) + break; + } + } + // Check if moving from source to content folder. Item may lose reference in Asset Database. Warn user. + foreach (var sourcePath in sourcePaths) + { + if (item.Path.Contains(sourcePath, StringComparison.Ordinal)) + { + bool isFound = false; + foreach (var contentPath in contentPaths) + { + if (newParent.Path.Contains(contentPath, StringComparison.Ordinal)) + { + isFound = true; + var result = MessageBox.Show(Editor.Windows.MainWindow, "Moving item from \"Source\" to \"Content\" folder may lose asset database reference.\nDo you want to continue?", "Moving item", MessageBoxButtons.OKCancel); + if (result == DialogResult.Cancel) + return; + break; + } + } + if (isFound) + break; + } + } + // Perform renaming { string oldPath = item.Path; diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index 13a920fa3..841f2b5dd 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -555,10 +555,20 @@ namespace FlaxEditor.Modules /// public void CreateParentForSelectedActors() { - Actor actor = new EmptyActor(); - Editor.SceneEditing.Spawn(actor, null, false); List selection = Editor.SceneEditing.Selection; var actors = selection.Where(x => x is ActorNode).Select(x => ((ActorNode)x).Actor); + var actorsCount = actors.Count(); + if (actorsCount == 0) + return; + Vector3 center = Vector3.Zero; + foreach (var actor in actors) + center += actor.Position; + center /= actorsCount; + Actor parent = new EmptyActor + { + Position = center, + }; + Editor.SceneEditing.Spawn(parent, null, false); using (new UndoMultiBlock(Undo, actors, "Reparent actors")) { for (int i = 0; i < selection.Count; i++) @@ -574,15 +584,15 @@ namespace FlaxEditor.Modules // Put created node as child of the Parent Node of node int parentOrder = node.Actor.OrderInParent; - actor.Parent = node.Actor.Parent; - actor.OrderInParent = parentOrder; + parent.SetParent(node.Actor.Parent, true, true); + parent.OrderInParent = parentOrder; } - node.Actor.Parent = actor; + node.Actor.SetParent(parent, true, false); } } } - Editor.SceneEditing.Select(actor); - Editor.Scene.GetActorNode(actor).TreeNode.StartRenaming(Editor.Windows.SceneWin, Editor.Windows.SceneWin.SceneTreePanel); + Editor.SceneEditing.Select(parent); + Editor.Scene.GetActorNode(parent).TreeNode.StartRenaming(Editor.Windows.SceneWin, Editor.Windows.SceneWin.SceneTreePanel); } /// diff --git a/Source/Editor/Modules/SimulationModule.cs b/Source/Editor/Modules/SimulationModule.cs index 6f54ab10d..bb9df0950 100644 --- a/Source/Editor/Modules/SimulationModule.cs +++ b/Source/Editor/Modules/SimulationModule.cs @@ -2,6 +2,7 @@ using System; using System.Threading; +using FlaxEditor.GUI.Docking; using FlaxEditor.States; using FlaxEditor.Windows; using FlaxEngine; @@ -20,6 +21,7 @@ namespace FlaxEditor.Modules private bool _updateOrFixedUpdateWasCalled; private long _breakpointHangFlag; private EditorWindow _enterPlayFocusedWindow; + private DockWindow _previousWindow; private Guid[] _scenesToReload; internal SimulationModule(Editor editor) @@ -272,23 +274,43 @@ namespace FlaxEditor.Modules // Show Game widow if hidden if (gameWin != null) { - if (gameWin.FocusOnPlay) + switch (gameWin.FocusOnPlayOption) + { + case Options.InterfaceOptions.PlayModeFocus.None: break; + + case Options.InterfaceOptions.PlayModeFocus.GameWindow: gameWin.FocusGameViewport(); + break; + + case Options.InterfaceOptions.PlayModeFocus.GameWindowThenRestore: + _previousWindow = gameWin.ParentDockPanel.SelectedTab; + gameWin.FocusGameViewport(); + break; + } + gameWin.SetWindowMode(Editor.Options.Options.Interface.DefaultGameWindowMode); } - Editor.Log("[PlayMode] Enter"); } /// public override void OnPlayEnd() { - // Restore focused window before play mode - if (_enterPlayFocusedWindow != null) + var gameWin = Editor.Windows.GameWin; + + switch (gameWin.FocusOnPlayOption) { - _enterPlayFocusedWindow.FocusOrShow(); - _enterPlayFocusedWindow = null; + 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; } Editor.UI.UncheckPauseButton(); diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 6a197c62f..f6d140e0e 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -556,7 +556,7 @@ namespace FlaxEditor.Modules cm.AddSeparator(); _menuEditSelectAll = cm.AddButton("Select all", inputOptions.SelectAll, Editor.SceneEditing.SelectAllScenes); _menuEditDeselectAll = cm.AddButton("Deselect all", inputOptions.DeselectAll, Editor.SceneEditing.DeselectAllScenes); - _menuCreateParentForSelectedActors = cm.AddButton("Create parent for selected actors", Editor.SceneEditing.CreateParentForSelectedActors); + _menuCreateParentForSelectedActors = cm.AddButton("Parent to new Actor", inputOptions.GroupSelectedActors, Editor.SceneEditing.CreateParentForSelectedActors); _menuEditFind = cm.AddButton("Find", inputOptions.Search, Editor.Windows.SceneWin.Search); cm.AddSeparator(); cm.AddButton("Game Settings", () => diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index 211e8e54f..76610f675 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -160,6 +160,10 @@ namespace FlaxEditor.Options [EditorDisplay("Scene"), EditorOrder(573)] public InputBinding PilotActor = new InputBinding(KeyboardKeys.None); + [DefaultValue(typeof(InputBinding), "Ctrl+G")] + [EditorDisplay("Scene"), EditorOrder(574)] + public InputBinding GroupSelectedActors = new InputBinding(KeyboardKeys.G, KeyboardKeys.Control); + #endregion #region Tools diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 30cb64aa8..ec46ccdd9 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -1,8 +1,8 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System; using System.ComponentModel; using FlaxEditor.GUI.Docking; -using FlaxEditor.Utilities; using FlaxEngine; using FlaxEngine.GUI; @@ -139,6 +139,27 @@ namespace FlaxEditor.Options AutoUnit, } + /// + /// Options focus Game Window behaviour when play mode is entered. + /// + public enum PlayModeFocus + { + /// + /// Don't change focus. + /// + None, + + /// + /// Focus the Game Window. + /// + GameWindow, + + /// + /// Focus the Game Window. On play mode end restore focus to the previous window. + /// + GameWindowThenRestore, + } + /// /// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required. /// @@ -339,6 +360,18 @@ namespace FlaxEditor.Options [EditorDisplay("Output Log", "Text Shadow Offset"), EditorOrder(445), Tooltip("The output log text shadow offset. Set to 0 to disable this feature.")] public Float2 OutputLogTextShadowOffset { get; set; } = new Float2(1); + // [Deprecated in v1.10] + [Serialize, Obsolete, NoUndo] + private bool FocusGameWinOnPlay + { + get => throw new Exception(); + set + { + // Upgrade value + FocusOnPlayMode = value ? PlayModeFocus.GameWindow : PlayModeFocus.None; + } + } + /// /// Gets or sets a value indicating whether auto-focus output log window on code compilation error. /// @@ -361,11 +394,11 @@ namespace FlaxEditor.Options public bool OutputLogScrollToBottom { get; set; } = true; /// - /// Gets or sets a value indicating whether auto-focus game window on play mode start. + /// Gets or sets a value indicating what panel should be focused when play mode start. /// - [DefaultValue(true)] - [EditorDisplay("Play In-Editor", "Focus Game Window On Play"), EditorOrder(500), Tooltip("Determines whether auto-focus game window on play mode start.")] - public bool FocusGameWinOnPlay { get; set; } = true; + [DefaultValue(PlayModeFocus.GameWindow)] + [EditorDisplay("Play In-Editor", "Focus On Play"), EditorOrder(500), Tooltip("Set what panel to focus on play mode start.")] + public PlayModeFocus FocusOnPlayMode { get; set; } = PlayModeFocus.GameWindow; /// /// Gets or sets a value indicating what action should be taken upon pressing the play button. diff --git a/Source/Editor/States/ChangingScenesState.cs b/Source/Editor/States/ChangingScenesState.cs index 4fe543153..17fa30ff7 100644 --- a/Source/Editor/States/ChangingScenesState.cs +++ b/Source/Editor/States/ChangingScenesState.cs @@ -109,29 +109,42 @@ namespace FlaxEditor.States private void TryEnter() { - // Remove redundant scene changes - for (int i = 0; i < _scenesToUnload.Count; i++) + // Reload existing scene if only 1 scene exists + bool reloadExistingScene = false; + if (Level.Scenes.Length == 1 && _scenesToLoad.Count == 1) { - var id = _scenesToUnload[i].ID; - for (int j = 0; j < _scenesToLoad.Count; j++) + if (Level.FindScene(_scenesToLoad[0])) { - if (_scenesToLoad[j] == id) - { - _scenesToLoad.RemoveAt(j--); - _scenesToUnload.RemoveAt(i); - break; - } + reloadExistingScene = true; } } - // Skip already loaded scenes - for (int j = 0; j < _scenesToLoad.Count; j++) + if (!reloadExistingScene) { - if (Level.FindScene(_scenesToLoad[j])) + // Remove redundant scene changes + for (int i = 0; i < _scenesToUnload.Count; i++) { - _scenesToLoad.RemoveAt(j--); - if (_scenesToLoad.Count == 0) - break; + var id = _scenesToUnload[i].ID; + for (int j = 0; j < _scenesToLoad.Count; j++) + { + if (_scenesToLoad[j] == id) + { + _scenesToLoad.RemoveAt(j--); + _scenesToUnload.RemoveAt(i); + break; + } + } + } + + // Skip already loaded scenes + for (int j = 0; j < _scenesToLoad.Count; j++) + { + if (Level.FindScene(_scenesToLoad[j])) + { + _scenesToLoad.RemoveAt(j--); + if (_scenesToLoad.Count == 0) + break; + } } } diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 147bd71b7..09a059251 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1432,6 +1432,7 @@ namespace FlaxEditor.Utilities inputActions.Add(options => options.SelectAll, Editor.Instance.SceneEditing.SelectAllScenes); inputActions.Add(options => options.DeselectAll, Editor.Instance.SceneEditing.DeselectAllScenes); inputActions.Add(options => options.Delete, Editor.Instance.SceneEditing.Delete); + inputActions.Add(options => options.GroupSelectedActors, Editor.Instance.SceneEditing.CreateParentForSelectedActors); inputActions.Add(options => options.Search, () => Editor.Instance.Windows.SceneWin.Search()); inputActions.Add(options => options.MoveActorToViewport, Editor.Instance.UI.MoveActorToViewport); inputActions.Add(options => options.AlignActorWithViewport, Editor.Instance.UI.AlignActorWithViewport); diff --git a/Source/Editor/Windows/AssetReferencesGraphWindow.cs b/Source/Editor/Windows/AssetReferencesGraphWindow.cs index 64eef8224..5d0eee183 100644 --- a/Source/Editor/Windows/AssetReferencesGraphWindow.cs +++ b/Source/Editor/Windows/AssetReferencesGraphWindow.cs @@ -2,7 +2,9 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using FlaxEditor.Content; @@ -137,6 +139,7 @@ namespace FlaxEditor.Windows } } + private string _cacheFolder; private Guid _assetId; private Surface _surface; private Label _loadingLabel; @@ -161,6 +164,9 @@ namespace FlaxEditor.Windows { Title = assetItem.ShortName + " References"; + _cacheFolder = Path.Combine(Globals.ProjectCacheFolder, "References"); + if (!Directory.Exists(_cacheFolder)) + Directory.CreateDirectory(_cacheFolder); _assetId = assetItem.ID; _surface = new Surface(this) { @@ -189,7 +195,7 @@ namespace FlaxEditor.Windows return node; } - private void SearchRefs(Guid assetId) + private unsafe void SearchRefs(Guid assetId) { // Skip assets that never contain references to prevent loading them if (FlaxEngine.Content.GetAssetInfo(assetId, out var assetInfo) && @@ -202,8 +208,24 @@ namespace FlaxEditor.Windows if (_refs.ContainsKey(assetId)) return; + // Try to load cached references form previous run + var cachePath = Path.Combine(_cacheFolder, $"{FlaxEngine.Json.JsonSerializer.GetStringID(assetId)}.1.cache"); + var hasInfo = FlaxEngine.Content.GetAssetInfo(assetId, out var info); + if (hasInfo && File.Exists(cachePath) && File.GetLastWriteTime(cachePath) > File.GetLastWriteTime(info.Path)) + { + byte[] rawData = File.ReadAllBytes(cachePath); + Guid[] loadedRefs = new Guid[rawData.Length / sizeof(Guid)]; + if (rawData.Length != 0) + { + fixed (byte* rawDataPtr = rawData) + fixed (Guid* loadedRefsPtr = loadedRefs) + Unsafe.CopyBlock(loadedRefsPtr, rawDataPtr, (uint)rawData.Length); + } + _refs[assetId] = loadedRefs; + return; + } + // Load asset (with cancel support) - //Debug.Log("Searching refs for " + assetInfo.Path); var obj = FlaxEngine.Object.TryFind(ref assetId); if (obj is Scene scene) { @@ -226,7 +248,21 @@ namespace FlaxEditor.Windows return; // Get direct references - _refs[assetId] = asset.GetReferences(); + var references = asset.GetReferences(); + _refs[assetId] = references; + + // Save reference to the cache + if (hasInfo) + { + byte[] rawData = new byte[references.Length * sizeof(Guid)]; + if (rawData.Length != 0) + { + fixed (byte* rawDataPtr = rawData) + fixed (Guid* referencesPtr = references) + Unsafe.CopyBlock(rawDataPtr, referencesPtr, (uint)rawData.Length); + } + File.WriteAllBytes(cachePath, rawData); + } } private void BuildGraph(AssetNode node, int level, bool reverse) diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index edcff9b98..8bcc4dc68 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -39,6 +39,28 @@ namespace FlaxEditor.Windows private bool _useAspect = false; private bool _freeAspect = true; + private List _focusOptions = new List() + { + new PlayModeFocusOptions + { + Name = "None", + Tooltip = "Don't change focus.", + FocusOption = InterfaceOptions.PlayModeFocus.None, + }, + new PlayModeFocusOptions + { + Name = "Game Window", + Tooltip = "Focus the Game Window.", + FocusOption = InterfaceOptions.PlayModeFocus.GameWindow, + }, + new PlayModeFocusOptions + { + Name = "Game Window Then Restore", + Tooltip = "Focus the Game Window. On play mode end restore focus to the previous window.", + FocusOption = InterfaceOptions.PlayModeFocus.GameWindowThenRestore, + }, + }; + /// /// Gets the viewport. /// @@ -162,9 +184,9 @@ namespace FlaxEditor.Windows public bool CenterMouseOnFocus { get; set; } /// - /// Gets or sets a value indicating whether auto-focus game window on play mode start. + /// Gets or sets a value indicating what panel should be focused when play mode start. /// - public bool FocusOnPlay { get; set; } + public InterfaceOptions.PlayModeFocus FocusOnPlayOption { get; set; } private enum ViewportScaleType { @@ -195,6 +217,29 @@ namespace FlaxEditor.Windows public bool Active; } + private class PlayModeFocusOptions + { + /// + /// The name. + /// + public string Name; + + /// + /// The tooltip. + /// + public string Tooltip; + + /// + /// The type of focus. + /// + public InterfaceOptions.PlayModeFocus FocusOption; + + /// + /// If the option is active. + /// + public bool Active; + } + /// /// Root control for game UI preview in Editor. Supports basic UI editing via . /// @@ -432,7 +477,7 @@ namespace FlaxEditor.Windows private void OnOptionsChanged(EditorOptions options) { CenterMouseOnFocus = options.Interface.CenterMouseOnGameWinFocus; - FocusOnPlay = options.Interface.FocusGameWinOnPlay; + FocusOnPlayOption = options.Interface.FocusOnPlayMode; } private void PlayingStateOnSceneDuplicating() @@ -497,10 +542,15 @@ namespace FlaxEditor.Windows // Focus on play { - var focus = menu.AddButton("Start Focused"); - focus.CloseMenuOnClick = false; - var checkbox = new CheckBox(140, 2, FocusOnPlay) { Parent = focus }; - checkbox.StateChanged += state => FocusOnPlay = state.Checked; + var pfMenu = menu.AddChildMenu("Focus On Play Override").ContextMenu; + + GenerateFocusOptionsContextMenu(pfMenu); + + pfMenu.AddSeparator(); + + var button = pfMenu.AddButton("Remove override"); + button.TooltipText = "Reset the override to the value set in the editor options."; + button.Clicked += () => FocusOnPlayOption = Editor.Instance.Options.Options.Interface.FocusOnPlayMode; } menu.AddSeparator(); @@ -602,6 +652,40 @@ namespace FlaxEditor.Windows menu.AddSeparator(); } + private void GenerateFocusOptionsContextMenu(ContextMenu pfMenu) + { + foreach (PlayModeFocusOptions f in _focusOptions) + { + f.Active = f.FocusOption == FocusOnPlayOption; + + var button = pfMenu.AddButton(f.Name); + button.CloseMenuOnClick = false; + button.Tag = f; + button.TooltipText = f.Tooltip; + button.Icon = f.Active ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + button.Clicked += () => + { + foreach (var child in pfMenu.Items) + { + if (child is ContextMenuButton cmb && cmb.Tag is PlayModeFocusOptions p) + { + if (cmb == button) + { + p.Active = true; + button.Icon = Style.Current.CheckBoxTick; + FocusOnPlayOption = p.FocusOption; + } + else if (p.Active) + { + cmb.Icon = SpriteHandle.Invalid; + p.Active = false; + } + } + } + }; + } + } + private void CreateViewportSizingContextMenu(ContextMenu vsMenu) { // Add default viewport sizing options diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index f22befe9c..6468a2b42 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -926,8 +926,10 @@ namespace FlaxEditor.Windows // Update the output var cachedScrollValue = _vScroll.Value; var cachedSelection = _output.SelectionRange; - var isBottomScroll = _vScroll.Value >= _vScroll.Maximum - 20.0f || wasEmpty; + var cachedOutputTargetViewOffset = _output.TargetViewOffset; + var isBottomScroll = _vScroll.Value >= _vScroll.Maximum - (_scrollSize*2) || wasEmpty; _output.Text = _textBuffer.ToString(); + _output.TargetViewOffset = cachedOutputTargetViewOffset; _textBufferCount = _entries.Count; if (!_vScroll.IsThumbClicked) _vScroll.TargetValue = isBottomScroll ? _vScroll.Maximum : cachedScrollValue; diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index 8d31b2744..2cbe56a27 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -133,7 +133,7 @@ namespace FlaxEditor.Windows contextMenu.AddSeparator(); - b = contextMenu.AddButton("Create parent for selected actors", Editor.SceneEditing.CreateParentForSelectedActors); + b = contextMenu.AddButton("Parent to new Actor", inputOptions.GroupSelectedActors, Editor.SceneEditing.CreateParentForSelectedActors); b.Enabled = canEditScene && hasSthSelected; b = contextMenu.AddButton("Create Prefab", Editor.Prefabs.CreatePrefab); diff --git a/Source/Engine/Content/Assets/Animation.cpp b/Source/Engine/Content/Assets/Animation.cpp index f65a1c3ba..6f8b23ec7 100644 --- a/Source/Engine/Content/Assets/Animation.cpp +++ b/Source/Engine/Content/Assets/Animation.cpp @@ -12,6 +12,8 @@ #include "Engine/Serialization/MemoryReadStream.h" #if USE_EDITOR #include "Engine/Serialization/MemoryWriteStream.h" +#include "Engine/Serialization/JsonWriters.h" +#include "Engine/Content/JsonAsset.h" #include "Engine/Level/Level.h" #endif @@ -486,6 +488,34 @@ bool Animation::Save(const StringView& path) return false; } +void Animation::GetReferences(Array& assets, Array& files) const +{ + BinaryAsset::GetReferences(assets, files); + + for (const auto& e : Events) + { + for (const auto& k : e.Second.GetKeyframes()) + { + if (k.Value.Instance) + { + // Collect refs from Anim Event data (as Json) + rapidjson_flax::StringBuffer buffer; + CompactJsonWriter writer(buffer); + writer.StartObject(); + k.Value.Instance->Serialize(writer, nullptr); + writer.EndObject(); + JsonAssetBase::GetReferences(StringAnsiView((const char*)buffer.GetString(), (int32)buffer.GetSize()), assets); + } + } + } + + // Add nested animations + for (const auto& e : NestedAnims) + { + assets.Add(e.Second.Anim.GetID()); + } +} + #endif uint64 Animation::GetMemoryUsage() const diff --git a/Source/Engine/Content/Assets/Animation.h b/Source/Engine/Content/Assets/Animation.h index b90fbdb73..f287fbd72 100644 --- a/Source/Engine/Content/Assets/Animation.h +++ b/Source/Engine/Content/Assets/Animation.h @@ -153,6 +153,9 @@ public: public: // [BinaryAsset] +#if USE_EDITOR + void GetReferences(Array& assets, Array& files) const override; +#endif uint64 GetMemoryUsage() const override; void OnScriptingDispose() override; diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h index a17d8f686..ec2d28f49 100644 --- a/Source/Engine/Core/Collections/Array.h +++ b/Source/Engine/Core/Collections/Array.h @@ -17,15 +17,15 @@ API_CLASS(InBuild) class Array { friend Array; public: - typedef T ItemType; - typedef typename AllocationType::template Data AllocationData; + using ItemType = T; + using AllocationData = typename AllocationType::template Data; private: int32 _count; int32 _capacity; AllocationData _allocation; - FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromCount, int32 fromCapacity) + FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, const int32 fromCount, const int32 fromCapacity) { if IF_CONSTEXPR (AllocationType::HasSwap) to.Swap(from); @@ -52,7 +52,7 @@ public: /// Initializes a new instance of the class. /// /// The initial capacity. - Array(int32 capacity) + explicit Array(const int32 capacity) : _count(0) , _capacity(capacity) { @@ -66,7 +66,7 @@ public: /// The initial values defined in the array. Array(std::initializer_list initList) { - _count = _capacity = (int32)initList.size(); + _count = _capacity = static_cast(initList.size()); if (_count > 0) { _allocation.Allocate(_count); @@ -79,7 +79,7 @@ public: /// /// The initial data. /// The amount of items. - Array(const T* data, int32 length) + Array(const T* data, const int32 length) { ASSERT(length >= 0); _count = _capacity = length; @@ -160,8 +160,8 @@ public: Clear(); if (initList.size() > 0) { - EnsureCapacity((int32)initList.size()); - _count = (int32)initList.size(); + EnsureCapacity(static_cast(initList.size())); + _count = static_cast(initList.size()); Memory::ConstructItems(_allocation.Get(), initList.begin(), _count); } return *this; @@ -255,7 +255,7 @@ public: /// /// The index. /// true if is valid a index; otherwise, false. - bool IsValidIndex(int32 index) const + bool IsValidIndex(const int32 index) const { return index < _count && index >= 0; } @@ -280,7 +280,7 @@ public: /// Gets item at the given index. /// /// The reference to the item. - FORCE_INLINE T& At(int32 index) + FORCE_INLINE T& At(const int32 index) { ASSERT(index >= 0 && index < _count); return _allocation.Get()[index]; @@ -290,7 +290,7 @@ public: /// Gets item at the given index. /// /// The reference to the item. - FORCE_INLINE const T& At(int32 index) const + FORCE_INLINE const T& At(const int32 index) const { ASSERT(index >= 0 && index < _count); return _allocation.Get()[index]; @@ -300,7 +300,7 @@ public: /// Gets or sets the item at the given index. /// /// The reference to the item. - FORCE_INLINE T& operator[](int32 index) + FORCE_INLINE T& operator[](const int32 index) { ASSERT(index >= 0 && index < _count); return _allocation.Get()[index]; @@ -310,7 +310,7 @@ public: /// Gets the item at the given index. /// /// The reference to the item. - FORCE_INLINE const T& operator[](int32 index) const + FORCE_INLINE const T& operator[](const int32 index) const { ASSERT(index >= 0 && index < _count); return _allocation.Get()[index]; @@ -406,7 +406,7 @@ public: /// /// The new capacity. /// True if preserve collection data when changing its size, otherwise collection after resize will be empty. - void SetCapacity(const int32 capacity, bool preserveContents = true) + void SetCapacity(const int32 capacity, const bool preserveContents = true) { if (capacity == _capacity) return; @@ -422,7 +422,7 @@ public: /// /// The new collection size. /// True if preserve collection data when changing its size, otherwise collection after resize might not contain the previous data. - void Resize(int32 size, bool preserveContents = true) + void Resize(const int32 size, const bool preserveContents = true) { if (_count > size) { @@ -441,7 +441,7 @@ public: /// /// The minimum capacity. /// True if preserve collection data when changing its size, otherwise collection after resize will be empty. - void EnsureCapacity(int32 minCapacity, bool preserveContents = true) + void EnsureCapacity(const int32 minCapacity, const bool preserveContents = true) { if (_capacity < minCapacity) { @@ -466,7 +466,7 @@ public: /// /// The data. /// The amount of items. - void Set(const T* data, int32 count) + void Set(const T* data, const int32 count) { EnsureCapacity(count, false); Memory::DestructItems(_allocation.Get(), _count); @@ -482,7 +482,7 @@ public: { EnsureCapacity(_count + 1); Memory::ConstructItems(_allocation.Get() + _count, &item, 1); - _count++; + ++_count; } /// @@ -493,7 +493,7 @@ public: { EnsureCapacity(_count + 1); Memory::MoveItems(_allocation.Get() + _count, &item, 1); - _count++; + ++_count; } /// @@ -501,7 +501,7 @@ public: /// /// The items to add. /// The items count. - void Add(const T* items, int32 count) + void Add(const T* items, const int32 count) { EnsureCapacity(_count + count); Memory::ConstructItems(_allocation.Get() + _count, items, count); @@ -532,7 +532,7 @@ public: /// Adds the given amount of items to the collection. /// /// The items count. - FORCE_INLINE void AddDefault(int32 count = 1) + FORCE_INLINE void AddDefault(const int32 count = 1) { EnsureCapacity(_count + count); Memory::ConstructItems(_allocation.Get() + _count, count); @@ -543,7 +543,7 @@ public: /// Adds the given amount of uninitialized items to the collection without calling the constructor. /// /// The items count. - FORCE_INLINE void AddUninitialized(int32 count = 1) + FORCE_INLINE void AddUninitialized(const int32 count = 1) { EnsureCapacity(_count + count); _count += count; @@ -557,7 +557,7 @@ public: { EnsureCapacity(_count + 1); Memory::ConstructItems(_allocation.Get() + _count, 1); - _count++; + ++_count; return _allocation.Get()[_count - 1]; } @@ -568,7 +568,7 @@ public: /// Warning! AddZeroed() will create items without calling the constructor and this is not appropriate for item types that require a constructor to function properly. /// /// The number of new items to add. - void AddZeroed(int32 count = 1) + void AddZeroed(const int32 count = 1) { EnsureCapacity(_count + count); Platform::MemoryClear(_allocation.Get() + _count, count * sizeof(T)); @@ -580,7 +580,7 @@ public: /// /// The zero-based index at which item should be inserted. /// The item to be inserted by copying. - void Insert(int32 index, const T& item) + void Insert(const int32 index, const T& item) { ASSERT(index >= 0 && index <= _count); EnsureCapacity(_count + 1); @@ -597,7 +597,7 @@ public: /// /// The zero-based index at which item should be inserted. /// The item to inserted by moving. - void Insert(int32 index, T&& item) + void Insert(const int32 index, T&& item) { ASSERT(index >= 0 && index <= _count); EnsureCapacity(_count + 1); @@ -605,7 +605,7 @@ public: Memory::ConstructItems(data + _count, 1); for (int32 i = _count - 1; i >= index; i--) data[i + 1] = MoveTemp(data[i]); - _count++; + ++_count; data[index] = MoveTemp(item); } @@ -613,7 +613,7 @@ public: /// Insert the given item at specified index with keeping items order. /// /// The zero-based index at which item should be inserted. - void Insert(int32 index) + void Insert(const int32 index) { ASSERT(index >= 0 && index <= _count); EnsureCapacity(_count + 1); @@ -621,7 +621,7 @@ public: Memory::ConstructItems(data + _count, 1); for (int32 i = _count - 1; i >= index; i--) data[i + 1] = data[i]; - _count++; + ++_count; } /// @@ -661,7 +661,7 @@ public: /// The item to remove. void RemoveAllKeepOrder(const T& item) { - for (int32 i = Count() - 1; i >= 0; i--) + for (int32 i = Count() - 1; i >= 0; --i) { if (_allocation.Get()[i] == item) { @@ -679,14 +679,14 @@ public: void RemoveAtKeepOrder(const int32 index) { ASSERT(index < _count && index >= 0); - _count--; + --_count; T* data = _allocation.Get(); if (index < _count) { T* dst = data + index; T* src = data + (index + 1); const int32 count = _count - index; - for (int32 i = 0; i < count; i++) + for (int32 i = 0; i < count; ++i) dst[i] = MoveTemp(src[i]); } Memory::DestructItems(data + _count, 1); @@ -712,7 +712,7 @@ public: /// The item to remove. void RemoveAll(const T& item) { - for (int32 i = Count() - 1; i >= 0; i--) + for (int32 i = Count() - 1; i >= 0; --i) { if (_allocation.Get()[i] == item) { @@ -730,7 +730,7 @@ public: void RemoveAt(const int32 index) { ASSERT(index < _count && index >= 0); - _count--; + --_count; T* data = _allocation.Get(); if (_count) data[index] = data[_count]; @@ -743,7 +743,7 @@ public: void RemoveLast() { ASSERT(_count > 0); - _count--; + --_count; Memory::DestructItems(_allocation.Get() + _count, 1); } @@ -772,7 +772,7 @@ public: { T* data = _allocation.Get(); const int32 count = _count / 2; - for (int32 i = 0; i < count; i++) + for (int32 i = 0; i < count; ++i) ::Swap(data[i], data[_count - i - 1]); } @@ -954,7 +954,7 @@ public: { } - Iterator(Array const* array, const int32 index) + Iterator(const Array* array, const int32 index) : _array(const_cast(array)) , _index(index) { @@ -1052,7 +1052,7 @@ public: Iterator& operator--() { if (_index > 0) - _index--; + --_index; return *this; } @@ -1060,7 +1060,7 @@ public: { Iterator temp = *this; if (_index > 0) - _index--; + --_index; return temp; } }; @@ -1084,7 +1084,7 @@ public: }; template -void* operator new(size_t size, Array& array) +void* operator new(const size_t size, Array& array) { ASSERT(size == sizeof(T)); const int32 index = array.Count(); @@ -1093,7 +1093,7 @@ void* operator new(size_t size, Array& array) } template -void* operator new(size_t size, Array& array, int32 index) +void* operator new(const size_t size, Array& array, const int32 index) { ASSERT(size == sizeof(T)); array.Insert(index); diff --git a/Source/Engine/Core/Collections/BitArray.h b/Source/Engine/Core/Collections/BitArray.h index c7853b989..1b601c72f 100644 --- a/Source/Engine/Core/Collections/BitArray.h +++ b/Source/Engine/Core/Collections/BitArray.h @@ -14,20 +14,20 @@ API_CLASS(InBuild) class BitArray { friend BitArray; public: - typedef uint64 ItemType; - typedef typename AllocationType::template Data AllocationData; + using ItemType = uint64; + using AllocationData = typename AllocationType::template Data; private: int32 _count; int32 _capacity; AllocationData _allocation; - FORCE_INLINE static int32 ToItemCount(int32 size) + FORCE_INLINE static int32 ToItemCount(const int32 size) { return Math::DivideAndRoundUp(size, sizeof(ItemType)); } - FORCE_INLINE static int32 ToItemCapacity(int32 size) + FORCE_INLINE static int32 ToItemCapacity(const int32 size) { return Math::Max(Math::DivideAndRoundUp(size, sizeof(ItemType)), 1); } @@ -46,7 +46,7 @@ public: /// Initializes a new instance of the class. /// /// The initial capacity. - BitArray(int32 capacity) + explicit BitArray(const int32 capacity) : _count(0) , _capacity(capacity) { @@ -200,7 +200,7 @@ public: /// /// The index of the item. /// The value of the item. - FORCE_INLINE bool operator[](int32 index) const + FORCE_INLINE bool operator[](const int32 index) const { return Get(index); } @@ -210,7 +210,7 @@ public: /// /// The index of the item. /// The value of the item. - bool Get(int32 index) const + bool Get(const int32 index) const { ASSERT(index >= 0 && index < _count); const ItemType offset = index / sizeof(ItemType); @@ -224,7 +224,7 @@ public: /// /// The index of the item. /// The value to set. - void Set(int32 index, bool value) + void Set(const int32 index, const bool value) { ASSERT(index >= 0 && index < _count); const ItemType offset = index / sizeof(ItemType); @@ -233,7 +233,7 @@ public: if (value) item |= bitMask; else - item &= ~bitMask; + item &= ~bitMask; // Clear the bit } public: @@ -250,7 +250,7 @@ public: /// /// The new capacity. /// True if preserve collection data when changing its size, otherwise collection after resize will be empty. - void SetCapacity(const int32 capacity, bool preserveContents = true) + void SetCapacity(const int32 capacity, const bool preserveContents = true) { if (capacity == _capacity) return; @@ -266,7 +266,7 @@ public: /// /// The new collection size. /// True if preserve collection data when changing its size, otherwise collection after resize might not contain the previous data. - void Resize(int32 size, bool preserveContents = true) + void Resize(const int32 size, const bool preserveContents = true) { if (_count <= size) EnsureCapacity(size, preserveContents); @@ -278,7 +278,7 @@ public: /// /// The minimum capacity. /// True if preserve collection data when changing its size, otherwise collection after resize will be empty. - void EnsureCapacity(int32 minCapacity, bool preserveContents = true) + void EnsureCapacity(const int32 minCapacity, const bool preserveContents = true) { if (_capacity < minCapacity) { @@ -304,7 +304,7 @@ public: void Add(const bool item) { EnsureCapacity(_count + 1); - _count++; + ++_count; Set(_count - 1, item); } @@ -313,10 +313,10 @@ public: /// /// The items to add. /// The items count. - void Add(const bool* items, int32 count) + void Add(const bool* items, const int32 count) { EnsureCapacity(_count + count); - for (int32 i = 0; i < count; i++) + for (int32 i = 0; i < count; ++i) Add(items[i]); } @@ -327,7 +327,7 @@ public: void Add(const BitArray& other) { EnsureCapacity(_count, other.Count()); - for (int32 i = 0; i < other.Count(); i++) + for (int32 i = 0; i < other.Count(); ++i) Add(other[i]); } diff --git a/Source/Engine/Core/Collections/BucketState.h b/Source/Engine/Core/Collections/BucketState.h new file mode 100644 index 000000000..16cbdb845 --- /dev/null +++ b/Source/Engine/Core/Collections/BucketState.h @@ -0,0 +1,15 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Types/BaseTypes.h" + +/// +/// Tells if the object is occupied, and if not, if the bucket is a subject of compaction. +/// +enum class BucketState : byte +{ + Empty = 0, + Deleted = 1, + Occupied = 2, +}; diff --git a/Source/Engine/Core/Collections/Dictionary.h b/Source/Engine/Core/Collections/Dictionary.h index 9e516dae7..efb7bafcf 100644 --- a/Source/Engine/Core/Collections/Dictionary.h +++ b/Source/Engine/Core/Collections/Dictionary.h @@ -4,6 +4,7 @@ #include "Engine/Core/Memory/Memory.h" #include "Engine/Core/Memory/Allocation.h" +#include "Engine/Core/Collections/BucketState.h" #include "Engine/Core/Collections/HashFunctions.h" #include "Engine/Core/Collections/Config.h" @@ -25,34 +26,27 @@ public: { friend Dictionary; - enum State : byte - { - Empty = 0, - Deleted = 1, - Occupied = 2, - }; - /// The key. KeyType Key; /// The value. ValueType Value; private: - State _state; + BucketState _state; FORCE_INLINE void Free() { - if (_state == Occupied) + if (_state == BucketState::Occupied) { Memory::DestructItem(&Key); Memory::DestructItem(&Value); } - _state = Empty; + _state = BucketState::Empty; } FORCE_INLINE void Delete() { - _state = Deleted; + _state = BucketState::Deleted; Memory::DestructItem(&Key); Memory::DestructItem(&Value); } @@ -62,7 +56,7 @@ public: { Memory::ConstructItems(&Key, &key, 1); Memory::ConstructItem(&Value); - _state = Occupied; + _state = BucketState::Occupied; } template @@ -70,7 +64,7 @@ public: { Memory::ConstructItems(&Key, &key, 1); Memory::ConstructItems(&Value, &value, 1); - _state = Occupied; + _state = BucketState::Occupied; } template @@ -78,31 +72,31 @@ public: { Memory::ConstructItems(&Key, &key, 1); Memory::MoveItems(&Value, &value, 1); - _state = Occupied; + _state = BucketState::Occupied; } FORCE_INLINE bool IsEmpty() const { - return _state == Empty; + return _state == BucketState::Empty; } FORCE_INLINE bool IsDeleted() const { - return _state == Deleted; + return _state == BucketState::Deleted; } FORCE_INLINE bool IsOccupied() const { - return _state == Occupied; + return _state == BucketState::Occupied; } FORCE_INLINE bool IsNotOccupied() const { - return _state != Occupied; + return _state != BucketState::Occupied; } }; - typedef typename AllocationType::template Data AllocationData; + using AllocationData = typename AllocationType::template Data; private: int32 _elementsCount = 0; @@ -110,7 +104,7 @@ private: int32 _size = 0; AllocationData _allocation; - FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromSize) + FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, const int32 fromSize) { if IF_CONSTEXPR (AllocationType::HasSwap) to.Swap(from); @@ -127,10 +121,10 @@ private: Bucket& toBucket = toData[i]; Memory::MoveItems(&toBucket.Key, &fromBucket.Key, 1); Memory::MoveItems(&toBucket.Value, &fromBucket.Value, 1); - toBucket._state = Bucket::Occupied; + toBucket._state = BucketState::Occupied; Memory::DestructItem(&fromBucket.Key); Memory::DestructItem(&fromBucket.Value); - fromBucket._state = Bucket::Empty; + fromBucket._state = BucketState::Empty; } } from.Free(); @@ -149,7 +143,7 @@ public: /// Initializes a new instance of the class. /// /// The initial capacity. - Dictionary(int32 capacity) + explicit Dictionary(const int32 capacity) { SetCapacity(capacity); } @@ -289,7 +283,7 @@ public: { } - Iterator(Iterator&& i) + Iterator(Iterator&& i) noexcept : _collection(i._collection) , _index(i._index) { @@ -348,7 +342,7 @@ public: return *this; } - Iterator& operator=(Iterator&& v) + Iterator& operator=(Iterator&& v) noexcept { _collection = v._collection; _index = v._index; @@ -363,8 +357,9 @@ public: const Bucket* data = _collection->_allocation.Get(); do { - _index++; - } while (_index != capacity && data[_index].IsNotOccupied()); + ++_index; + } + while (_index != capacity && data[_index].IsNotOccupied()); } return *this; } @@ -383,8 +378,9 @@ public: const Bucket* data = _collection->_allocation.Get(); do { - _index--; - } while (_index > 0 && data[_index].IsNotOccupied()); + --_index; + } + while (_index > 0 && data[_index].IsNotOccupied()); } return *this; } @@ -423,7 +419,7 @@ public: // Insert ASSERT(pos.FreeSlotIndex != -1); - _elementsCount++; + ++_elementsCount; Bucket& bucket = _allocation.Get()[pos.FreeSlotIndex]; bucket.Occupy(key); return bucket.Value; @@ -498,7 +494,7 @@ public: FindPosition(key, pos); if (pos.ObjectIndex == -1) return nullptr; - return (ValueType*)&_allocation.Get()[pos.ObjectIndex].Value; + return const_cast(&_allocation.Get()[pos.ObjectIndex].Value); //TODO This one is problematic. I think this entire method should be removed. } public: @@ -538,7 +534,7 @@ public: /// /// The new capacity. /// Enables preserving collection contents during resizing. - void SetCapacity(int32 capacity, bool preserveContents = true) + void SetCapacity(int32 capacity, const bool preserveContents = true) { if (capacity == Capacity()) return; @@ -564,7 +560,7 @@ public: _allocation.Allocate(capacity); Bucket* data = _allocation.Get(); for (int32 i = 0; i < capacity; i++) - data[i]._state = Bucket::Empty; + data[i]._state = BucketState::Empty; } _size = capacity; Bucket* oldData = oldAllocation.Get(); @@ -581,8 +577,8 @@ public: Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex]; Memory::MoveItems(&bucket->Key, &oldBucket.Key, 1); Memory::MoveItems(&bucket->Value, &oldBucket.Value, 1); - bucket->_state = Bucket::Occupied; - _elementsCount++; + bucket->_state = BucketState::Occupied; + ++_elementsCount; } } } @@ -598,7 +594,7 @@ public: /// /// The minimum required capacity. /// True if preserve collection data when changing its size, otherwise collection after resize will be empty. - void EnsureCapacity(int32 minCapacity, bool preserveContents = true) + void EnsureCapacity(int32 minCapacity, const bool preserveContents = true) { if (_size >= minCapacity) return; @@ -682,8 +678,8 @@ public: if (pos.ObjectIndex != -1) { _allocation.Get()[pos.ObjectIndex].Delete(); - _elementsCount--; - _deletedCount++; + --_elementsCount; + ++_deletedCount; return true; } return false; @@ -701,8 +697,8 @@ public: { ASSERT(_allocation.Get()[i._index].IsOccupied()); _allocation.Get()[i._index].Delete(); - _elementsCount--; - _deletedCount++; + --_elementsCount; + ++_deletedCount; return true; } return false; @@ -721,7 +717,7 @@ public: if (i->Value == value) { Remove(i); - result++; + ++result; } } return result; @@ -768,7 +764,7 @@ public: if (HasItems()) { const Bucket* data = _allocation.Get(); - for (int32 i = 0; i < _size; i++) + for (int32 i = 0; i < _size; ++i) { if (data[i].IsOccupied() && data[i].Value == value) return true; @@ -788,7 +784,7 @@ public: if (HasItems()) { const Bucket* data = _allocation.Get(); - for (int32 i = 0; i < _size; i++) + for (int32 i = 0; i < _size; ++i) { if (data[i].IsOccupied() && data[i].Value == value) { @@ -862,14 +858,14 @@ public: return Iterator(this, _size); } - const Iterator begin() const + Iterator begin() const { Iterator i(this, -1); ++i; return i; } - FORCE_INLINE const Iterator end() const + FORCE_INLINE Iterator end() const { return Iterator(this, _size); } @@ -928,7 +924,7 @@ private: result.ObjectIndex = bucketIndex; return; } - checksCount++; + ++checksCount; bucketIndex = (bucketIndex + DICTIONARY_PROB_FUNC(_size, checksCount)) & tableSizeMinusOne; } result.ObjectIndex = -1; @@ -965,7 +961,7 @@ private: // Fast path if it's empty Bucket* data = _allocation.Get(); for (int32 i = 0; i < _size; i++) - data[i]._state = Bucket::Empty; + data[i]._state = BucketState::Empty; } else { @@ -975,7 +971,7 @@ private: _allocation.Allocate(_size); Bucket* data = _allocation.Get(); for (int32 i = 0; i < _size; i++) - data[i]._state = Bucket::Empty; + data[i]._state = BucketState::Empty; Bucket* oldData = oldAllocation.Get(); FindPositionResult pos; for (int32 i = 0; i < _size; i++) @@ -988,7 +984,7 @@ private: Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex]; Memory::MoveItems(&bucket->Key, &oldBucket.Key, 1); Memory::MoveItems(&bucket->Value, &oldBucket.Value, 1); - bucket->_state = Bucket::Occupied; + bucket->_state = BucketState::Occupied; } } for (int32 i = 0; i < _size; i++) diff --git a/Source/Engine/Core/Collections/HashSet.h b/Source/Engine/Core/Collections/HashSet.h index b53225044..aa61354fd 100644 --- a/Source/Engine/Core/Collections/HashSet.h +++ b/Source/Engine/Core/Collections/HashSet.h @@ -4,6 +4,7 @@ #include "Engine/Core/Memory/Memory.h" #include "Engine/Core/Memory/Allocation.h" +#include "Engine/Core/Collections/BucketState.h" #include "Engine/Core/Collections/HashFunctions.h" #include "Engine/Core/Collections/Config.h" @@ -24,29 +25,22 @@ public: { friend HashSet; - enum State : byte - { - Empty, - Deleted, - Occupied, - }; - /// The item. T Item; private: - State _state; + BucketState _state; FORCE_INLINE void Free() { - if (_state == Occupied) + if (_state == BucketState::Occupied) Memory::DestructItem(&Item); - _state = Empty; + _state = BucketState::Empty; } FORCE_INLINE void Delete() { - _state = Deleted; + _state = BucketState::Deleted; Memory::DestructItem(&Item); } @@ -54,38 +48,38 @@ public: FORCE_INLINE void Occupy(const ItemType& item) { Memory::ConstructItems(&Item, &item, 1); - _state = Occupied; + _state = BucketState::Occupied; } template FORCE_INLINE void Occupy(ItemType&& item) { Memory::MoveItems(&Item, &item, 1); - _state = Occupied; + _state = BucketState::Occupied; } FORCE_INLINE bool IsEmpty() const { - return _state == Empty; + return _state == BucketState::Empty; } FORCE_INLINE bool IsDeleted() const { - return _state == Deleted; + return _state == BucketState::Deleted; } FORCE_INLINE bool IsOccupied() const { - return _state == Occupied; + return _state == BucketState::Occupied; } FORCE_INLINE bool IsNotOccupied() const { - return _state != Occupied; + return _state != BucketState::Occupied; } }; - typedef typename AllocationType::template Data AllocationData; + using AllocationData = typename AllocationType::template Data; private: int32 _elementsCount = 0; @@ -93,7 +87,7 @@ private: int32 _size = 0; AllocationData _allocation; - FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromSize) + FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, const int32 fromSize) { if IF_CONSTEXPR (AllocationType::HasSwap) to.Swap(from); @@ -102,16 +96,16 @@ private: to.Allocate(fromSize); Bucket* toData = to.Get(); Bucket* fromData = from.Get(); - for (int32 i = 0; i < fromSize; i++) + for (int32 i = 0; i < fromSize; ++i) { Bucket& fromBucket = fromData[i]; if (fromBucket.IsOccupied()) { Bucket& toBucket = toData[i]; Memory::MoveItems(&toBucket.Item, &fromBucket.Item, 1); - toBucket._state = Bucket::Occupied; + toBucket._state = BucketState::Occupied; Memory::DestructItem(&fromBucket.Item); - fromBucket._state = Bucket::Empty; + fromBucket._state = BucketState::Empty; } } from.Free(); @@ -130,7 +124,7 @@ public: /// Initializes a new instance of the class. /// /// The initial capacity. - HashSet(int32 capacity) + explicit HashSet(const int32 capacity) { SetCapacity(capacity); } @@ -252,7 +246,7 @@ public: { } - Iterator(HashSet const* collection, const int32 index) + Iterator(const HashSet* collection, const int32 index) : _collection(const_cast(collection)) , _index(index) { @@ -270,7 +264,7 @@ public: { } - Iterator(Iterator&& i) + Iterator(Iterator&& i) noexcept : _collection(i._collection) , _index(i._index) { @@ -307,7 +301,7 @@ public: return _index >= 0 && _index < _collection->_size; } - FORCE_INLINE bool operator !() const + FORCE_INLINE bool operator!() const { return !(bool)*this; } @@ -329,7 +323,7 @@ public: return *this; } - Iterator& operator=(Iterator&& v) + Iterator& operator=(Iterator&& v) noexcept { _collection = v._collection; _index = v._index; @@ -344,8 +338,9 @@ public: const Bucket* data = _collection->_allocation.Get(); do { - _index++; - } while (_index != capacity && data[_index].IsNotOccupied()); + ++_index; + } + while (_index != capacity && data[_index].IsNotOccupied()); } return *this; } @@ -364,8 +359,9 @@ public: const Bucket* data = _collection->_allocation.Get(); do { - _index--; - } while (_index > 0 && data[_index].IsNotOccupied()); + --_index; + } + while (_index > 0 && data[_index].IsNotOccupied()); } return *this; } @@ -415,7 +411,7 @@ public: /// /// New capacity /// Enable/disable preserving collection contents during resizing - void SetCapacity(int32 capacity, bool preserveContents = true) + void SetCapacity(int32 capacity, const bool preserveContents = true) { if (capacity == Capacity()) return; @@ -441,7 +437,7 @@ public: _allocation.Allocate(capacity); Bucket* data = _allocation.Get(); for (int32 i = 0; i < capacity; i++) - data[i]._state = Bucket::Empty; + data[i]._state = BucketState::Empty; } _size = capacity; Bucket* oldData = oldAllocation.Get(); @@ -457,7 +453,7 @@ public: ASSERT(pos.FreeSlotIndex != -1); Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex]; Memory::MoveItems(&bucket->Item, &oldBucket.Item, 1); - bucket->_state = Bucket::Occupied; + bucket->_state = BucketState::Occupied; _elementsCount++; } } @@ -474,7 +470,7 @@ public: /// /// The minimum required capacity. /// True if preserve collection data when changing its size, otherwise collection after resize will be empty. - void EnsureCapacity(int32 minCapacity, bool preserveContents = true) + void EnsureCapacity(const int32 minCapacity, const bool preserveContents = true) { if (_size >= minCapacity) return; @@ -559,8 +555,8 @@ public: if (pos.ObjectIndex != -1) { _allocation.Get()[pos.ObjectIndex].Delete(); - _elementsCount--; - _deletedCount++; + --_elementsCount; + ++_deletedCount; return true; } return false; @@ -578,8 +574,8 @@ public: { ASSERT(_allocation.Get()[i._index].IsOccupied()); _allocation.Get()[i._index].Delete(); - _elementsCount--; - _deletedCount++; + --_elementsCount; + ++_deletedCount; return true; } return false; @@ -656,14 +652,14 @@ public: return Iterator(this, _size); } - const Iterator begin() const + Iterator begin() const { Iterator i(this, -1); ++i; return i; } - FORCE_INLINE const Iterator end() const + FORCE_INLINE Iterator end() const { return Iterator(this, _size); } @@ -750,7 +746,7 @@ private: // Insert ASSERT(pos.FreeSlotIndex != -1); - _elementsCount++; + ++_elementsCount; return &_allocation.Get()[pos.FreeSlotIndex]; } @@ -760,8 +756,8 @@ private: { // Fast path if it's empty Bucket* data = _allocation.Get(); - for (int32 i = 0; i < _size; i++) - data[i]._state = Bucket::Empty; + for (int32 i = 0; i < _size; ++i) + data[i]._state = BucketState::Empty; } else { @@ -770,11 +766,11 @@ private: MoveToEmpty(oldAllocation, _allocation, _size); _allocation.Allocate(_size); Bucket* data = _allocation.Get(); - for (int32 i = 0; i < _size; i++) - data[i]._state = Bucket::Empty; + for (int32 i = 0; i < _size; ++i) + data[i]._state = BucketState::Empty; Bucket* oldData = oldAllocation.Get(); FindPositionResult pos; - for (int32 i = 0; i < _size; i++) + for (int32 i = 0; i < _size; ++i) { Bucket& oldBucket = oldData[i]; if (oldBucket.IsOccupied()) @@ -783,10 +779,10 @@ private: ASSERT(pos.FreeSlotIndex != -1); Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex]; Memory::MoveItems(&bucket->Item, &oldBucket.Item, 1); - bucket->_state = Bucket::Occupied; + bucket->_state = BucketState::Occupied; } } - for (int32 i = 0; i < _size; i++) + for (int32 i = 0; i < _size; ++i) oldData[i].Free(); } _deletedCount = 0; diff --git a/Source/Engine/Core/Collections/RingBuffer.h b/Source/Engine/Core/Collections/RingBuffer.h index 4dcc75e33..c46f13031 100644 --- a/Source/Engine/Core/Collections/RingBuffer.h +++ b/Source/Engine/Core/Collections/RingBuffer.h @@ -14,8 +14,8 @@ template class RingBuffer { public: - typedef T ItemType; - typedef typename AllocationType::template Data AllocationData; + using ItemType = T; + using AllocationData = typename AllocationType::template Data; private: int32 _front = 0, _back = 0, _count = 0, _capacity = 0; @@ -62,7 +62,7 @@ public: } Memory::ConstructItems(_allocation.Get() + _back, &data, 1); _back = (_back + 1) % _capacity; - _count++; + ++_count; } FORCE_INLINE T& PeekFront() @@ -75,13 +75,13 @@ public: return _allocation.Get()[_front]; } - FORCE_INLINE T& operator[](int32 index) + FORCE_INLINE T& operator[](const int32 index) { ASSERT(index >= 0 && index < _count); return _allocation.Get()[(_front + index) % _capacity]; } - FORCE_INLINE const T& operator[](int32 index) const + FORCE_INLINE const T& operator[](const int32 index) const { ASSERT(index >= 0 && index < _count); return _allocation.Get()[(_front + index) % _capacity]; @@ -91,7 +91,7 @@ public: { Memory::DestructItems(_allocation.Get() + _front, 1); _front = (_front + 1) % _capacity; - _count--; + --_count; } void Clear() diff --git a/Source/Engine/Core/LogContext.cpp b/Source/Engine/Core/LogContext.cpp index 9df4bce99..c7ed7ea2e 100644 --- a/Source/Engine/Core/LogContext.cpp +++ b/Source/Engine/Core/LogContext.cpp @@ -54,12 +54,17 @@ void LogContext::Print(LogType verbosity) StringBuilder msg; for (int32 index = (int32)stack.Count - 1; index >= 0; index--) { + LogContextData& context = stack.Ptr[index]; + + // Skip duplicates + if (index < (int32)stack.Count - 1 && stack.Ptr[stack.Count - 1] == context) + continue; + // Build call hierarchy via indentation msg.Clear(); for (uint32 i = index; i < stack.Count; i++) msg.Append(indentation); - LogContextData& context = stack.Ptr[index]; if (context.ObjectID != Guid::Empty) { // Object reference context diff --git a/Source/Engine/Core/LogContext.h b/Source/Engine/Core/LogContext.h index 7a851a6c9..59a0c537c 100644 --- a/Source/Engine/Core/LogContext.h +++ b/Source/Engine/Core/LogContext.h @@ -20,6 +20,11 @@ API_STRUCT(NoDefault) struct FLAXENGINE_API LogContextData /// A GUID for an object which this context applies to. /// API_FIELD() Guid ObjectID; + + friend bool operator==(const LogContextData& lhs, const LogContextData& rhs) + { + return lhs.ObjectID == rhs.ObjectID; + } }; template<> diff --git a/Source/Engine/Core/Memory/Allocation.h b/Source/Engine/Core/Memory/Allocation.h index 7d28c70cb..5fce2ad73 100644 --- a/Source/Engine/Core/Memory/Allocation.h +++ b/Source/Engine/Core/Memory/Allocation.h @@ -31,28 +31,28 @@ public: FORCE_INLINE T* Get() { - return (T*)_data; + return reinterpret_cast(_data); } FORCE_INLINE const T* Get() const { - return (T*)_data; + return reinterpret_cast(_data); } - FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, int32 minCapacity) const + FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, const int32 minCapacity) const { ASSERT(minCapacity <= Capacity); return Capacity; } - FORCE_INLINE void Allocate(int32 capacity) + FORCE_INLINE void Allocate(const int32 capacity) { #if ENABLE_ASSERTION_LOW_LAYERS ASSERT(capacity <= Capacity); #endif } - FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount) + FORCE_INLINE void Relocate(const int32 capacity, int32 oldCount, int32 newCount) { #if ENABLE_ASSERTION_LOW_LAYERS ASSERT(capacity <= Capacity); @@ -104,7 +104,7 @@ public: return _data; } - FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, int32 minCapacity) const + FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, const int32 minCapacity) const { if (capacity < minCapacity) capacity = minCapacity; @@ -129,21 +129,21 @@ public: return capacity; } - FORCE_INLINE void Allocate(int32 capacity) + FORCE_INLINE void Allocate(const int32 capacity) { #if ENABLE_ASSERTION_LOW_LAYERS ASSERT(!_data); #endif - _data = (T*)Allocator::Allocate(capacity * sizeof(T)); + _data = static_cast(Allocator::Allocate(capacity * sizeof(T))); #if !BUILD_RELEASE if (!_data) OUT_OF_MEMORY; #endif } - FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount) + FORCE_INLINE void Relocate(const int32 capacity, int32 oldCount, int32 newCount) { - T* newData = capacity != 0 ? (T*)Allocator::Allocate(capacity * sizeof(T)) : nullptr; + T* newData = capacity != 0 ? static_cast(Allocator::Allocate(capacity * sizeof(T))) : nullptr; #if !BUILD_RELEASE if (!newData && capacity != 0) OUT_OF_MEMORY; @@ -203,12 +203,12 @@ public: FORCE_INLINE T* Get() { - return _useOther ? _other.Get() : (T*)_data; + return _useOther ? _other.Get() : reinterpret_cast(_data); } FORCE_INLINE const T* Get() const { - return _useOther ? _other.Get() : (T*)_data; + return _useOther ? _other.Get() : reinterpret_cast(_data); } FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, int32 minCapacity) const @@ -227,13 +227,15 @@ public: FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount) { + T* data = reinterpret_cast(_data); + // Check if the new allocation will fit into inlined storage if (capacity <= Capacity) { if (_useOther) { // Move the items from other allocation to the inlined storage - Memory::MoveItems((T*)_data, _other.Get(), newCount); + Memory::MoveItems(data, _other.Get(), newCount); // Free the other allocation Memory::DestructItems(_other.Get(), oldCount); @@ -255,8 +257,8 @@ public: _useOther = true; // Move the items from the inlined storage to the other allocation - Memory::MoveItems(_other.Get(), (T*)_data, newCount); - Memory::DestructItems((T*)_data, oldCount); + Memory::MoveItems(_other.Get(), data, newCount); + Memory::DestructItems(data, oldCount); } } } @@ -277,4 +279,4 @@ public: }; }; -typedef HeapAllocation DefaultAllocation; +using DefaultAllocation = HeapAllocation; diff --git a/Source/Engine/Networking/NetworkManager.cpp b/Source/Engine/Networking/NetworkManager.cpp index 4d3212e14..c3a49da5a 100644 --- a/Source/Engine/Networking/NetworkManager.cpp +++ b/Source/Engine/Networking/NetworkManager.cpp @@ -624,6 +624,8 @@ void NetworkManager::Stop() State = NetworkConnectionState::Disconnected; Mode = NetworkManagerMode::Offline; + LastUpdateTime = 0; + StateChanged(); } diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 19c72f760..18518a16f 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -1322,6 +1322,14 @@ void NetworkReplicator::MapObjectId(Guid& objectId) } } +void NetworkReplicator::AddObjectIdMapping(const ScriptingObject* obj, const Guid& objectId) +{ + CHECK(obj); + const Guid id = obj->GetID(); + NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Remap object ID={} into object {}:{}", objectId, id.ToString(), obj->GetType().ToString()); + IdsRemappingTable[objectId] = id; +} + ScriptingObject* NetworkReplicator::ResolveForeignObject(Guid objectId) { if (const auto& object = ResolveObject(objectId)) diff --git a/Source/Engine/Networking/NetworkReplicator.h b/Source/Engine/Networking/NetworkReplicator.h index a67e1dd29..babdb1dce 100644 --- a/Source/Engine/Networking/NetworkReplicator.h +++ b/Source/Engine/Networking/NetworkReplicator.h @@ -122,6 +122,13 @@ public: /// /// The network object identifier to map. Contains result ID once the method completes. API_FUNCTION() static void MapObjectId(API_PARAM(Ref) Guid& objectId); + + /// + /// Adds a new mapping for object identifier. Can be used to link locally-spawned object with across different clients. + /// + /// The network object. + /// The network object identifier to use (eg. defined by server or global/static). + API_FUNCTION() static void AddObjectIdMapping(const ScriptingObject* obj, API_PARAM(Ref) const Guid& objectId); /// /// Resolves foreign Guid into a local ScriptingObject diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index d88031a01..5c5d107e8 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -2801,7 +2801,14 @@ float PhysicsBackend::ComputeShapeSqrDistanceToPoint(void* shape, const Vector3& { auto shapePhysX = (PxShape*)shape; const PxTransform trans(C2P(position), C2P(orientation)); +#if USE_LARGE_WORLDS + PxVec3 closestPointPx; + float result = PxGeometryQuery::pointDistance(C2P(point), shapePhysX->getGeometry(), trans, &closestPointPx); + *closestPoint = P2C(closestPointPx); + return result; +#else return PxGeometryQuery::pointDistance(C2P(point), shapePhysX->getGeometry(), trans, (PxVec3*)closestPoint); +#endif } bool PhysicsBackend::RayCastShape(void* shape, const Vector3& position, const Quaternion& orientation, const Vector3& origin, const Vector3& direction, float& resultHitDistance, float maxDistance) @@ -3440,39 +3447,13 @@ PxVehicleAntiRollBarData CreatePxPxVehicleAntiRollBarData(const WheeledVehicle:: return antiRollBar; } -bool SortWheelsFrontToBack(WheeledVehicle::Wheel const& a, WheeledVehicle::Wheel const& b) +bool SortWheelsFrontToBack(WheeledVehicle::Wheel* const& a, WheeledVehicle::Wheel* const& b) { - return a.Collider && b.Collider && a.Collider->GetLocalPosition().Z > b.Collider->GetLocalPosition().Z; + return a->Collider && b->Collider && a->Collider->GetLocalPosition().Z > b->Collider->GetLocalPosition().Z; } void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor) { - // Physx vehicles needs to have all wheels sorted to apply controls correctly. - // Its needs to know what wheels are on front to turn wheel to correctly side - // and needs to know wheel side to apply throttle to correctly direction for each track when using tanks. - // Anti roll bars needs to have all wheels sorted to get correctly wheel index too. - if (actor->_driveType != WheeledVehicle::DriveTypes::NoDrive) - { - Sorting::QuickSort(actor->_wheels.Get(), actor->_wheels.Count(), SortWheelsFrontToBack); - - // Sort wheels by side - if (actor->_driveType == WheeledVehicle::DriveTypes::Tank) - { - for (int32 i = 0; i < actor->_wheels.Count() - 1; i += 2) - { - auto a = actor->_wheels[i]; - auto b = actor->_wheels[i + 1]; - if (!a.Collider || !b.Collider) - continue; - if (a.Collider->GetLocalPosition().X > b.Collider->GetLocalPosition().X) - { - actor->_wheels[i] = b; - actor->_wheels[i + 1] = a; - } - } - } - } - // Get wheels Array> wheels; for (auto& wheel : actor->_wheels) @@ -3503,6 +3484,33 @@ void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor) // No woman, no cry return nullptr; } + + // Physx vehicles needs to have all wheels sorted to apply controls correctly. + // Its needs to know what wheels are on front to turn wheel to correctly side + // and needs to know wheel side to apply throttle to correctly direction for each track when using tanks. + // Anti roll bars needs to have all wheels sorted to get correctly wheel index too. + if (actor->_driveType != WheeledVehicle::DriveTypes::NoDrive) + { + Sorting::QuickSort(wheels.Get(), wheels.Count(), SortWheelsFrontToBack); + + // Sort wheels by side + if (actor->_driveType == WheeledVehicle::DriveTypes::Tank) + { + for (int32 i = 0; i < wheels.Count() - 1; i += 2) + { + auto a = wheels[i]; + auto b = wheels[i + 1]; + if (!a->Collider || !b->Collider) + continue; + if (a->Collider->GetLocalPosition().X > b->Collider->GetLocalPosition().X) + { + wheels[i] = b; + wheels[i + 1] = a; + } + } + } + } + actor->_wheelsData.Resize(wheels.Count()); auto actorPhysX = (PxRigidDynamic*)actor->GetPhysicsActor(); diff --git a/Source/Engine/Renderer/RenderListBuffer.h b/Source/Engine/Renderer/RenderListBuffer.h index e4f8ab19e..e6a22949a 100644 --- a/Source/Engine/Renderer/RenderListBuffer.h +++ b/Source/Engine/Renderer/RenderListBuffer.h @@ -17,8 +17,8 @@ class RenderListBuffer { friend RenderListBuffer; public: - typedef T ItemType; - typedef typename AllocationType::template Data AllocationData; + using ItemType = T; + using AllocationData = typename AllocationType::template Data; private: volatile int64 _count; @@ -42,7 +42,7 @@ public: /// Initializes a new instance of the class. /// /// The initial capacity. - RenderListBuffer(int32 capacity) + explicit RenderListBuffer(const int32 capacity) : _count(0) , _capacity(capacity) { @@ -55,7 +55,7 @@ public: /// /// The initial data. /// The amount of items. - RenderListBuffer(const T* data, int32 length) + RenderListBuffer(const T* data, const int32 length) { ASSERT(length >= 0); _count = _capacity = length; @@ -76,7 +76,7 @@ public: if (_capacity > 0) { _allocation.Allocate(_capacity); - Memory::ConstructItems(_allocation.Get(), other.Get(), (int32)other._count); + Memory::ConstructItems(_allocation.Get(), other.Get(), static_cast(other._count)); } } @@ -102,7 +102,7 @@ public: { if (this != &other) { - Memory::DestructItems(_allocation.Get(), (int32)_count); + Memory::DestructItems(_allocation.Get(), static_cast(_count)); if (_capacity < other.Count()) { _allocation.Free(); @@ -110,7 +110,7 @@ public: _allocation.Allocate(_capacity); } _count = other.Count(); - Memory::ConstructItems(_allocation.Get(), other.Get(), (int32)_count); + Memory::ConstructItems(_allocation.Get(), other.Get(), static_cast(_count)); } return *this; } @@ -124,7 +124,7 @@ public: { if (this != &other) { - Memory::DestructItems(_allocation.Get(), (int32)_count); + Memory::DestructItems(_allocation.Get(), static_cast(_count)); _allocation.Free(); _count = other._count; _capacity = other._capacity; @@ -140,7 +140,7 @@ public: /// ~RenderListBuffer() { - Memory::DestructItems(_allocation.Get(), (int32)_count); + Memory::DestructItems(_allocation.Get(), static_cast(_count)); } public: @@ -149,7 +149,7 @@ public: /// FORCE_INLINE int32 Count() const { - return (int32)Platform::AtomicRead((volatile int64*)&_count); + return static_cast(Platform::AtomicRead((volatile int64*)&_count)); } /// @@ -157,7 +157,7 @@ public: /// FORCE_INLINE int32 Capacity() const { - return (int32)Platform::AtomicRead((volatile int64*)&_capacity); + return static_cast(Platform::AtomicRead((volatile int64*)&_capacity)); } /// @@ -188,7 +188,7 @@ public: /// Gets or sets the item at the given index. /// /// The reference to the item. - FORCE_INLINE T& operator[](int32 index) + FORCE_INLINE T& operator[](const int32 index) { ASSERT(index >= 0 && index < Count()); return _allocation.Get()[index]; @@ -198,7 +198,7 @@ public: /// Gets the item at the given index. /// /// The reference to the item. - FORCE_INLINE const T& operator[](int32 index) const + FORCE_INLINE const T& operator[](const int32 index) const { ASSERT(index >= 0 && index < Count()); return _allocation.Get()[index]; @@ -232,7 +232,7 @@ public: void Clear() { _locker.Lock(); - Memory::DestructItems(_allocation.Get(), (int32)_count); + Memory::DestructItems(_allocation.Get(), static_cast(_count)); _count = 0; _locker.Unlock(); } @@ -242,14 +242,14 @@ public: /// /// The new capacity. /// True if preserve collection data when changing its size, otherwise collection after resize will be empty. - void SetCapacity(const int32 capacity, bool preserveContents = true) + void SetCapacity(const int32 capacity, const bool preserveContents = true) { if (capacity == Capacity()) return; _locker.Lock(); ASSERT(capacity >= 0); - const int32 count = preserveContents ? ((int32)_count < capacity ? (int32)_count : capacity) : 0; - _allocation.Relocate(capacity, (int32)_count, count); + const int32 count = preserveContents ? (static_cast(_count) < capacity ? static_cast(_count) : capacity) : 0; + _allocation.Relocate(capacity, static_cast(_count), count); Platform::AtomicStore(&_capacity, capacity); Platform::AtomicStore(&_count, count); _locker.Unlock(); @@ -260,17 +260,17 @@ public: /// /// The new collection size. /// True if preserve collection data when changing its size, otherwise collection after resize might not contain the previous data. - void Resize(int32 size, bool preserveContents = true) + void Resize(const int32 size, const bool preserveContents = true) { _locker.Lock(); if (_count > size) { - Memory::DestructItems(_allocation.Get() + size, (int32)_count - size); + Memory::DestructItems(_allocation.Get() + size, static_cast(_count) - size); } else { EnsureCapacity(size, preserveContents); - Memory::ConstructItems(_allocation.Get() + _count, size - (int32)_count); + Memory::ConstructItems(_allocation.Get() + _count, size - static_cast(_count)); } _count = size; _locker.Unlock(); @@ -280,14 +280,14 @@ public: /// Ensures the collection has given capacity (or more). /// /// The minimum capacity. - void EnsureCapacity(int32 minCapacity) + void EnsureCapacity(const int32 minCapacity) { _locker.Lock(); - int32 capacity = (int32)Platform::AtomicRead(&_capacity); + int32 capacity = static_cast(Platform::AtomicRead(&_capacity)); if (capacity < minCapacity) { capacity = _allocation.CalculateCapacityGrow(capacity, minCapacity); - const int32 count = (int32)_count; + const int32 count = static_cast(_count); _allocation.Relocate(capacity, count, count); Platform::AtomicStore(&_capacity, capacity); } @@ -324,8 +324,8 @@ private: int32 AddOne() { Platform::InterlockedIncrement(&_threadsAdding); - int32 count = (int32)Platform::AtomicRead(&_count); - int32 capacity = (int32)Platform::AtomicRead(&_capacity); + int32 count = static_cast(Platform::AtomicRead(&_count)); + int32 capacity = static_cast(Platform::AtomicRead(&_capacity)); const int32 minCapacity = GetMinCapacity(count); if (minCapacity > capacity || Platform::AtomicRead(&_threadsResizing)) // Resize if not enough space or someone else is already doing it (don't add mid-resizing) { @@ -340,7 +340,7 @@ private: // Thread-safe resizing _locker.Lock(); - capacity = (int32)Platform::AtomicRead(&_capacity); + capacity = static_cast(Platform::AtomicRead(&_capacity)); if (capacity < minCapacity) { if (Platform::AtomicRead(&_threadsAdding)) @@ -350,7 +350,7 @@ private: goto RETRY; } capacity = _allocation.CalculateCapacityGrow(capacity, minCapacity); - count = (int32)Platform::AtomicRead(&_count); + count = static_cast(Platform::AtomicRead(&_count)); _allocation.Relocate(capacity, count, count); Platform::AtomicStore(&_capacity, capacity); } @@ -362,10 +362,10 @@ private: // Let other thread enter resizing-area _locker.Unlock(); } - return (int32)Platform::InterlockedIncrement(&_count) - 1; + return static_cast(Platform::InterlockedIncrement(&_count)) - 1; } - FORCE_INLINE static int32 GetMinCapacity(int32 count) + FORCE_INLINE static int32 GetMinCapacity(const int32 count) { // Ensure there is a slack for others threads to reduce resize counts in highly multi-threaded environment constexpr int32 slack = PLATFORM_THREADS_LIMIT * 8; diff --git a/Source/Engine/Tests/TestScripting.h b/Source/Engine/Tests/TestScripting.h index b86938b85..a308a9237 100644 --- a/Source/Engine/Tests/TestScripting.h +++ b/Source/Engine/Tests/TestScripting.h @@ -172,6 +172,15 @@ public: return str.Length(); } + // Test trailing return type + API_FUNCTION() auto TestTrailingReturn(int32 number) -> float + { + return static_cast(number); + } + + // Test nameless arguments + API_FUNCTION() void TestNamelessArguments(int32, float, bool){} + int32 TestInterfaceMethod(const String& str) override { return str.Length(); diff --git a/Source/Engine/UI/GUI/CanvasScaler.cs b/Source/Engine/UI/GUI/CanvasScaler.cs index abcd97a38..f27f906b2 100644 --- a/Source/Engine/UI/GUI/CanvasScaler.cs +++ b/Source/Engine/UI/GUI/CanvasScaler.cs @@ -99,8 +99,8 @@ namespace FlaxEngine.GUI private float _scale = 1.0f; private float _scaleFactor = 1.0f; private float _physicalUnitSize = 1.0f; - private Float2 _resolutionMin = new Float2(640, 480); - private Float2 _resolutionMax = new Float2(7680, 4320); + private Float2 _resolutionMin = new Float2(1f, 1f); + private Float2 _resolutionMax = new Float2(7680f, 4320f); /// /// Gets the current UI scale. Computed based on the setup when performing layout. @@ -246,10 +246,11 @@ namespace FlaxEngine.GUI #endif public LinearCurve ResolutionCurve = new LinearCurve(new[] { - new LinearCurve.Keyframe(480, 0.444f), // 480p - new LinearCurve.Keyframe(720, 0.666f), // 720p - new LinearCurve.Keyframe(1080, 1.0f), // 1080p - new LinearCurve.Keyframe(8640, 8.0f), // 8640p + new LinearCurve.Keyframe(0f, 0f), // 0p + new LinearCurve.Keyframe(480f, 0.444f), // 480p + new LinearCurve.Keyframe(720f, 0.666f), // 720p + new LinearCurve.Keyframe(1080f, 1.0f), // 1080p + new LinearCurve.Keyframe(8640f, 8.0f), // 8640p }); /// @@ -335,7 +336,7 @@ namespace FlaxEngine.GUI scale = min / value; else if (value > max) scale = max / value; - if (ResolutionCurve != null && ResolutionCurve.Keyframes?.Length != 0) + if (ResolutionCurve != null && ResolutionCurve.Keyframes?.Length != 0f) { ResolutionCurve.Evaluate(out var curveScale, value, false); scale *= curveScale; @@ -364,13 +365,13 @@ namespace FlaxEngine.GUI dpi = 25.4f; break; case PhysicalUnitMode.Inches: - dpi = 1; + dpi = 1f; break; case PhysicalUnitMode.Points: - dpi = 72; + dpi = 72f; break; case PhysicalUnitMode.Picas: - dpi = 6; + dpi = 6f; break; } return dpi; diff --git a/Source/Platforms/DotNet/Microsoft.VisualStudio.Setup.Configuration.Interop.dll b/Source/Platforms/DotNet/Microsoft.VisualStudio.Setup.Configuration.Interop.dll index fbbba57a5..c589d05c1 100644 --- a/Source/Platforms/DotNet/Microsoft.VisualStudio.Setup.Configuration.Interop.dll +++ b/Source/Platforms/DotNet/Microsoft.VisualStudio.Setup.Configuration.Interop.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f1e8f9f551bd39474ab7bc25b5c78d2cb9acaf4869d79da6ffd0f0f458e6bde -size 21888 +oid sha256:0f12e6ea9bc8aa0d069c29176c5d248b9f3f5c1ed2d3aaf0d9cc1e83f21f56a1 +size 22544 diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index d99fa8b92..fd2fff04a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -390,7 +390,22 @@ namespace Flax.Build.Bindings // Read parameter type and name currentParam.Type = ParseType(ref context); - currentParam.Name = context.Tokenizer.ExpectToken(TokenType.Identifier).Value; + token = context.Tokenizer.NextToken(); + if (token.Type == TokenType.Identifier) + { + currentParam.Name = token.Value; + } + // Support nameless arguments. assume optional usage + else + { + context.Tokenizer.PreviousToken(); + if (string.IsNullOrEmpty(currentParam.Attributes)) + currentParam.Attributes = "Optional"; + else + currentParam.Attributes += ", Optional"; + currentParam.Name = $"namelessArg{parameters.Count}"; + } + if (currentParam.IsOut && (currentParam.Type.IsPtr || currentParam.Type.IsRef) && currentParam.Type.Type.EndsWith("*")) { // Pointer to value passed as output pointer @@ -831,6 +846,7 @@ namespace Flax.Build.Bindings } // Read return type + // Handle if "auto" later desc.ReturnType = ParseType(ref context); // Read name @@ -841,15 +857,21 @@ namespace Flax.Build.Bindings // Read parameters desc.Parameters.AddRange(ParseFunctionParameters(ref context)); - // Read ';' or 'const' or 'override' or '= 0' or '{' + // Read ';' or 'const' or 'override' or '= 0' or '{' or '-' while (true) { - var token = context.Tokenizer.ExpectAnyTokens(new[] { TokenType.SemiColon, TokenType.LeftCurlyBrace, TokenType.Equal, TokenType.Identifier }); + var token = context.Tokenizer.ExpectAnyTokens(new[] { TokenType.SemiColon, TokenType.LeftCurlyBrace, TokenType.Equal, TokenType.Sub, TokenType.Identifier }); if (token.Type == TokenType.Equal) { context.Tokenizer.SkipUntil(TokenType.SemiColon); break; } + // Support auto FunctionName() -> Type + else if (token.Type == TokenType.Sub && desc.ReturnType.ToString() == "auto") + { + context.Tokenizer.SkipUntil(TokenType.GreaterThan); + desc.ReturnType = ParseType(ref context); + } else if (token.Type == TokenType.Identifier) { switch (token.Value) diff --git a/Source/Tools/Flax.Build/Build/Builder.Projects.cs b/Source/Tools/Flax.Build/Build/Builder.Projects.cs index d0ab1dd8a..c3a8cc096 100644 --- a/Source/Tools/Flax.Build/Build/Builder.Projects.cs +++ b/Source/Tools/Flax.Build/Build/Builder.Projects.cs @@ -335,6 +335,12 @@ namespace Flax.Build var referenceTargets = GetProjectTargets(reference.Project); foreach (var referenceTarget in referenceTargets) { + // Skip referenced targets that don't meet this configuration specs (eg. Editor target should skip Android platform) + if (!referenceTarget.Platforms.Contains(configurationData.Platform)) + continue; + if (!referenceTarget.Architectures.Contains(configurationData.Architecture)) + continue; + try { var referenceBuildOptions = GetBuildOptions(referenceTarget, configurationData.TargetBuildOptions.Platform, configurationData.TargetBuildOptions.Toolchain, configurationData.Architecture, configurationData.Configuration, reference.Project.ProjectFolderPath); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs index 88b7a8df5..27092b219 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs @@ -143,6 +143,7 @@ namespace Flax.Build.Projects.VisualStudioCode var buildToolWorkspace = Environment.CurrentDirectory; var buildToolPath = Path.ChangeExtension(Utilities.MakePathRelativeTo(typeof(Builder).Assembly.Location, solution.WorkspaceRootPath), null); var rules = Builder.GenerateRulesAssembly(); + var mainProject = solution.MainProject ?? solution.Projects.FirstOrDefault(x => x.Name == Globals.Project.Name); // Create tasks file using (var json = new JsonWriter()) @@ -159,10 +160,10 @@ namespace Flax.Build.Projects.VisualStudioCode continue; // Skip duplicate build tasks - if (project.Name == "FlaxEngine" || (solution.MainProject.Name != "Flax" && solution.MainProject != project)) + if (project.Name == "FlaxEngine" || (mainProject != null && mainProject.Name != "Flax" && mainProject != project)) continue; - bool defaultTask = project == solution.MainProject; + bool defaultTask = project == mainProject; foreach (var configuration in project.Configurations) { var target = configuration.Target; @@ -301,7 +302,7 @@ namespace Flax.Build.Projects.VisualStudioCode json.BeginArray("configurations"); { var cppProject = solution.Projects.FirstOrDefault(x => x.BaseName == solution.Name || x.Name == solution.Name); - var mainProjectModule = solution.MainProject?.Targets.Length != 0 ? solution.MainProject.Targets[0].Modules[0] : null; + var mainProjectModule = mainProject != null && mainProject.Targets?.Length != 0 ? mainProject.Targets[0]?.Modules[0] : null; var csharpProject = mainProjectModule != null ? solution.Projects.FirstOrDefault(x => x.BaseName == mainProjectModule || x.Name == mainProjectModule) : null; if (cppProject != null) @@ -533,10 +534,9 @@ namespace Flax.Build.Projects.VisualStudioCode json.BeginRootObject(); json.BeginArray("configurations"); json.BeginObject(); - var project = solution.MainProject ?? solution.Projects.FirstOrDefault(x => x.Name == Globals.Project.Name); - if (project != null) + if (mainProject != null) { - json.AddField("name", project.Name); + json.AddField("name", mainProject.Name); var targetPlatform = Platform.BuildPlatform.Target; var configuration = TargetConfiguration.Development; @@ -544,10 +544,10 @@ namespace Flax.Build.Projects.VisualStudioCode var includePaths = new HashSet(); var preprocessorDefinitions = new HashSet(); - foreach (var e in project.Defines) + foreach (var e in mainProject.Defines) preprocessorDefinitions.Add(e); - foreach (var target in project.Targets) + foreach (var target in mainProject.Targets) { var platform = Platform.GetPlatform(targetPlatform); if (platform.HasRequiredSDKsInstalled && target.Platforms.Contains(targetPlatform))