From 39f4c00135eb46232a051131b01ff23d14e3871f Mon Sep 17 00:00:00 2001 From: envision3d Date: Wed, 28 Jun 2023 02:02:10 -0500 Subject: [PATCH 01/52] add play button actions, number of players - add context menu support for toolstrip buttons - add "Play Game" vs. "Play Scenes" - add context menu for choosing play button action to toolstrip button - add number of game client selection for cook & run - add context menu for cook & run to toolstrip button - add menu items for the above - add editor option entries for saving user preferences for the above --- .../ContextMenuSingleSelectGroup.cs | 108 ++++++++++++ Source/Editor/GUI/ToolStrip.cs | 24 ++- Source/Editor/GUI/ToolStripButton.cs | 62 +++++-- Source/Editor/Modules/UIModule.cs | 140 +++++++++++++++- Source/Editor/Options/InterfaceOptions.cs | 38 ++++- Source/Editor/Windows/GameCookerWindow.cs | 157 ++++++++++-------- .../Editor/Windows/Profiler/ProfilerWindow.cs | 2 +- 7 files changed, 427 insertions(+), 104 deletions(-) create mode 100644 Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs new file mode 100644 index 000000000..794729547 --- /dev/null +++ b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs @@ -0,0 +1,108 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.GUI.ContextMenu +{ + /// + /// Context menu single select group. + /// + [HideInEditor] + class ContextMenuSingleSelectGroup + { + public struct SingleSelectGroupItem + { + public string text; + public U value; + public Action onSelected; + public List buttons; + } + + private List _menus = new List(); + private List> _items = new List>(); + public Action OnSelectionChanged; + + public SingleSelectGroupItem activeItem; + + public ContextMenuSingleSelectGroup AddItem(string text, T value, Action onSelected = null) + { + var item = new SingleSelectGroupItem + { + text = text, + value = value, + onSelected = onSelected, + buttons = new List() + }; + _items.Add(item); + + foreach (var contextMenu in _menus) + AddItemToContextMenu(contextMenu, item); + + return this; + } + + public ContextMenuSingleSelectGroup AddItemsToContextMenu(ContextMenu contextMenu) + { + _menus.Add(contextMenu); + + for (int i = 0; i < _items.Count; i++) + { + AddItemToContextMenu(contextMenu, _items[i]); + } + + return this; + } + + private void AddItemToContextMenu(ContextMenu contextMenu, SingleSelectGroupItem item) + { + var btn = contextMenu.AddButton(item.text, () => + { + SetItemAsActive(item); + }); + + item.buttons.Add(btn); + + if (item.Equals(activeItem)) + { + btn.Checked = true; + } + } + + private void DeselectAll() + { + foreach (var item in _items) + { + foreach (var btn in item.buttons) btn.Checked = false; + } + } + + public void SetItemAsActive(T value) + { + var index = _items.FindIndex(x => x.value.Equals(value)); + + if (index == -1) return; + + SetItemAsActive(_items[index]); + } + + private void SetItemAsActive(SingleSelectGroupItem item) + { + DeselectAll(); + + var index = _items.IndexOf(item); + OnSelectionChanged?.Invoke(item.value); + item.onSelected?.Invoke(); + + foreach (var btn in item.buttons) + { + btn.Checked = true; + } + + activeItem = item; + } + } +} diff --git a/Source/Editor/GUI/ToolStrip.cs b/Source/Editor/GUI/ToolStrip.cs index 6a881281f..858e21b51 100644 --- a/Source/Editor/GUI/ToolStrip.cs +++ b/Source/Editor/GUI/ToolStrip.cs @@ -23,9 +23,14 @@ namespace FlaxEditor.GUI public const int DefaultMarginH = 2; /// - /// Event fired when button gets clicked. + /// Event fired when button gets clicked with the primary mouse button. /// - public Action ButtonClicked; + public Action ButtonPrimaryClicked; + + /// + /// Event fired when button gets clicked with the secondary mouse button. + /// + public Action ButtonSecondaryClicked; /// /// Tries to get the last button. @@ -91,7 +96,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.Clicked += onClick; + button.PrimaryClicked += onClick; return button; } @@ -110,7 +115,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.Clicked += onClick; + button.PrimaryClicked += onClick; return button; } @@ -128,7 +133,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.Clicked += onClick; + button.PrimaryClicked += onClick; return button; } @@ -141,9 +146,14 @@ namespace FlaxEditor.GUI return AddChild(new ToolStripSeparator(ItemsHeight)); } - internal void OnButtonClicked(ToolStripButton button) + internal void OnButtonPrimaryClicked(ToolStripButton button) { - ButtonClicked?.Invoke(button); + ButtonPrimaryClicked?.Invoke(button); + } + + internal void OnButtonSecondaryClicked(ToolStripButton button) + { + ButtonSecondaryClicked?.Invoke(button); } /// diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index 09a26b940..f8d032696 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -11,8 +11,12 @@ namespace FlaxEditor.GUI /// /// [HideInEditor] - public class ToolStripButton : Control + public partial class ToolStripButton : Control { + // TODO: abstracted for potential future in-editor input configuration (ex. for left handed mouse users) + private const MouseButton PRIMARY_MOUSE_BUTTON = MouseButton.Left; + private const MouseButton SECONDARY_MOUSE_BUTTON = MouseButton.Right; + /// /// The default margin for button parts (icon, text, etc.). /// @@ -20,12 +24,18 @@ namespace FlaxEditor.GUI private SpriteHandle _icon; private string _text; - private bool _mouseDown; + private bool _primaryMouseDown; + private bool _secondaryMouseDown; /// /// Event fired when user clicks the button. /// - public Action Clicked; + public Action PrimaryClicked; + + /// + /// Event fired when user clicks the button. + /// + public Action SecondaryClicked; /// /// The checked state. @@ -63,6 +73,11 @@ namespace FlaxEditor.GUI } } + /// + /// A reference to a context menu to raise when the secondary mouse button is pressed. + /// + public ContextMenu.ContextMenu ContextMenu; + /// /// Initializes a new instance of the class. /// @@ -108,10 +123,11 @@ namespace FlaxEditor.GUI var iconRect = new Rectangle(DefaultMargin, DefaultMargin, iconSize, iconSize); var textRect = new Rectangle(DefaultMargin, 0, 0, Height); bool enabled = EnabledInHierarchy; + bool mouseButtonDown = _primaryMouseDown || _secondaryMouseDown; // Draw background if (enabled && (IsMouseOver || IsNavFocused || Checked)) - Render2D.FillRectangle(clientRect, Checked ? style.BackgroundSelected : _mouseDown ? style.BackgroundHighlighted : (style.LightBackground * 1.3f)); + Render2D.FillRectangle(clientRect, Checked ? style.BackgroundSelected : mouseButtonDown ? style.BackgroundHighlighted : (style.LightBackground * 1.3f)); // Draw icon if (_icon.IsValid) @@ -153,10 +169,18 @@ namespace FlaxEditor.GUI /// public override bool OnMouseDown(Float2 location, MouseButton button) { - if (button == MouseButton.Left) + if (button == PRIMARY_MOUSE_BUTTON) { // Set flag - _mouseDown = true; + _primaryMouseDown = true; + + Focus(); + return true; + } + else if (button == SECONDARY_MOUSE_BUTTON) + { + // Set flag + _secondaryMouseDown = true; Focus(); return true; @@ -168,16 +192,28 @@ namespace FlaxEditor.GUI /// public override bool OnMouseUp(Float2 location, MouseButton button) { - if (button == MouseButton.Left && _mouseDown) + if (button == PRIMARY_MOUSE_BUTTON && _primaryMouseDown) { // Clear flag - _mouseDown = false; + _primaryMouseDown = false; // Fire events if (AutoCheck) Checked = !Checked; - Clicked?.Invoke(); - (Parent as ToolStrip)?.OnButtonClicked(this); + PrimaryClicked?.Invoke(); + (Parent as ToolStrip)?.OnButtonPrimaryClicked(this); + + return true; + } + else if (button == SECONDARY_MOUSE_BUTTON && _secondaryMouseDown) + { + // Clear flag + _secondaryMouseDown = false; + + SecondaryClicked?.Invoke(); + (Parent as ToolStrip)?.OnButtonSecondaryClicked(this); + + ContextMenu?.Show(this, new Float2(0, Height)); return true; } @@ -189,7 +225,8 @@ namespace FlaxEditor.GUI public override void OnMouseLeave() { // Clear flag - _mouseDown = false; + _primaryMouseDown = false; + _secondaryMouseDown = false; base.OnMouseLeave(); } @@ -198,7 +235,8 @@ namespace FlaxEditor.GUI public override void OnLostFocus() { // Clear flag - _mouseDown = false; + _primaryMouseDown = false; + _secondaryMouseDown = false; base.OnLostFocus(); } diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 858ef2b1a..c41ccbec3 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -20,6 +20,8 @@ using FlaxEngine.GUI; using FlaxEngine.Json; using DockHintWindow = FlaxEditor.GUI.Docking.DockHintWindow; using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel; +using FlaxEditor.Content.Settings; +using static FlaxEditor.Options.InterfaceOptions; namespace FlaxEditor.Modules { @@ -36,6 +38,9 @@ namespace FlaxEditor.Modules private ContentStats _contentStats; private bool _progressFailed; + ContextMenuSingleSelectGroup _numberOfClientsGroup = new ContextMenuSingleSelectGroup(); + private Scene[] _scenesToReload; + private ContextMenuButton _menuFileSaveScenes; private ContextMenuButton _menuFileCloseScenes; private ContextMenuButton _menuFileGenerateScriptsProjectFiles; @@ -54,7 +59,9 @@ namespace FlaxEditor.Modules private ContextMenuButton _menuSceneAlignViewportWithActor; private ContextMenuButton _menuScenePilotActor; private ContextMenuButton _menuSceneCreateTerrain; - private ContextMenuButton _menuGamePlay; + private ContextMenuButton _menuGamePlayGame; + private ContextMenuButton _menuGamePlayCurrentScenes; + private ContextMenuButton _menuGameStop; private ContextMenuButton _menuGamePause; private ContextMenuButton _menuToolsBuildScenes; private ContextMenuButton _menuToolsBakeLightmaps; @@ -74,6 +81,7 @@ namespace FlaxEditor.Modules private ToolStripButton _toolStripRotate; private ToolStripButton _toolStripScale; private ToolStripButton _toolStripBuildScenes; + private ToolStripButton _toolStripCook; private ToolStripButton _toolStripPlay; private ToolStripButton _toolStripPause; private ToolStripButton _toolStripStep; @@ -351,6 +359,7 @@ namespace FlaxEditor.Modules // Update window background mainWindow.BackgroundColor = Style.Current.Background; + InitSharedMenus(); InitMainMenu(mainWindow); InitToolstrip(mainWindow); InitStatusBar(mainWindow); @@ -466,6 +475,19 @@ namespace FlaxEditor.Modules return dialog; } + private void InitSharedMenus() + { + for (int i = 1; i <= 4; i++) _numberOfClientsGroup.AddItem(i.ToString(), i); + + _numberOfClientsGroup.SetItemAsActive(Editor.Options.Options.Interface.NumberOfGameClientsToLaunch); + _numberOfClientsGroup.OnSelectionChanged = (value) => + { + var options = Editor.Options.Options; + options.Interface.NumberOfGameClientsToLaunch = value; + Editor.Options.Apply(options); + }; + } + private void InitMainMenu(RootControl mainWindow) { MainMenu = new MainMenu(mainWindow) @@ -523,11 +545,19 @@ namespace FlaxEditor.Modules MenuGame = MainMenu.AddButton("Game"); cm = MenuGame.ContextMenu; cm.VisibleChanged += OnMenuGameShowHide; - _menuGamePlay = cm.AddButton("Play", inputOptions.Play.ToString(), Editor.Simulation.RequestStartPlay); + + _menuGamePlayGame = cm.AddButton("Play Game", PlayGame); + _menuGamePlayCurrentScenes = cm.AddButton("Play Current Scenes", inputOptions.Play.ToString(), PlayScenes); + _menuGameStop = cm.AddButton("Stop Game", Editor.Simulation.RequestStopPlay); _menuGamePause = cm.AddButton("Pause", inputOptions.Pause.ToString(), Editor.Simulation.RequestPausePlay); + cm.AddSeparator(); - cm.AddButton("Cook & Run", Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip("Runs Game Cooker to build the game for this platform and runs the game after."); - cm.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked).LinkTooltip("Runs the game build from the last cooking output. Use Cook&Play or Game Cooker first."); + var numberOfClientsMenu = cm.AddChildMenu("Number of game clients"); + _numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu); + + cm.AddSeparator(); + cm.AddButton("Cook & Run", CookAndRun).LinkTooltip("Runs Game Cooker to build the game for this platform and runs the game after."); + cm.AddButton("Run cooked game", RunCookedGame).LinkTooltip("Runs the game build from the last cooking output. Use Cook&Play or Game Cooker first."); // Tools MenuTools = MainMenu.AddButton("Tools"); @@ -603,7 +633,7 @@ namespace FlaxEditor.Modules _menuEditDuplicate.ShortKeys = inputOptions.Duplicate.ToString(); _menuEditSelectAll.ShortKeys = inputOptions.SelectAll.ToString(); _menuEditFind.ShortKeys = inputOptions.Search.ToString(); - _menuGamePlay.ShortKeys = inputOptions.Play.ToString(); + _menuGamePlayCurrentScenes.ShortKeys = inputOptions.Play.ToString(); _menuGamePause.ShortKeys = inputOptions.Pause.ToString(); MainMenuShortcutKeysUpdated?.Invoke(); @@ -611,6 +641,8 @@ namespace FlaxEditor.Modules private void InitToolstrip(RootControl mainWindow) { + var inputOptions = Editor.Options.Options.Input; + ToolStrip = new ToolStrip(34.0f, MainMenu.Bottom) { Parent = mainWindow, @@ -627,8 +659,34 @@ namespace FlaxEditor.Modules ToolStrip.AddSeparator(); _toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options (Ctrl+F10)"); ToolStrip.AddSeparator(); - _toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play64, Editor.Simulation.RequestPlayOrStopPlay).LinkTooltip("Start/Stop game (F5)"); - _toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip("Pause/Resume game(F6)"); + + // build + _toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.BuildSettings128, CookAndRun).LinkTooltip("Cook and run"); + _toolStripCook.ContextMenu = new ContextMenu(); + _toolStripCook.ContextMenu.AddButton("Run cooked game", RunCookedGame); + _toolStripCook.ContextMenu.AddSeparator(); + var numberOfClientsMenu = _toolStripCook.ContextMenu.AddChildMenu("Number of game clients"); + _numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu); + + // play + _toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play64, OnPlayPressed).LinkTooltip("Play Game"); + _toolStripPlay.ContextMenu = new ContextMenu(); + ContextMenuChildMenu playSubMenu; + + // play - button action + playSubMenu = _toolStripPlay.ContextMenu.AddChildMenu("Play button action"); + var playActionGroup = new ContextMenuSingleSelectGroup(); + playActionGroup.AddItem("Play Game", PlayAction.PlayGame); + playActionGroup.AddItem("Play Scenes", PlayAction.PlayScenes); + playActionGroup.AddItemsToContextMenu(playSubMenu.ContextMenu); + playActionGroup.SetItemAsActive(Editor.Options.Options.Interface.PlayButtonAction); + // important to add the handler after setting the initial item as active above or the editor will crash + playActionGroup.OnSelectionChanged = SetPlayAction; + // TODO: there are some holes in the syncing of these values + // - when changing in the editor, the options will be updated, but the options UI in-editor will not + // - when updating the value in the options UI, the value in the tool strip will not update (adding an options changed event handler results in a crash) + + _toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game({inputOptions.Pause.ToString()})"); _toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game"); UpdateToolstrip(); @@ -786,7 +844,9 @@ namespace FlaxEditor.Modules var isPlayMode = Editor.StateMachine.IsPlayMode; var canPlay = Level.IsAnySceneLoaded; - _menuGamePlay.Enabled = !isPlayMode && canPlay; + _menuGamePlayGame.Enabled = !isPlayMode && canPlay; + _menuGamePlayCurrentScenes.Enabled = !isPlayMode && canPlay; + _menuGameStop.Enabled = isPlayMode && canPlay; _menuGamePause.Enabled = isPlayMode && canPlay; c.PerformLayout(); @@ -968,6 +1028,70 @@ namespace FlaxEditor.Modules projectInfo.Save(); } + private void SetPlayAction(PlayAction newPlayAction) + { + var options = Editor.Options.Options; + options.Interface.PlayButtonAction = newPlayAction; + Editor.Options.Apply(options); + } + + private void OnPlayPressed() + { + switch (Editor.Options.Options.Interface.PlayButtonAction) + { + case PlayAction.PlayGame: + if (Editor.IsPlayMode) + Editor.Simulation.RequestStopPlay(); + else + PlayGame(); + return; + case PlayAction.PlayScenes: PlayScenes(); return; + } + } + + private void PlayGame() + { + var firstScene = GameSettings.Load().FirstScene; + + if (firstScene == Guid.Empty) + { + if (Level.IsAnySceneLoaded) Editor.Simulation.RequestStartPlay(); + return; + } + + _scenesToReload = Level.Scenes; + Level.UnloadAllScenes(); + Level.LoadScene(firstScene); + + Editor.PlayModeEnd += OnPlayGameSceneEnding; + Editor.Simulation.RequestPlayOrStopPlay(); + } + + private void OnPlayGameSceneEnding() + { + Editor.PlayModeEnd -= OnPlayGameSceneEnding; + + Level.UnloadAllScenes(); + + foreach (var scene in _scenesToReload) + Level.LoadScene(scene.ID); + } + + private void PlayScenes() + { + Editor.Simulation.RequestPlayOrStopPlay(); + } + + private void CookAndRun() + { + Editor.Windows.GameCookerWin.BuildAndRun(); + } + + private void RunCookedGame() + { + Editor.Windows.GameCookerWin.RunCooked(); + } + private void OnMainWindowClosing() { // Clear UI references (GUI cannot be used after window closing) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index acfb7f5e8..7d97fecd6 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -74,6 +74,21 @@ namespace FlaxEditor.Options DockBottom = DockState.DockBottom } + /// + /// Options for the action taken by the play button. + /// + public enum PlayAction + { + /// + /// Launches the game from the First Scene defined in the project settings. + /// + PlayGame, + /// + /// Launches the game using the scenes currently loaded in the editor. + /// + PlayScenes + } + /// /// 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. /// @@ -196,30 +211,45 @@ namespace FlaxEditor.Options [EditorDisplay("Play In-Editor", "Focus Game Window On Play"), EditorOrder(400), Tooltip("Determines whether auto-focus game window on play mode start.")] public bool FocusGameWinOnPlay { get; set; } = true; + /// + /// Gets or sets a value indicating what action should be taken upon pressing the play button. + /// + [DefaultValue(PlayAction.PlayScenes)] + [EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(410), Tooltip("Determines the action taken when the play button is pressed.")] + public PlayAction PlayButtonAction { get; set; } = PlayAction.PlayScenes; + + /// + /// Gets or sets a value indicating the number of game clients to launch when building and/or running cooked game. + /// + [DefaultValue(1)] + [EditorDisplay("Cook & Run", "Number Of GameClients To Launch"), EditorOrder(500), Tooltip("Determines the number of game clients to launch when building and/or running cooked game.")] + [Range(1, 4)] + public int NumberOfGameClientsToLaunch = 1; + private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); /// /// Gets or sets the title font for editor UI. /// - [EditorDisplay("Fonts"), EditorOrder(500), Tooltip("The title font for editor UI.")] + [EditorDisplay("Fonts"), EditorOrder(600), Tooltip("The title font for editor UI.")] public FontReference TitleFont { get; set; } = new FontReference(DefaultFont, 18); /// /// Gets or sets the large font for editor UI. /// - [EditorDisplay("Fonts"), EditorOrder(510), Tooltip("The large font for editor UI.")] + [EditorDisplay("Fonts"), EditorOrder(610), Tooltip("The large font for editor UI.")] public FontReference LargeFont { get; set; } = new FontReference(DefaultFont, 14); /// /// Gets or sets the medium font for editor UI. /// - [EditorDisplay("Fonts"), EditorOrder(520), Tooltip("The medium font for editor UI.")] + [EditorDisplay("Fonts"), EditorOrder(620), Tooltip("The medium font for editor UI.")] public FontReference MediumFont { get; set; } = new FontReference(DefaultFont, 9); /// /// Gets or sets the small font for editor UI. /// - [EditorDisplay("Fonts"), EditorOrder(530), Tooltip("The small font for editor UI.")] + [EditorDisplay("Fonts"), EditorOrder(630), Tooltip("The small font for editor UI.")] public FontReference SmallFont { get; set; } = new FontReference(DefaultFont, 9); } } diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index f3fd753d1..f88fa36f5 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -110,14 +110,14 @@ namespace FlaxEditor.Windows #if PLATFORM_WINDOWS switch (BuildPlatform) { - case BuildPlatform.MacOSx64: - case BuildPlatform.MacOSARM64: - case BuildPlatform.iOSARM64: - IsSupported = false; - break; - default: - IsSupported = true; - break; + case BuildPlatform.MacOSx64: + case BuildPlatform.MacOSARM64: + case BuildPlatform.iOSARM64: + IsSupported = false; + break; + default: + IsSupported = true; + break; } #elif PLATFORM_LINUX switch (BuildPlatform) @@ -160,17 +160,17 @@ namespace FlaxEditor.Windows { switch (BuildPlatform) { - case BuildPlatform.Windows32: - case BuildPlatform.Windows64: - case BuildPlatform.UWPx86: - case BuildPlatform.UWPx64: - case BuildPlatform.LinuxX64: - case BuildPlatform.AndroidARM64: - layout.Label("Use Flax Launcher and download the required package.", TextAlignment.Center); - break; - default: - layout.Label("Engine source is required to target this platform.", TextAlignment.Center); - break; + case BuildPlatform.Windows32: + case BuildPlatform.Windows64: + case BuildPlatform.UWPx86: + case BuildPlatform.UWPx64: + case BuildPlatform.LinuxX64: + case BuildPlatform.AndroidARM64: + layout.Label("Use Flax Launcher and download the required package.", TextAlignment.Center); + break; + default: + layout.Label("Engine source is required to target this platform.", TextAlignment.Center); + break; } } else @@ -272,43 +272,43 @@ namespace FlaxEditor.Windows string name; switch (_platform) { - case PlatformType.Windows: - name = "Windows"; - break; - case PlatformType.XboxOne: - name = "Xbox One"; - break; - case PlatformType.UWP: - name = "Windows Store"; - layout.Label("UWP (Windows Store) platform has been deprecated and is no longer supported", TextAlignment.Center).Label.TextColor = Color.Red; - break; - case PlatformType.Linux: - name = "Linux"; - break; - case PlatformType.PS4: - name = "PlayStation 4"; - break; - case PlatformType.XboxScarlett: - name = "Xbox Scarlett"; - break; - case PlatformType.Android: - name = "Android"; - break; - case PlatformType.Switch: - name = "Switch"; - break; - case PlatformType.PS5: - name = "PlayStation 5"; - break; - case PlatformType.Mac: - name = "Mac"; - break; - case PlatformType.iOS: - name = "iOS"; - break; - default: - name = Utilities.Utils.GetPropertyNameUI(_platform.ToString()); - break; + case PlatformType.Windows: + name = "Windows"; + break; + case PlatformType.XboxOne: + name = "Xbox One"; + break; + case PlatformType.UWP: + name = "Windows Store"; + layout.Label("UWP (Windows Store) platform has been deprecated and is no longer supported", TextAlignment.Center).Label.TextColor = Color.Red; + break; + case PlatformType.Linux: + name = "Linux"; + break; + case PlatformType.PS4: + name = "PlayStation 4"; + break; + case PlatformType.XboxScarlett: + name = "Xbox Scarlett"; + break; + case PlatformType.Android: + name = "Android"; + break; + case PlatformType.Switch: + name = "Switch"; + break; + case PlatformType.PS5: + name = "PlayStation 5"; + break; + case PlatformType.Mac: + name = "Mac"; + break; + case PlatformType.iOS: + name = "iOS"; + break; + default: + name = Utilities.Utils.GetPropertyNameUI(_platform.ToString()); + break; } var group = layout.Group(name); @@ -659,16 +659,24 @@ namespace FlaxEditor.Windows { Editor.Log("Building and running"); GameCooker.GetCurrentPlatform(out var platform, out var buildPlatform, out var buildConfiguration); - _buildingQueue.Enqueue(new QueueItem + var numberOfClients = Editor.Options.Options.Interface.NumberOfGameClientsToLaunch; + + for (int i = 0; i < numberOfClients; i++) { - Target = new BuildTarget + var buildOptions = BuildOptions.AutoRun; + if (i > 0) buildOptions |= BuildOptions.NoCook; + + _buildingQueue.Enqueue(new QueueItem { - Output = _buildTabProxy.PerPlatformOptions[platform].Output, - Platform = buildPlatform, - Mode = buildConfiguration, - }, - Options = BuildOptions.AutoRun, - }); + Target = new BuildTarget + { + Output = _buildTabProxy.PerPlatformOptions[platform].Output, + Platform = buildPlatform, + Mode = buildConfiguration, + }, + Options = buildOptions, + }); + } } /// @@ -678,16 +686,21 @@ namespace FlaxEditor.Windows { Editor.Log("Running cooked build"); GameCooker.GetCurrentPlatform(out var platform, out var buildPlatform, out var buildConfiguration); - _buildingQueue.Enqueue(new QueueItem + var numberOfClients = Editor.Options.Options.Interface.NumberOfGameClientsToLaunch; + + for (int i = 0; i < numberOfClients; i++) { - Target = new BuildTarget + _buildingQueue.Enqueue(new QueueItem { - Output = _buildTabProxy.PerPlatformOptions[platform].Output, - Platform = buildPlatform, - Mode = buildConfiguration, - }, - Options = BuildOptions.AutoRun | BuildOptions.NoCook, - }); + Target = new BuildTarget + { + Output = _buildTabProxy.PerPlatformOptions[platform].Output, + Platform = buildPlatform, + Mode = buildConfiguration, + }, + Options = BuildOptions.AutoRun | BuildOptions.NoCook, + }); + } } private void BuildTarget() diff --git a/Source/Editor/Windows/Profiler/ProfilerWindow.cs b/Source/Editor/Windows/Profiler/ProfilerWindow.cs index f5a5c6f86..b35641bf6 100644 --- a/Source/Editor/Windows/Profiler/ProfilerWindow.cs +++ b/Source/Editor/Windows/Profiler/ProfilerWindow.cs @@ -93,7 +93,7 @@ namespace FlaxEditor.Windows.Profiler _liveRecordingButton = toolstrip.AddButton(editor.Icons.Play64); _liveRecordingButton.LinkTooltip("Live profiling events recording"); _liveRecordingButton.AutoCheck = true; - _liveRecordingButton.Clicked += () => _liveRecordingButton.Icon = LiveRecording ? editor.Icons.Stop64 : editor.Icons.Play64; + _liveRecordingButton.PrimaryClicked += () => _liveRecordingButton.Icon = LiveRecording ? editor.Icons.Stop64 : editor.Icons.Play64; _clearButton = toolstrip.AddButton(editor.Icons.Rotate32, Clear); _clearButton.LinkTooltip("Clear data"); toolstrip.AddSeparator(); From 940b0e02e5d8410b3e6db7f39dfc40ce2faecdc7 Mon Sep 17 00:00:00 2001 From: envision3d Date: Wed, 28 Jun 2023 04:08:36 -0500 Subject: [PATCH 02/52] improve state syncing of context menus and editor options --- .../ContextMenuSingleSelectGroup.cs | 3 +-- Source/Editor/Modules/UIModule.cs | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs index 794729547..9b58c09a8 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs @@ -92,6 +92,7 @@ namespace FlaxEditor.GUI.ContextMenu private void SetItemAsActive(SingleSelectGroupItem item) { DeselectAll(); + activeItem = item; var index = _items.IndexOf(item); OnSelectionChanged?.Invoke(item.value); @@ -101,8 +102,6 @@ namespace FlaxEditor.GUI.ContextMenu { btn.Checked = true; } - - activeItem = item; } } } diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index c41ccbec3..5e0af20fe 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -486,6 +486,14 @@ namespace FlaxEditor.Modules options.Interface.NumberOfGameClientsToLaunch = value; Editor.Options.Apply(options); }; + + Editor.Options.OptionsChanged += (options) => + { + if (options.Interface.NumberOfGameClientsToLaunch != _numberOfClientsGroup.activeItem.value) + { + _numberOfClientsGroup.SetItemAsActive(options.Interface.NumberOfGameClientsToLaunch); + } + }; } private void InitMainMenu(RootControl mainWindow) @@ -682,9 +690,16 @@ namespace FlaxEditor.Modules playActionGroup.SetItemAsActive(Editor.Options.Options.Interface.PlayButtonAction); // important to add the handler after setting the initial item as active above or the editor will crash playActionGroup.OnSelectionChanged = SetPlayAction; - // TODO: there are some holes in the syncing of these values + // TODO: there is a hole in the syncing of these values: // - when changing in the editor, the options will be updated, but the options UI in-editor will not - // - when updating the value in the options UI, the value in the tool strip will not update (adding an options changed event handler results in a crash) + + Editor.Options.OptionsChanged += (options) => + { + if (options.Interface.PlayButtonAction != playActionGroup.activeItem.value) + { + playActionGroup.SetItemAsActive(options.Interface.PlayButtonAction); + } + }; _toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game({inputOptions.Pause.ToString()})"); _toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game"); From ff56152ef2b9166345405e93ba1fa4de6a1c2b11 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 15 Jul 2023 13:19:22 +0300 Subject: [PATCH 03/52] Fix releasing non-collectible types with collectible generic types --- .../Engine/Engine/NativeInterop.Unmanaged.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 17b8b73af..57454ad1e 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -244,7 +244,25 @@ namespace FlaxEngine.Interop @namespace = NativeAllocStringAnsi(type.Namespace ?? ""), typeAttributes = (uint)type.Attributes, }; - *assemblyHandle = GetAssemblyHandle(type.Assembly); + + Assembly assembly = null; + if (type.IsGenericType && !type.Assembly.IsCollectible) + { + // The owning assembly of a generic type with type arguments referencing + // collectible assemblies must be one of the collectible assemblies. + foreach (var genericType in type.GetGenericArguments()) + { + if (genericType.Assembly.IsCollectible) + { + assembly = genericType.Assembly; + break; + } + } + } + if (assembly == null) + assembly = type.Assembly; + + *assemblyHandle = GetAssemblyHandle(assembly); } [UnmanagedCallersOnly] From ba93e1e1d0bcd711a46e6a573abc2085717a2e5d Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Jul 2023 10:00:34 -0500 Subject: [PATCH 04/52] Add showing default materials to model entries. --- .../CustomEditors/Editors/AssetRefEditor.cs | 35 ++++---- .../Editors/ModelInstanceEntryEditor.cs | 85 +++++++++++++++++++ Source/Engine/Graphics/GPUDevice.h | 2 +- 3 files changed, 105 insertions(+), 17 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index 17999d772..b83b44d59 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -33,7 +33,10 @@ namespace FlaxEditor.CustomEditors.Editors [CustomEditor(typeof(Asset)), DefaultEditor] public class AssetRefEditor : CustomEditor { - private AssetPicker _picker; + /// + /// The asset picker used to get a reference to an asset. + /// + public AssetPicker Picker; private ScriptType _valueType; /// @@ -44,7 +47,7 @@ namespace FlaxEditor.CustomEditors.Editors { if (HasDifferentTypes) return; - _picker = layout.Custom().CustomControl; + Picker = layout.Custom().CustomControl; _valueType = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]); var assetType = _valueType; @@ -66,7 +69,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Generic file picker assetType = ScriptType.Null; - _picker.FileExtension = assetReference.TypeName; + Picker.FileExtension = assetReference.TypeName; } else { @@ -78,23 +81,23 @@ namespace FlaxEditor.CustomEditors.Editors } } - _picker.AssetType = assetType; - _picker.Height = height; - _picker.SelectedItemChanged += OnSelectedItemChanged; + Picker.AssetType = assetType; + Picker.Height = height; + Picker.SelectedItemChanged += OnSelectedItemChanged; } private void OnSelectedItemChanged() { if (typeof(AssetItem).IsAssignableFrom(_valueType.Type)) - SetValue(_picker.SelectedItem); + SetValue(Picker.SelectedItem); else if (_valueType.Type == typeof(Guid)) - SetValue(_picker.SelectedID); + SetValue(Picker.SelectedID); else if (_valueType.Type == typeof(SceneReference)) - SetValue(new SceneReference(_picker.SelectedID)); + SetValue(new SceneReference(Picker.SelectedID)); else if (_valueType.Type == typeof(string)) - SetValue(_picker.SelectedPath); + SetValue(Picker.SelectedPath); else - SetValue(_picker.SelectedAsset); + SetValue(Picker.SelectedAsset); } /// @@ -105,15 +108,15 @@ namespace FlaxEditor.CustomEditors.Editors if (!HasDifferentValues) { if (Values[0] is AssetItem assetItem) - _picker.SelectedItem = assetItem; + Picker.SelectedItem = assetItem; else if (Values[0] is Guid guid) - _picker.SelectedID = guid; + Picker.SelectedID = guid; else if (Values[0] is SceneReference sceneAsset) - _picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID); + Picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID); else if (Values[0] is string path) - _picker.SelectedPath = path; + Picker.SelectedPath = path; else - _picker.SelectedAsset = Values[0] as Asset; + Picker.SelectedAsset = Values[0] as Asset; } } } diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index a08a254d4..6ac96fcea 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -1,6 +1,8 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEditor.CustomEditors.Elements; +using FlaxEditor.CustomEditors.GUI; +using FlaxEditor.Scripting; using FlaxEngine; namespace FlaxEditor.CustomEditors.Editors @@ -13,6 +15,8 @@ namespace FlaxEditor.CustomEditors.Editors { private GroupElement _group; private bool _updateName; + private MaterialBase _material; + private ModelInstanceEntry _entry; /// public override void Initialize(LayoutElementsContainer layout) @@ -20,10 +24,91 @@ namespace FlaxEditor.CustomEditors.Editors _updateName = true; var group = layout.Group("Entry"); _group = group; + + _entry = (ModelInstanceEntry)Values[0]; + var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this); + var materiaLabel = new PropertyNameLabel("Material"); + materiaLabel.TooltipText = "The mesh surface material used for the rendering."; + if (ParentEditor.ParentEditor.Values[0] is StaticModel staticModel) + { + // Ensure that entry with default material set is set back to null + if (_entry.Material == staticModel.Model.MaterialSlots[entryIndex].Material) + { + staticModel.SetMaterial(entryIndex, null); + } + _material = staticModel.GetMaterial(entryIndex); + var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); + var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; + materialEditor.Values.SetDefaultValue(staticModel.Model.MaterialSlots[entryIndex].Material); + materialEditor.RefreshDefaultValue(); + materialEditor.Picker.SelectedItemChanged += () => + { + var material = materialEditor.Picker.SelectedAsset as MaterialBase; + if (!material) + { + staticModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); + materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; + } + else if (material == staticModel.Model.MaterialSlots[entryIndex]) + { + staticModel.SetMaterial(entryIndex, null); + } + else + { + staticModel.SetMaterial(entryIndex, material); + } + }; + } + else if (ParentEditor.ParentEditor.Values[0] is AnimatedModel animatedModel) + { + // Ensure that entry with default material set is set back to null + if (_entry.Material == animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) + { + animatedModel.SetMaterial(entryIndex, null); + } + _material = animatedModel.GetMaterial(entryIndex); + + var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => + { + _material = value as MaterialBase; + }); + var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; + materialEditor.Values.SetDefaultValue(animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material); + materialEditor.RefreshDefaultValue(); + materialEditor.Picker.SelectedItemChanged += () => + { + var material = materialEditor.Picker.SelectedAsset as MaterialBase; + if (!material) + { + animatedModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); + materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; + } + else if (material == animatedModel.SkinnedModel.MaterialSlots[entryIndex]) + { + animatedModel.SetMaterial(entryIndex, null); + } + else + { + animatedModel.SetMaterial(entryIndex, material); + } + }; + } + base.Initialize(group); } + /// + protected override void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item) + { + // Skip material member as it is overridden + if (item.Info.Name == "Material") + { + return; + } + base.SpawnProperty(itemLayout, itemValues, item); + } + /// public override void Refresh() { diff --git a/Source/Engine/Graphics/GPUDevice.h b/Source/Engine/Graphics/GPUDevice.h index 4dbcf6d06..422e554a0 100644 --- a/Source/Engine/Graphics/GPUDevice.h +++ b/Source/Engine/Graphics/GPUDevice.h @@ -238,7 +238,7 @@ public: /// /// Gets the default material. /// - MaterialBase* GetDefaultMaterial() const; + API_PROPERTY() MaterialBase* GetDefaultMaterial() const; /// /// Gets the default material (Deformable domain). From c8edaf5d6eabd0a99a35273652e0b810ef6bd42f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Jul 2023 10:10:37 -0500 Subject: [PATCH 05/52] Fix bug with not using slot material --- .../Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index 6ac96fcea..a605b5464 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -50,7 +50,7 @@ namespace FlaxEditor.CustomEditors.Editors staticModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; } - else if (material == staticModel.Model.MaterialSlots[entryIndex]) + else if (material == staticModel.Model.MaterialSlots[entryIndex].Material) { staticModel.SetMaterial(entryIndex, null); } @@ -84,7 +84,7 @@ namespace FlaxEditor.CustomEditors.Editors animatedModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; } - else if (material == animatedModel.SkinnedModel.MaterialSlots[entryIndex]) + else if (material == animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) { animatedModel.SetMaterial(entryIndex, null); } @@ -112,6 +112,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Refresh() { + Debug.Log("Hit"); if (_updateName && _group != null && ParentEditor?.ParentEditor != null && From 02219beac97507d1d7e578d0715aa7b48e98b908 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Jul 2023 10:27:59 -0500 Subject: [PATCH 06/52] Handle edge case --- .../Editors/ModelInstanceEntryEditor.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index a605b5464..d33efe551 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -40,7 +40,7 @@ namespace FlaxEditor.CustomEditors.Editors var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; - materialEditor.Values.SetDefaultValue(staticModel.Model.MaterialSlots[entryIndex].Material); + materialEditor.Values.SetDefaultValue((staticModel.Model.MaterialSlots[entryIndex].Material) ? staticModel.Model.MaterialSlots[entryIndex].Material : GPUDevice.Instance.DefaultMaterial); materialEditor.RefreshDefaultValue(); materialEditor.Picker.SelectedItemChanged += () => { @@ -54,6 +54,10 @@ namespace FlaxEditor.CustomEditors.Editors { staticModel.SetMaterial(entryIndex, null); } + else if (material == GPUDevice.Instance.DefaultMaterial && !staticModel.Model.MaterialSlots[entryIndex].Material) + { + staticModel.SetMaterial(entryIndex, null); + } else { staticModel.SetMaterial(entryIndex, material); @@ -69,12 +73,9 @@ namespace FlaxEditor.CustomEditors.Editors } _material = animatedModel.GetMaterial(entryIndex); - var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => - { - _material = value as MaterialBase; - }); + var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; - materialEditor.Values.SetDefaultValue(animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material); + materialEditor.Values.SetDefaultValue((animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) ? animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material : GPUDevice.Instance.DefaultMaterial); materialEditor.RefreshDefaultValue(); materialEditor.Picker.SelectedItemChanged += () => { @@ -88,6 +89,10 @@ namespace FlaxEditor.CustomEditors.Editors { animatedModel.SetMaterial(entryIndex, null); } + else if (material == GPUDevice.Instance.DefaultMaterial && !animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) + { + animatedModel.SetMaterial(entryIndex, null); + } else { animatedModel.SetMaterial(entryIndex, material); From c89421438b56ae193cebfca8fec44b90c8dd9d33 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Jul 2023 10:42:41 -0500 Subject: [PATCH 07/52] Code cleanup --- .../Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index d33efe551..b7989cae3 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -108,16 +108,13 @@ namespace FlaxEditor.CustomEditors.Editors { // Skip material member as it is overridden if (item.Info.Name == "Material") - { return; - } base.SpawnProperty(itemLayout, itemValues, item); } /// public override void Refresh() { - Debug.Log("Hit"); if (_updateName && _group != null && ParentEditor?.ParentEditor != null && From 868db7c84890c15e8be07663432c913ebb301ca8 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Jul 2023 13:52:10 -0500 Subject: [PATCH 08/52] Add functionality to combine similar logs into a log with a count. --- Source/Editor/Windows/DebugLogWindow.cs | 34 ++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index b02f63f87..81dcaae92 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -80,6 +80,7 @@ namespace FlaxEditor.Windows public LogGroup Group; public LogEntryDescription Desc; public SpriteHandle Icon; + public int LogCount = 1; public LogEntry(DebugLogWindow window, ref LogEntryDescription desc) : base(0, 0, 120, DefaultHeight) @@ -137,7 +138,12 @@ namespace FlaxEditor.Windows // Title var textRect = new Rectangle(38, 2, clientRect.Width - 40, clientRect.Height - 10); Render2D.PushClip(ref clientRect); - Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground); + string countText = string.Empty; + if (LogCount > 1) + { + countText = $" ({LogCount})"; + } + Render2D.DrawText(style.FontMedium, Desc.Title + countText, textRect, style.Foreground); Render2D.PopClip(); } @@ -289,6 +295,7 @@ namespace FlaxEditor.Windows private readonly List _pendingEntries = new List(32); private readonly ToolStripButton _clearOnPlayButton; + private readonly ToolStripButton _collapseLogsButton; private readonly ToolStripButton _pauseOnErrorButton; private readonly ToolStripButton[] _groupButtons = new ToolStripButton[3]; @@ -316,6 +323,7 @@ namespace FlaxEditor.Windows }; toolstrip.AddButton("Clear", Clear).LinkTooltip("Clears all log entries"); _clearOnPlayButton = (ToolStripButton)toolstrip.AddButton("Clear on Play").SetAutoCheck(true).SetChecked(true).LinkTooltip("Clears all log entries on enter playmode"); + _collapseLogsButton = (ToolStripButton)toolstrip.AddButton("Collapse").SetAutoCheck(true).SetChecked(true).LinkTooltip("Collapses similar logs."); _pauseOnErrorButton = (ToolStripButton)toolstrip.AddButton("Pause on Error").SetAutoCheck(true).LinkTooltip("Performs auto pause on error"); toolstrip.AddSeparator(); _groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () => UpdateLogTypeVisibility(LogGroup.Error, _groupButtons[0].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides error messages"); @@ -612,6 +620,30 @@ namespace FlaxEditor.Windows var top = _entriesPanel.Children.Count != 0 ? _entriesPanel.Children[_entriesPanel.Children.Count - 1].Bottom + spacing : margin.Top; for (int i = 0; i < _pendingEntries.Count; i++) { + if (_collapseLogsButton.Checked) + { + bool logExists = false; + foreach (var child in _entriesPanel.Children) + { + if (child is LogEntry entry) + { + var pendingEntry = _pendingEntries[i]; + if (string.Equals(entry.Desc.Title, pendingEntry.Desc.Title) && + string.Equals(entry.Desc.LocationFile, pendingEntry.Desc.LocationFile) && + entry.Desc.Level == pendingEntry.Desc.Level && + string.Equals(entry.Desc.Description, pendingEntry.Desc.Description) && + entry.Desc.LocationLine == pendingEntry.Desc.LocationLine) + { + entry.LogCount += 1; + newEntry = entry; + logExists = true; + break; + } + } + } + if (logExists) + continue; + } newEntry = _pendingEntries[i]; newEntry.Visible = _groupButtons[(int)newEntry.Group].Checked; anyVisible |= newEntry.Visible; From 6d48fce76320a46ecdc5239493ba6dc0ff649244 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 15 Jul 2023 14:01:56 +0200 Subject: [PATCH 09/52] Fix mouse cursor setting on macOS to properly handle screen scale --- Source/Engine/Platform/Mac/MacPlatform.cpp | 7 ++++--- Source/Engine/Platform/Mac/MacWindow.cpp | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp index 04f928bba..15b526f02 100644 --- a/Source/Engine/Platform/Mac/MacPlatform.cpp +++ b/Source/Engine/Platform/Mac/MacPlatform.cpp @@ -301,15 +301,16 @@ Float2 MacPlatform::GetMousePosition() CGEventRef event = CGEventCreate(nullptr); CGPoint cursor = CGEventGetLocation(event); CFRelease(event); - return Float2((float)cursor.x, (float)cursor.y); + return Float2((float)cursor.x, (float)cursor.y) * MacPlatform::ScreenScale; } void MacPlatform::SetMousePosition(const Float2& pos) { CGPoint cursor; - cursor.x = (CGFloat)pos.X; - cursor.y = (CGFloat)pos.Y; + cursor.x = (CGFloat)(pos.X / MacPlatform::ScreenScale); + cursor.y = (CGFloat)(pos.Y / MacPlatform::ScreenScale); CGWarpMouseCursorPosition(cursor); + CGAssociateMouseAndMouseCursorPosition(true); } Float2 MacPlatform::GetDesktopSize() diff --git a/Source/Engine/Platform/Mac/MacWindow.cpp b/Source/Engine/Platform/Mac/MacWindow.cpp index 90bb07279..c55f36c7c 100644 --- a/Source/Engine/Platform/Mac/MacWindow.cpp +++ b/Source/Engine/Platform/Mac/MacWindow.cpp @@ -643,7 +643,6 @@ MacWindow::MacWindow(const CreateWindowSettings& settings) // TODO: impl StartPosition for MacWindow // TODO: impl Fullscreen for MacWindow // TODO: impl ShowInTaskbar for MacWindow - // TODO: impl AllowInput for MacWindow // TODO: impl IsTopmost for MacWindow } From 6fc168bdf13b9c86c407635618e279de12964619 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 15 Jul 2023 14:42:54 +0200 Subject: [PATCH 10/52] Add macOS message box with buttons --- Source/Engine/Platform/Mac/MacPlatform.cpp | 80 +++++++++++++++++++--- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp index 15b526f02..46b978e1d 100644 --- a/Source/Engine/Platform/Mac/MacPlatform.cpp +++ b/Source/Engine/Platform/Mac/MacPlatform.cpp @@ -56,36 +56,94 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri { if (CommandLine::Options.Headless) return DialogResult::None; - CFStringRef textRef = AppleUtils::ToString(text); - CFStringRef captionRef = AppleUtils::ToString(caption); - CFOptionFlags flags = 0; + NSAlert* alert = [[NSAlert alloc] init]; + ASSERT(alert); switch (buttons) { case MessageBoxButtons::AbortRetryIgnore: + [alert addButtonWithTitle:@"Abort"]; + [alert addButtonWithTitle:@"Retry"]; + [alert addButtonWithTitle:@"Ignore"]; + break; + case MessageBoxButtons::OK: + [alert addButtonWithTitle:@"OK"]; + break; case MessageBoxButtons::OKCancel: + [alert addButtonWithTitle:@"OK"]; + [alert addButtonWithTitle:@"Cancel"]; + break; case MessageBoxButtons::RetryCancel: + [alert addButtonWithTitle:@"Retry"]; + [alert addButtonWithTitle:@"Cancel"]; + break; case MessageBoxButtons::YesNo: + [alert addButtonWithTitle:@"Yes"]; + [alert addButtonWithTitle:@"No"]; + break; case MessageBoxButtons::YesNoCancel: - flags |= kCFUserNotificationCancelResponse; + [alert addButtonWithTitle:@"Yes"]; + [alert addButtonWithTitle:@"No"]; + [alert addButtonWithTitle:@"Cancel"]; break; } switch (icon) { case MessageBoxIcon::Information: - flags |= kCFUserNotificationNoteAlertLevel; + [alert setAlertStyle:NSAlertStyleCritical]; break; case MessageBoxIcon::Error: case MessageBoxIcon::Stop: - flags |= kCFUserNotificationStopAlertLevel; + [alert setAlertStyle:NSAlertStyleInformational]; break; case MessageBoxIcon::Warning: - flags |= kCFUserNotificationCautionAlertLevel; + [alert setAlertStyle:NSAlertStyleWarning]; break; } - SInt32 result = CFUserNotificationDisplayNotice(0, flags, nullptr, nullptr, nullptr, captionRef, textRef, nullptr); - CFRelease(captionRef); - CFRelease(textRef); - return DialogResult::OK; + [alert setMessageText:(NSString*)AppleUtils::ToString(caption)]; + [alert setInformativeText:(NSString*)AppleUtils::ToString(text)]; + NSInteger button = [alert runModal]; + DialogResult result = DialogResult::OK; + switch (buttons) + { + case MessageBoxButtons::AbortRetryIgnore: + if (button == NSAlertFirstButtonReturn) + result = DialogResult::Abort; + else if (button == NSAlertSecondButtonReturn) + result = DialogResult::Retry; + else + result = DialogResult::Ignore; + break; + case MessageBoxButtons::OK: + result = DialogResult::OK; + break; + case MessageBoxButtons::OKCancel: + if (button == NSAlertFirstButtonReturn) + result = DialogResult::OK; + else + result = DialogResult::Cancel; + break; + case MessageBoxButtons::RetryCancel: + if (button == NSAlertFirstButtonReturn) + result = DialogResult::Retry; + else + result = DialogResult::Cancel; + break; + case MessageBoxButtons::YesNo: + if (button == NSAlertFirstButtonReturn) + result = DialogResult::Yes; + else + result = DialogResult::No; + break; + case MessageBoxButtons::YesNoCancel: + if (button == NSAlertFirstButtonReturn) + result = DialogResult::Yes; + else if (button == NSAlertSecondButtonReturn) + result = DialogResult::No; + else + result = DialogResult::Cancel; + break; + } + return result; } Float2 AppleUtils::PosToCoca(const Float2& pos) From 6853aa6e812d607a2312c6aa67fa5995888397b9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 15 Jul 2023 15:00:55 +0200 Subject: [PATCH 11/52] Add control/command/option keys handling on macOS --- Source/Engine/Platform/Mac/MacWindow.cpp | 29 ++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Mac/MacWindow.cpp b/Source/Engine/Platform/Mac/MacWindow.cpp index c55f36c7c..a52c11324 100644 --- a/Source/Engine/Platform/Mac/MacWindow.cpp +++ b/Source/Engine/Platform/Mac/MacWindow.cpp @@ -75,8 +75,8 @@ KeyboardKeys GetKey(NSEvent* event) case 0x33: return KeyboardKeys::Delete; //case 0x34: case 0x35: return KeyboardKeys::Escape; - //case 0x36: - //case 0x37: Command + case 0x36: return KeyboardKeys::Control; // Command (right) + case 0x37: return KeyboardKeys::Control; // Command (left) case 0x38: return KeyboardKeys::Shift; case 0x39: return KeyboardKeys::Capital; case 0x3A: return KeyboardKeys::Alt; @@ -411,6 +411,31 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r) Input::Keyboard->OnKeyUp(key); } +- (void)flagsChanged:(NSEvent*)event +{ + int32 modMask; + int32 keyCode = [event keyCode]; + if (keyCode == 0x36 || keyCode == 0x37) + modMask = NSEventModifierFlagCommand; + else if (keyCode == 0x38 || keyCode == 0x3c) + modMask = NSEventModifierFlagShift; + else if (keyCode == 0x3a || keyCode == 0x3d) + modMask = NSEventModifierFlagOption; + else if (keyCode == 0x3b || keyCode == 0x3e) + modMask = NSEventModifierFlagControl; + else + return; + KeyboardKeys key = GetKey(event); + if (key != KeyboardKeys::None) + { + int32 modifierFlags = [event modifierFlags]; + if ((modifierFlags & modMask) == modMask) + Input::Keyboard->OnKeyDown(key); + else + Input::Keyboard->OnKeyUp(key); + } +} + - (void)scrollWheel:(NSEvent*)event { Float2 mousePos = GetMousePosition(Window, event); From 011162744c7f4769f03ccebc77e143b0ca5c8070 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 15 Jul 2023 15:07:52 +0200 Subject: [PATCH 12/52] Fix various keyboard handling on macOS --- Source/Engine/Platform/Mac/MacWindow.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Platform/Mac/MacWindow.cpp b/Source/Engine/Platform/Mac/MacWindow.cpp index a52c11324..aeee93e37 100644 --- a/Source/Engine/Platform/Mac/MacWindow.cpp +++ b/Source/Engine/Platform/Mac/MacWindow.cpp @@ -72,7 +72,7 @@ KeyboardKeys GetKey(NSEvent* event) case 0x30: return KeyboardKeys::Tab; case 0x31: return KeyboardKeys::Spacebar; case 0x32: return KeyboardKeys::BackQuote; - case 0x33: return KeyboardKeys::Delete; + case 0x33: return KeyboardKeys::Backspace; //case 0x34: case 0x35: return KeyboardKeys::Escape; case 0x36: return KeyboardKeys::Control; // Command (right) @@ -378,7 +378,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r) { KeyboardKeys key = GetKey(event); if (key != KeyboardKeys::None) - Input::Keyboard->OnKeyDown(key); + Input::Keyboard->OnKeyDown(key, Window); // Send a text input event switch (key) @@ -400,7 +400,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r) if (length >= 16) length = 15; [text getCharacters:buffer range:NSMakeRange(0, length)]; - Input::Keyboard->OnCharInput((Char)buffer[0]); + Input::Keyboard->OnCharInput((Char)buffer[0], Window); } } @@ -408,7 +408,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r) { KeyboardKeys key = GetKey(event); if (key != KeyboardKeys::None) - Input::Keyboard->OnKeyUp(key); + Input::Keyboard->OnKeyUp(key, Window); } - (void)flagsChanged:(NSEvent*)event @@ -430,9 +430,9 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r) { int32 modifierFlags = [event modifierFlags]; if ((modifierFlags & modMask) == modMask) - Input::Keyboard->OnKeyDown(key); + Input::Keyboard->OnKeyDown(key, Window); else - Input::Keyboard->OnKeyUp(key); + Input::Keyboard->OnKeyUp(key, Window); } } From 44518e88d567f3e3ea590d029c5ca6392fc6cb65 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 09:48:43 +0200 Subject: [PATCH 13/52] Fix crash when using Vector3 soft casting in Visual Scripts --- Source/Engine/Core/Types/Variant.cpp | 117 +++++++++++++++++++++++++ Source/Engine/Core/Types/Variant.h | 3 + Source/Engine/Scripting/Scripting.cpp | 18 +++- Source/Engine/Visject/VisjectGraph.cpp | 4 + Source/Engine/Visject/VisjectGraph.h | 1 + 5 files changed, 142 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 8232d415f..973e494f1 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -2796,6 +2796,122 @@ String Variant::ToString() const } } +void Variant::Inline() +{ + VariantType::Types type = VariantType::Null; + byte data[sizeof(Matrix)]; + if (Type.Type == VariantType::Structure && AsBlob.Data && AsBlob.Length <= sizeof(Matrix)) + { + for (int32 i = 2; i < VariantType::MAX; i++) + { + if (StringUtils::Compare(Type.TypeName, InBuiltTypesTypeNames[i]) == 0) + { + type = (VariantType::Types)i; + break; + } + } + if (type == VariantType::Null) + { + // Aliases + if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector2") == 0) + type = VariantType::Types::Vector2; + else if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector3") == 0) + type = VariantType::Types::Vector3; + else if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector4") == 0) + type = VariantType::Types::Vector4; + } + if (type != VariantType::Null) + Platform::MemoryCopy(data, AsBlob.Data, AsBlob.Length); + } + if (type != VariantType::Null) + { + switch (type) + { + case VariantType::Bool: + *this = *(bool*)data; + break; + case VariantType::Int: + *this = *(int32*)data; + break; + case VariantType::Uint: + *this = *(uint32*)data; + break; + case VariantType::Int64: + *this = *(int64*)data; + break; + case VariantType::Uint64: + *this = *(uint64*)data; + break; + case VariantType::Float: + *this = *(float*)data; + break; + case VariantType::Double: + *this = *(double*)data; + break; + case VariantType::Float2: + *this = *(Float2*)data; + break; + case VariantType::Float3: + *this = *(Float3*)data; + break; + case VariantType::Float4: + *this = *(Float4*)data; + break; + case VariantType::Color: + *this = *(Color*)data; + break; + case VariantType::Guid: + *this = *(Guid*)data; + break; + case VariantType::BoundingBox: + *this = Variant(*(BoundingBox*)data); + break; + case VariantType::BoundingSphere: + *this = *(BoundingSphere*)data; + break; + case VariantType::Quaternion: + *this = *(Quaternion*)data; + break; + case VariantType::Transform: + *this = Variant(*(Transform*)data); + break; + case VariantType::Rectangle: + *this = *(Rectangle*)data; + break; + case VariantType::Ray: + *this = Variant(*(Ray*)data); + break; + case VariantType::Matrix: + *this = Variant(*(Matrix*)data); + break; + case VariantType::Int2: + *this = *(Int2*)data; + break; + case VariantType::Int3: + *this = *(Int3*)data; + break; + case VariantType::Int4: + *this = *(Int4*)data; + break; + case VariantType::Int16: + *this = *(int16*)data; + break; + case VariantType::Uint16: + *this = *(uint16*)data; + break; + case VariantType::Double2: + *this = *(Double2*)data; + break; + case VariantType::Double3: + *this = *(Double3*)data; + break; + case VariantType::Double4: + *this = *(Double4*)data; + break; + } + } +} + bool Variant::CanCast(const Variant& v, const VariantType& to) { if (v.Type == to) @@ -3682,6 +3798,7 @@ void Variant::AllocStructure() const ScriptingType& type = typeHandle.GetType(); AsBlob.Length = type.Size; AsBlob.Data = Allocator::Allocate(AsBlob.Length); + Platform::MemoryClear(AsBlob.Data, AsBlob.Length); type.Struct.Ctor(AsBlob.Data); } else if (typeName == "System.Int16" || typeName == "System.UInt16") diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h index 7745307c3..9776c8e0b 100644 --- a/Source/Engine/Core/Types/Variant.h +++ b/Source/Engine/Core/Types/Variant.h @@ -359,6 +359,9 @@ public: void SetAsset(Asset* asset); String ToString() const; + // Inlines potential value type into in-built format (eg. Vector3 stored as Structure, or String stored as ManagedObject). + void Inline(); + FORCE_INLINE Variant Cast(const VariantType& to) const { return Cast(*this, to); diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index e2d709653..bbfa802f1 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -476,12 +476,28 @@ bool Scripting::Load() // Load FlaxEngine const String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll"); - if (((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->Load(flaxEnginePath)) + auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine(); + if (flaxEngineModule->Assembly->Load(flaxEnginePath)) { LOG(Error, "Failed to load FlaxEngine C# assembly."); return true; } + // Insert type aliases for vector types that don't exist in C++ but are just typedef (properly redirect them to actual types) + // TODO: add support for automatic typedef aliases setup for scripting module to properly lookup type from the alias typename +#if USE_LARGE_WORLDS + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double2"]; + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double3"]; + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double4"]; +#else + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float2"]; + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float3"]; + flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float4"]; +#endif + flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector2")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"]; + flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector3")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"]; + flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector4")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"]; + #if USE_EDITOR // Skip loading game modules in Editor on startup - Editor loads them later during splash screen (eg. after first compilation) static bool SkipFirstLoad = true; diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index 37154e09f..ca4495f67 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -675,6 +675,10 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value) } } } + + // For in-built structures try to convert it into internal format for better comparability with the scripting + value.Inline(); + break; } // Unpack Structure diff --git a/Source/Engine/Visject/VisjectGraph.h b/Source/Engine/Visject/VisjectGraph.h index 238823afd..0d841f3d4 100644 --- a/Source/Engine/Visject/VisjectGraph.h +++ b/Source/Engine/Visject/VisjectGraph.h @@ -246,6 +246,7 @@ public: void ProcessGroupCollections(Box* box, Node* node, Value& value); protected: + void InlineVariantStruct(Variant& v); virtual Value eatBox(Node* caller, Box* box) = 0; virtual Graph* GetCurrentGraph() const = 0; From 488958ce44b2d7cff21b7edaa9ddbb0e3dd7d3da Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 10:16:11 +0200 Subject: [PATCH 14/52] Fix `DrawSceneDepth` to properly draw scene objects when custom actors list is empty #1253 --- Source/Editor/Gizmo/SelectionOutline.cs | 2 ++ Source/Engine/Renderer/Renderer.cs | 20 +++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Gizmo/SelectionOutline.cs b/Source/Editor/Gizmo/SelectionOutline.cs index ccc4bdd27..1374c7247 100644 --- a/Source/Editor/Gizmo/SelectionOutline.cs +++ b/Source/Editor/Gizmo/SelectionOutline.cs @@ -178,6 +178,8 @@ namespace FlaxEditor.Gizmo if (selection[i] is ActorNode actorNode && actorNode.Actor != null) CollectActors(actorNode.Actor); } + if (_actors.Count == 0) + return; // Render selected objects depth Renderer.DrawSceneDepth(context, task, customDepth, _actors); diff --git a/Source/Engine/Renderer/Renderer.cs b/Source/Engine/Renderer/Renderer.cs index bfb81a84c..71dca424b 100644 --- a/Source/Engine/Renderer/Renderer.cs +++ b/Source/Engine/Renderer/Renderer.cs @@ -17,10 +17,13 @@ namespace FlaxEngine [Unmanaged] public static void DrawSceneDepth(GPUContext context, SceneRenderTask task, GPUTexture output, List customActors) { - if (customActors.Count == 0) - return; - var temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME - var tempCount = temp.Length; + Actor[] temp = null; + int tempCount = 0; + if (customActors != null && customActors.Count != 0) + { + temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME + tempCount = temp.Length; + } Internal_DrawSceneDepth(FlaxEngine.Object.GetUnmanagedPtr(context), FlaxEngine.Object.GetUnmanagedPtr(task), FlaxEngine.Object.GetUnmanagedPtr(output), temp, ref tempCount); } @@ -32,7 +35,14 @@ namespace FlaxEngine [Unmanaged] public static void DrawActors(ref RenderContext renderContext, List customActors) { - DrawActors(ref renderContext, Utils.ExtractArrayFromList(customActors)); + Actor[] temp = null; + int tempCount = 0; + if (customActors != null && customActors.Count != 0) + { + temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME + tempCount = temp.Length; + } + Internal_DrawActors(ref renderContext, temp, ref tempCount); } } } From 0f613abfb96a7dcc3e29422eb06ed0dd94dd7031 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 10:54:21 +0200 Subject: [PATCH 15/52] Add `ToSpan` from 24c03c0e4b8bbbf4fb640d7b53b84bc2eaebcd57 --- Source/Engine/Core/Types/Span.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/Core/Types/Span.h b/Source/Engine/Core/Types/Span.h index e7bea5fbd..e0518ec77 100644 --- a/Source/Engine/Core/Types/Span.h +++ b/Source/Engine/Core/Types/Span.h @@ -115,6 +115,12 @@ inline Span ToSpan(const T* ptr, int32 length) return Span(ptr, length); } +template +inline Span ToSpan(const Array& data) +{ + return Span((U*)data.Get(), data.Count()); +} + template inline bool SpanContains(const Span span, const T& value) { From 338499536fe4e3836568969e5493e88d92f4f732 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 10:55:00 +0200 Subject: [PATCH 16/52] Add `ModelInstanceActor::GetMaterialSlots` --- Source/Engine/Content/Assets/MaterialBase.h | 1 - Source/Engine/Level/Actors/AnimatedModel.cpp | 8 ++++++++ Source/Engine/Level/Actors/AnimatedModel.h | 1 + Source/Engine/Level/Actors/ModelInstanceActor.h | 5 +++++ Source/Engine/Level/Actors/SplineModel.cpp | 8 ++++++++ Source/Engine/Level/Actors/SplineModel.h | 1 + Source/Engine/Level/Actors/StaticModel.cpp | 8 ++++++++ Source/Engine/Level/Actors/StaticModel.h | 1 + 8 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Content/Assets/MaterialBase.h b/Source/Engine/Content/Assets/MaterialBase.h index bca70b6da..070347348 100644 --- a/Source/Engine/Content/Assets/MaterialBase.h +++ b/Source/Engine/Content/Assets/MaterialBase.h @@ -27,7 +27,6 @@ public: /// /// Returns true if material is an material instance. /// - /// True if it's a material instance, otherwise false. virtual bool IsMaterialInstance() const = 0; public: diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 373278885..dff527924 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -898,6 +898,14 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m DrawModes |= DrawPass::GlobalSurfaceAtlas; } +const Span AnimatedModel::GetMaterialSlots() const +{ + const auto model = SkinnedModel.Get(); + if (model && !model->WaitForLoaded()) + return ToSpan(model->MaterialSlots); + return Span(); +} + MaterialBase* AnimatedModel::GetMaterial(int32 entryIndex) { if (SkinnedModel) diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 2693f573d..763629e93 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -373,6 +373,7 @@ public: bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + const Span GetMaterialSlots() const override; MaterialBase* GetMaterial(int32 entryIndex) override; bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override; bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override; diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.h b/Source/Engine/Level/Actors/ModelInstanceActor.h index 1dfd5c0e0..3faad35b3 100644 --- a/Source/Engine/Level/Actors/ModelInstanceActor.h +++ b/Source/Engine/Level/Actors/ModelInstanceActor.h @@ -35,6 +35,11 @@ public: /// API_PROPERTY() void SetEntries(const Array& value); + /// + /// Gets the material slots array set on the asset (eg. model or skinned model asset). + /// + API_PROPERTY(Sealed) virtual const Span GetMaterialSlots() const = 0; + /// /// Gets the material used to draw the meshes which are assigned to that slot (set in Entries or model's default). /// diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 843bf6076..6ca841b41 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -341,6 +341,14 @@ void SplineModel::OnParentChanged() OnSplineUpdated(); } +const Span SplineModel::GetMaterialSlots() const +{ + const auto model = Model.Get(); + if (model && !model->WaitForLoaded()) + return ToSpan(model->MaterialSlots); + return Span(); +} + MaterialBase* SplineModel::GetMaterial(int32 entryIndex) { if (Model) diff --git a/Source/Engine/Level/Actors/SplineModel.h b/Source/Engine/Level/Actors/SplineModel.h index 87f39699c..2a3d45a27 100644 --- a/Source/Engine/Level/Actors/SplineModel.h +++ b/Source/Engine/Level/Actors/SplineModel.h @@ -115,6 +115,7 @@ public: void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; void OnParentChanged() override; + const Span GetMaterialSlots() const override; MaterialBase* GetMaterial(int32 entryIndex) override; protected: diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index 778f24d65..93eef3056 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -535,6 +535,14 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod } } +const Span StaticModel::GetMaterialSlots() const +{ + const auto model = Model.Get(); + if (model && !model->WaitForLoaded()) + return ToSpan(model->MaterialSlots); + return Span(); +} + MaterialBase* StaticModel::GetMaterial(int32 entryIndex) { if (Model) diff --git a/Source/Engine/Level/Actors/StaticModel.h b/Source/Engine/Level/Actors/StaticModel.h index 06fbdd874..600f174c9 100644 --- a/Source/Engine/Level/Actors/StaticModel.h +++ b/Source/Engine/Level/Actors/StaticModel.h @@ -166,6 +166,7 @@ public: bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + const Span GetMaterialSlots() const override; MaterialBase* GetMaterial(int32 entryIndex) override; bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override; bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override; From 8f8c0e4b819cc70de89ad20a6f04977995a2d765 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 10:55:15 +0200 Subject: [PATCH 17/52] Minor fixes for #1247 --- .../CustomEditors/Editors/AssetRefEditor.cs | 1 + .../Editors/ModelInstanceEntryEditor.cs | 111 ++++++------------ 2 files changed, 36 insertions(+), 76 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index b83b44d59..88f916ae4 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -37,6 +37,7 @@ namespace FlaxEditor.CustomEditors.Editors /// The asset picker used to get a reference to an asset. /// public AssetPicker Picker; + private ScriptType _valueType; /// diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index b7989cae3..1f04115de 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -16,7 +16,6 @@ namespace FlaxEditor.CustomEditors.Editors private GroupElement _group; private bool _updateName; private MaterialBase _material; - private ModelInstanceEntry _entry; /// public override void Initialize(LayoutElementsContainer layout) @@ -24,82 +23,59 @@ namespace FlaxEditor.CustomEditors.Editors _updateName = true; var group = layout.Group("Entry"); _group = group; - - _entry = (ModelInstanceEntry)Values[0]; + + if (ParentEditor == null) + return; + var entry = (ModelInstanceEntry)Values[0]; var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this); - var materiaLabel = new PropertyNameLabel("Material"); - materiaLabel.TooltipText = "The mesh surface material used for the rendering."; - if (ParentEditor.ParentEditor.Values[0] is StaticModel staticModel) + var materialLabel = new PropertyNameLabel("Material"); + materialLabel.TooltipText = "The mesh surface material used for the rendering."; + if (ParentEditor.ParentEditor?.Values[0] is ModelInstanceActor modelInstance) { // Ensure that entry with default material set is set back to null - if (_entry.Material == staticModel.Model.MaterialSlots[entryIndex].Material) + var defaultValue = GPUDevice.Instance.DefaultMaterial; { - staticModel.SetMaterial(entryIndex, null); + var slots = modelInstance.MaterialSlots; + if (entry.Material == slots[entryIndex].Material) + { + modelInstance.SetMaterial(entryIndex, null); + } + _material = modelInstance.GetMaterial(entryIndex); + if (slots[entryIndex].Material) + { + defaultValue = slots[entryIndex].Material; + } } - _material = staticModel.GetMaterial(entryIndex); var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); - var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; - materialEditor.Values.SetDefaultValue((staticModel.Model.MaterialSlots[entryIndex].Material) ? staticModel.Model.MaterialSlots[entryIndex].Material : GPUDevice.Instance.DefaultMaterial); + var materialEditor = (AssetRefEditor)_group.Property(materialLabel, matContainer); + materialEditor.Values.SetDefaultValue(defaultValue); materialEditor.RefreshDefaultValue(); materialEditor.Picker.SelectedItemChanged += () => { + var slots = modelInstance.MaterialSlots; var material = materialEditor.Picker.SelectedAsset as MaterialBase; + var defaultMaterial = GPUDevice.Instance.DefaultMaterial; if (!material) { - staticModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); - materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; + modelInstance.SetMaterial(entryIndex, defaultMaterial); + materialEditor.Picker.SelectedAsset = defaultMaterial; } - else if (material == staticModel.Model.MaterialSlots[entryIndex].Material) + else if (material == slots[entryIndex].Material) { - staticModel.SetMaterial(entryIndex, null); + modelInstance.SetMaterial(entryIndex, null); } - else if (material == GPUDevice.Instance.DefaultMaterial && !staticModel.Model.MaterialSlots[entryIndex].Material) + else if (material == defaultMaterial && !slots[entryIndex].Material) { - staticModel.SetMaterial(entryIndex, null); + modelInstance.SetMaterial(entryIndex, null); } else { - staticModel.SetMaterial(entryIndex, material); + modelInstance.SetMaterial(entryIndex, material); } }; } - else if (ParentEditor.ParentEditor.Values[0] is AnimatedModel animatedModel) - { - // Ensure that entry with default material set is set back to null - if (_entry.Material == animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) - { - animatedModel.SetMaterial(entryIndex, null); - } - _material = animatedModel.GetMaterial(entryIndex); - - var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); - var materialEditor = (_group.Property(materiaLabel, matContainer)) as AssetRefEditor; - materialEditor.Values.SetDefaultValue((animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) ? animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material : GPUDevice.Instance.DefaultMaterial); - materialEditor.RefreshDefaultValue(); - materialEditor.Picker.SelectedItemChanged += () => - { - var material = materialEditor.Picker.SelectedAsset as MaterialBase; - if (!material) - { - animatedModel.SetMaterial(entryIndex, GPUDevice.Instance.DefaultMaterial); - materialEditor.Picker.SelectedAsset = GPUDevice.Instance.DefaultMaterial; - } - else if (material == animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) - { - animatedModel.SetMaterial(entryIndex, null); - } - else if (material == GPUDevice.Instance.DefaultMaterial && !animatedModel.SkinnedModel.MaterialSlots[entryIndex].Material) - { - animatedModel.SetMaterial(entryIndex, null); - } - else - { - animatedModel.SetMaterial(entryIndex, material); - } - }; - } - + base.Initialize(group); } @@ -121,30 +97,13 @@ namespace FlaxEditor.CustomEditors.Editors ParentEditor.ParentEditor.Values.Count > 0) { var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this); - if (ParentEditor.ParentEditor.Values[0] is StaticModel staticModel) + if (ParentEditor.ParentEditor.Values[0] is ModelInstanceActor modelInstance) { - var model = staticModel.Model; - if (model && model.IsLoaded) + var slots = modelInstance.MaterialSlots; + if (slots != null && slots.Length > entryIndex) { - var slots = model.MaterialSlots; - if (slots != null && slots.Length > entryIndex) - { - _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; - _updateName = false; - } - } - } - else if (ParentEditor.ParentEditor.Values[0] is AnimatedModel animatedModel) - { - var model = animatedModel.SkinnedModel; - if (model && model.IsLoaded) - { - var slots = model.MaterialSlots; - if (slots != null && slots.Length > entryIndex) - { - _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; - _updateName = false; - } + _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; + _updateName = false; } } } From 60c0995bc36a14f7d30f79ebc118f1a93e555122 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 11:46:15 +0200 Subject: [PATCH 18/52] Add undo for #1247 --- .../CustomEditors/Editors/AssetRefEditor.cs | 5 + .../Editors/ModelInstanceEntryEditor.cs | 113 ++++++++++++------ 2 files changed, 79 insertions(+), 39 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index 88f916ae4..e89c80372 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -38,6 +38,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public AssetPicker Picker; + private bool _isRefreshing; private ScriptType _valueType; /// @@ -89,6 +90,8 @@ namespace FlaxEditor.CustomEditors.Editors private void OnSelectedItemChanged() { + if (_isRefreshing) + return; if (typeof(AssetItem).IsAssignableFrom(_valueType.Type)) SetValue(Picker.SelectedItem); else if (_valueType.Type == typeof(Guid)) @@ -108,6 +111,7 @@ namespace FlaxEditor.CustomEditors.Editors if (!HasDifferentValues) { + _isRefreshing = true; if (Values[0] is AssetItem assetItem) Picker.SelectedItem = assetItem; else if (Values[0] is Guid guid) @@ -118,6 +122,7 @@ namespace FlaxEditor.CustomEditors.Editors Picker.SelectedPath = path; else Picker.SelectedAsset = Values[0] as Asset; + _isRefreshing = false; } } } diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index 1f04115de..277f9ecb0 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -15,7 +15,11 @@ namespace FlaxEditor.CustomEditors.Editors { private GroupElement _group; private bool _updateName; + private int _entryIndex; + private bool _isRefreshing; private MaterialBase _material; + private ModelInstanceActor _modelInstance; + private AssetRefEditor _materialEditor; /// public override void Initialize(LayoutElementsContainer layout) @@ -32,58 +36,75 @@ namespace FlaxEditor.CustomEditors.Editors materialLabel.TooltipText = "The mesh surface material used for the rendering."; if (ParentEditor.ParentEditor?.Values[0] is ModelInstanceActor modelInstance) { - // Ensure that entry with default material set is set back to null - var defaultValue = GPUDevice.Instance.DefaultMaterial; + _entryIndex = entryIndex; + _modelInstance = modelInstance; + var slots = modelInstance.MaterialSlots; + if (entry.Material == slots[entryIndex].Material) { - var slots = modelInstance.MaterialSlots; - if (entry.Material == slots[entryIndex].Material) - { - modelInstance.SetMaterial(entryIndex, null); - } - _material = modelInstance.GetMaterial(entryIndex); - if (slots[entryIndex].Material) - { - defaultValue = slots[entryIndex].Material; - } + // Ensure that entry with default material set is set back to null + modelInstance.SetMaterial(entryIndex, null); + } + _material = modelInstance.GetMaterial(entryIndex); + var defaultValue = GPUDevice.Instance.DefaultMaterial; + if (slots[entryIndex].Material) + { + // Use default value set on asset (eg. Model Asset) + defaultValue = slots[entryIndex].Material; } - var matContainer = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); - var materialEditor = (AssetRefEditor)_group.Property(materialLabel, matContainer); + // Create material picker + var materialValue = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); + var materialEditor = (AssetRefEditor)_group.Property(materialLabel, materialValue); materialEditor.Values.SetDefaultValue(defaultValue); materialEditor.RefreshDefaultValue(); - materialEditor.Picker.SelectedItemChanged += () => - { - var slots = modelInstance.MaterialSlots; - var material = materialEditor.Picker.SelectedAsset as MaterialBase; - var defaultMaterial = GPUDevice.Instance.DefaultMaterial; - if (!material) - { - modelInstance.SetMaterial(entryIndex, defaultMaterial); - materialEditor.Picker.SelectedAsset = defaultMaterial; - } - else if (material == slots[entryIndex].Material) - { - modelInstance.SetMaterial(entryIndex, null); - } - else if (material == defaultMaterial && !slots[entryIndex].Material) - { - modelInstance.SetMaterial(entryIndex, null); - } - else - { - modelInstance.SetMaterial(entryIndex, material); - } - }; + materialEditor.Picker.SelectedItemChanged += OnSelectedMaterialChanged; + _materialEditor = materialEditor; } base.Initialize(group); } + private void OnSelectedMaterialChanged() + { + if (_isRefreshing) + return; + _isRefreshing = true; + var slots = _modelInstance.MaterialSlots; + var material = _materialEditor.Picker.SelectedAsset as MaterialBase; + var defaultMaterial = GPUDevice.Instance.DefaultMaterial; + var value = (ModelInstanceEntry)Values[0]; + var prevMaterial = value.Material; + if (!material) + { + // Fallback to default material + _materialEditor.Picker.SelectedAsset = defaultMaterial; + value.Material = defaultMaterial; + } + else if (material == slots[_entryIndex].Material) + { + // Asset default material + value.Material = null; + } + else if (material == defaultMaterial && !slots[_entryIndex].Material) + { + // Default material while asset has no set as well + value.Material = null; + } + else + { + // Custom material + value.Material = material; + } + if (prevMaterial != value.Material) + SetValue(value); + _isRefreshing = false; + } + /// protected override void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item) { // Skip material member as it is overridden - if (item.Info.Name == "Material") + if (item.Info.Name == "Material" && _materialEditor != null) return; base.SpawnProperty(itemLayout, itemValues, item); } @@ -91,6 +112,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Refresh() { + // Update panel title to match material slot name if (_updateName && _group != null && ParentEditor?.ParentEditor != null && @@ -102,13 +124,26 @@ namespace FlaxEditor.CustomEditors.Editors var slots = modelInstance.MaterialSlots; if (slots != null && slots.Length > entryIndex) { - _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; _updateName = false; + _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; } } } + // Refresh currently selected material + _material = _modelInstance.GetMaterial(_entryIndex); + base.Refresh(); } + + /// + protected override void Deinitialize() + { + _material = null; + _modelInstance = null; + _materialEditor = null; + + base.Deinitialize(); + } } } From be079b9b67f7c5c9b63ba8189ca4ccc8ce2e2ca4 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 18 Jul 2023 09:51:21 -0500 Subject: [PATCH 19/52] Improvements to debug log count. --- Source/Editor/Windows/DebugLogWindow.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index 81dcaae92..49b03ede1 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -138,12 +138,14 @@ namespace FlaxEditor.Windows // Title var textRect = new Rectangle(38, 2, clientRect.Width - 40, clientRect.Height - 10); Render2D.PushClip(ref clientRect); - string countText = string.Empty; - if (LogCount > 1) + if (LogCount == 1) { - countText = $" ({LogCount})"; + Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground); + } + else if (LogCount > 1) + { + Render2D.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground); } - Render2D.DrawText(style.FontMedium, Desc.Title + countText, textRect, style.Foreground); Render2D.PopClip(); } @@ -628,10 +630,10 @@ namespace FlaxEditor.Windows if (child is LogEntry entry) { var pendingEntry = _pendingEntries[i]; - if (string.Equals(entry.Desc.Title, pendingEntry.Desc.Title) && - string.Equals(entry.Desc.LocationFile, pendingEntry.Desc.LocationFile) && + if (string.Equals(entry.Desc.Title, pendingEntry.Desc.Title, StringComparison.Ordinal) && + string.Equals(entry.Desc.LocationFile, pendingEntry.Desc.LocationFile, StringComparison.Ordinal) && entry.Desc.Level == pendingEntry.Desc.Level && - string.Equals(entry.Desc.Description, pendingEntry.Desc.Description) && + string.Equals(entry.Desc.Description, pendingEntry.Desc.Description, StringComparison.Ordinal) && entry.Desc.LocationLine == pendingEntry.Desc.LocationLine) { entry.LogCount += 1; From 872509df2a099b0cea4c1cd7c6d3f5827adbf6b2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 18:13:19 +0200 Subject: [PATCH 20/52] Fix incorrect `Transform Position To Screen UV` in particles graph in CPU code path --- .../Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp index 649d01d85..9ef996869 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp @@ -189,7 +189,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Va const Matrix viewProjection = context.ViewTask ? context.ViewTask->View.PrevViewProjection : Matrix::Identity; const Float3 position = (Float3)TryGetValue(node->GetBox(0), Value::Zero); Float4 projPos; - Float3::Transform(position, viewProjection); + Float3::Transform(position, viewProjection, projPos); projPos /= projPos.W; value = Float2(projPos.X * 0.5f + 0.5f, projPos.Y * 0.5f + 0.5f); break; From b2b10ce7da7f3ac47e8f5688bb2693e1a3d7a8c4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Jul 2023 18:20:11 +0200 Subject: [PATCH 21/52] Fix various core types to be trivially constructible as properly POD-type --- Source/Editor/Tools/Foliage/FoliageTools.cpp | 1 - Source/Engine/Core/Math/BoundingBox.h | 4 +--- Source/Engine/Core/Math/BoundingFrustum.h | 4 +--- Source/Engine/Core/Math/BoundingSphere.h | 4 +--- Source/Engine/Core/Math/Color.h | 4 +--- Source/Engine/Core/Math/Color32.h | 4 +--- Source/Engine/Core/Math/Half.h | 12 +++--------- Source/Engine/Core/Math/Matrix.h | 4 +--- Source/Engine/Core/Math/Matrix3x3.h | 4 +--- Source/Engine/Core/Math/Plane.h | 4 +--- Source/Engine/Core/Math/Quaternion.h | 4 +--- Source/Engine/Core/Math/Ray.h | 4 +--- Source/Engine/Core/Math/Rectangle.h | 4 +--- Source/Engine/Core/Math/Transform.h | 4 +--- Source/Engine/Core/Math/Triangle.h | 4 +--- Source/Engine/Core/Math/Vector2.h | 4 +--- Source/Engine/Core/Math/Vector3.h | 4 +--- Source/Engine/Core/Math/Vector4.h | 4 +--- Source/Engine/Core/Math/Viewport.h | 4 +--- 19 files changed, 20 insertions(+), 61 deletions(-) diff --git a/Source/Editor/Tools/Foliage/FoliageTools.cpp b/Source/Editor/Tools/Foliage/FoliageTools.cpp index b24bcdf00..7d0de7d41 100644 --- a/Source/Editor/Tools/Foliage/FoliageTools.cpp +++ b/Source/Editor/Tools/Foliage/FoliageTools.cpp @@ -292,7 +292,6 @@ void FoliageTools::Paint(Foliage* foliage, Span foliageTypesIndices, cons { PROFILE_CPU_NAMED("Place Instances"); - Matrix matrix; FoliageInstance instance; Quaternion tmp; Matrix world; diff --git a/Source/Engine/Core/Math/BoundingBox.h b/Source/Engine/Core/Math/BoundingBox.h index 0ee4fc1a9..d06ef0b3f 100644 --- a/Source/Engine/Core/Math/BoundingBox.h +++ b/Source/Engine/Core/Math/BoundingBox.h @@ -38,9 +38,7 @@ public: /// /// Empty constructor. /// - BoundingBox() - { - } + BoundingBox() = default; /// /// Initializes a new instance of the struct. diff --git a/Source/Engine/Core/Math/BoundingFrustum.h b/Source/Engine/Core/Math/BoundingFrustum.h index 287d33c2f..1502b6302 100644 --- a/Source/Engine/Core/Math/BoundingFrustum.h +++ b/Source/Engine/Core/Math/BoundingFrustum.h @@ -34,9 +34,7 @@ public: /// /// Empty constructor. /// - BoundingFrustum() - { - } + BoundingFrustum() = default; /// /// Initializes a new instance of the struct. diff --git a/Source/Engine/Core/Math/BoundingSphere.h b/Source/Engine/Core/Math/BoundingSphere.h index 08f5df7f1..da303e8d5 100644 --- a/Source/Engine/Core/Math/BoundingSphere.h +++ b/Source/Engine/Core/Math/BoundingSphere.h @@ -34,9 +34,7 @@ public: /// /// Empty constructor. /// - BoundingSphere() - { - } + BoundingSphere() = default; /// /// Initializes a new instance of the struct. diff --git a/Source/Engine/Core/Math/Color.h b/Source/Engine/Core/Math/Color.h index 997ed9c17..af93fc0f2 100644 --- a/Source/Engine/Core/Math/Color.h +++ b/Source/Engine/Core/Math/Color.h @@ -50,9 +50,7 @@ public: /// /// Empty constructor. /// - Color() - { - } + Color() = default; /// /// Initializes a new instance of the struct. diff --git a/Source/Engine/Core/Math/Color32.h b/Source/Engine/Core/Math/Color32.h index 36da3d41d..6c2b9648d 100644 --- a/Source/Engine/Core/Math/Color32.h +++ b/Source/Engine/Core/Math/Color32.h @@ -53,9 +53,7 @@ public: /// /// Empty constructor. /// - Color32() - { - } + Color32() = default; /// /// Constructs a new Color32 with given r, g, b, a components. diff --git a/Source/Engine/Core/Math/Half.h b/Source/Engine/Core/Math/Half.h index 06b03895d..346b5d6b3 100644 --- a/Source/Engine/Core/Math/Half.h +++ b/Source/Engine/Core/Math/Half.h @@ -121,9 +121,7 @@ public: /// /// Default constructor /// - Half2() - { - } + Half2() = default; /// /// Init @@ -185,9 +183,7 @@ public: Half Z; public: - Half3() - { - } + Half3() = default; Half3(Half x, Half y, Half z) : X(x) @@ -242,9 +238,7 @@ public: Half W; public: - Half4() - { - } + Half4() = default; Half4(Half x, Half y, Half z, Half w) : X(x) diff --git a/Source/Engine/Core/Math/Matrix.h b/Source/Engine/Core/Math/Matrix.h index 7d6d57bac..aed2ead8d 100644 --- a/Source/Engine/Core/Math/Matrix.h +++ b/Source/Engine/Core/Math/Matrix.h @@ -83,9 +83,7 @@ public: /// /// Empty constructor. /// - Matrix() - { - } + Matrix() = default; /// /// Initializes a new instance of the struct. diff --git a/Source/Engine/Core/Math/Matrix3x3.h b/Source/Engine/Core/Math/Matrix3x3.h index 1e29be85a..344f68455 100644 --- a/Source/Engine/Core/Math/Matrix3x3.h +++ b/Source/Engine/Core/Math/Matrix3x3.h @@ -63,9 +63,7 @@ public: /// /// Empty constructor. /// - Matrix3x3() - { - } + Matrix3x3() = default; /// /// Initializes a new instance of the struct. diff --git a/Source/Engine/Core/Math/Plane.h b/Source/Engine/Core/Math/Plane.h index 57a2be344..8c3d4129b 100644 --- a/Source/Engine/Core/Math/Plane.h +++ b/Source/Engine/Core/Math/Plane.h @@ -30,9 +30,7 @@ public: /// /// Empty constructor. /// - Plane() - { - } + Plane() = default; /// /// Init diff --git a/Source/Engine/Core/Math/Quaternion.h b/Source/Engine/Core/Math/Quaternion.h index 5b164f979..e6425f7ab 100644 --- a/Source/Engine/Core/Math/Quaternion.h +++ b/Source/Engine/Core/Math/Quaternion.h @@ -67,9 +67,7 @@ public: /// /// Empty constructor. /// - Quaternion() - { - } + Quaternion() = default; /// /// Init diff --git a/Source/Engine/Core/Math/Ray.h b/Source/Engine/Core/Math/Ray.h index c8486f39e..f2f9081e5 100644 --- a/Source/Engine/Core/Math/Ray.h +++ b/Source/Engine/Core/Math/Ray.h @@ -35,9 +35,7 @@ public: /// /// Empty constructor. /// - Ray() - { - } + Ray() = default; /// /// Initializes a new instance of the struct. diff --git a/Source/Engine/Core/Math/Rectangle.h b/Source/Engine/Core/Math/Rectangle.h index c59905fd6..bf282b6e3 100644 --- a/Source/Engine/Core/Math/Rectangle.h +++ b/Source/Engine/Core/Math/Rectangle.h @@ -31,9 +31,7 @@ public: /// /// Empty constructor. /// - Rectangle() - { - } + Rectangle() = default; // Init // @param x Rectangle location X coordinate diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h index ef644f08a..1079b4c23 100644 --- a/Source/Engine/Core/Math/Transform.h +++ b/Source/Engine/Core/Math/Transform.h @@ -40,9 +40,7 @@ public: /// /// Empty constructor. /// - Transform() - { - } + Transform() = default; /// /// Initializes a new instance of the struct. diff --git a/Source/Engine/Core/Math/Triangle.h b/Source/Engine/Core/Math/Triangle.h index a92919cfc..63f0e34ec 100644 --- a/Source/Engine/Core/Math/Triangle.h +++ b/Source/Engine/Core/Math/Triangle.h @@ -30,9 +30,7 @@ public: /// /// Empty constructor. /// - Triangle() - { - } + Triangle() = default; /// /// Initializes a new instance of the struct. diff --git a/Source/Engine/Core/Math/Vector2.h b/Source/Engine/Core/Math/Vector2.h index 87ced1de6..83deb009a 100644 --- a/Source/Engine/Core/Math/Vector2.h +++ b/Source/Engine/Core/Math/Vector2.h @@ -60,9 +60,7 @@ public: /// /// Empty constructor. /// - Vector2Base() - { - } + Vector2Base() = default; FORCE_INLINE Vector2Base(T xy) : X(xy) diff --git a/Source/Engine/Core/Math/Vector3.h b/Source/Engine/Core/Math/Vector3.h index 33be7f2d6..cad5250b7 100644 --- a/Source/Engine/Core/Math/Vector3.h +++ b/Source/Engine/Core/Math/Vector3.h @@ -89,9 +89,7 @@ public: /// /// Empty constructor. /// - Vector3Base() - { - } + Vector3Base() = default; FORCE_INLINE Vector3Base(T xyz) : X(xyz) diff --git a/Source/Engine/Core/Math/Vector4.h b/Source/Engine/Core/Math/Vector4.h index 213f05815..5c7b24c4a 100644 --- a/Source/Engine/Core/Math/Vector4.h +++ b/Source/Engine/Core/Math/Vector4.h @@ -76,9 +76,7 @@ public: /// /// Empty constructor. /// - Vector4Base() - { - } + Vector4Base() = default; FORCE_INLINE Vector4Base(T xyzw) : X(xyzw) diff --git a/Source/Engine/Core/Math/Viewport.h b/Source/Engine/Core/Math/Viewport.h index bb4ec8f8b..d478eb7cc 100644 --- a/Source/Engine/Core/Math/Viewport.h +++ b/Source/Engine/Core/Math/Viewport.h @@ -52,9 +52,7 @@ public: /// /// Empty constructor. /// - Viewport() - { - } + Viewport() = default; // Init // @param x The x coordinate of the upper-left corner of the viewport in pixels From a1cdf3e733dbf304cca6c6927cac3410f12f0a63 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 20 Jul 2023 21:31:07 -0500 Subject: [PATCH 22/52] Fix bug of populating asset picker if option platform type does not exist. --- Source/Editor/CustomEditors/Editors/AssetRefEditor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index e89c80372..cfba940c2 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -80,6 +80,8 @@ namespace FlaxEditor.CustomEditors.Editors assetType = customType; else if (!Content.Settings.GameSettings.OptionalPlatformSettings.Contains(assetReference.TypeName)) Debug.LogWarning(string.Format("Unknown asset type '{0}' to use for asset picker filter.", assetReference.TypeName)); + else + assetType = ScriptType.Void; } } From 83427ba1d492e3239e64e7a6669d8aa1db3d3470 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 20 Jul 2023 23:02:26 -0500 Subject: [PATCH 23/52] Add batch creating prefabs from mutiple selected actors in the scene tree. --- .../Content/GUI/ContentView.DragDrop.cs | 10 ++++- Source/Editor/Modules/PrefabsModule.cs | 44 ++++++++++++------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/Source/Editor/Content/GUI/ContentView.DragDrop.cs b/Source/Editor/Content/GUI/ContentView.DragDrop.cs index 3c0993b93..348e2b443 100644 --- a/Source/Editor/Content/GUI/ContentView.DragDrop.cs +++ b/Source/Editor/Content/GUI/ContentView.DragDrop.cs @@ -45,8 +45,14 @@ namespace FlaxEditor.Content.GUI private void ImportActors(DragActors actors, ContentFolder location) { - // Use only the first actor - Editor.Instance.Prefabs.CreatePrefab(actors.Objects[0].Actor); + foreach (var actorNode in actors.Objects) + { + var actor = actorNode.Actor; + if (actors.Objects.Contains(actorNode.ParentNode as ActorNode)) + continue; + + Editor.Instance.Prefabs.CreatePrefab(actor, false); + } } /// diff --git a/Source/Editor/Modules/PrefabsModule.cs b/Source/Editor/Modules/PrefabsModule.cs index ed422e991..7b3ffebb3 100644 --- a/Source/Editor/Modules/PrefabsModule.cs +++ b/Source/Editor/Modules/PrefabsModule.cs @@ -37,6 +37,11 @@ namespace FlaxEditor.Modules /// public event Action PrefabApplied; + /// + /// Locally cached actor for prefab creation. + /// + private Actor _prefabCreationActor; + internal PrefabsModule(Editor editor) : base(editor) { @@ -78,6 +83,16 @@ namespace FlaxEditor.Modules /// /// The root prefab actor. public void CreatePrefab(Actor actor) + { + CreatePrefab(actor, true); + } + + /// + /// Starts the creating prefab for the given actor by showing the new item creation dialog in . + /// + /// The root prefab actor. + /// Allow renaming or not + public void CreatePrefab(Actor actor, bool rename) { // Skip in invalid states if (!Editor.StateMachine.CurrentState.CanEditContent) @@ -90,7 +105,8 @@ namespace FlaxEditor.Modules PrefabCreating?.Invoke(actor); var proxy = Editor.ContentDatabase.GetProxy(); - Editor.Windows.ContentWin.NewItem(proxy, actor, OnPrefabCreated, actor.Name); + _prefabCreationActor = actor; + Editor.Windows.ContentWin.NewItem(proxy, actor, OnPrefabCreated, actor.Name, rename); } private void OnPrefabCreated(ContentItem contentItem) @@ -107,25 +123,21 @@ namespace FlaxEditor.Modules // Record undo for prefab creating (backend links the target instance with the prefab) if (Editor.Undo.Enabled) { - var selection = Editor.SceneEditing.Selection.Where(x => x is ActorNode).ToList().BuildNodesParents(); - if (selection.Count == 0) + if (!_prefabCreationActor) return; - if (selection.Count == 1) + var actorsList = new List(); + Utilities.Utils.GetActorsTree(actorsList, _prefabCreationActor); + + var actions = new IUndoAction[actorsList.Count]; + for (int i = 0; i < actorsList.Count; i++) { - var action = BreakPrefabLinkAction.Linked(((ActorNode)selection[0]).Actor); - Undo.AddAction(action); - } - else - { - var actions = new IUndoAction[selection.Count]; - for (int i = 0; i < selection.Count; i++) - { - var action = BreakPrefabLinkAction.Linked(((ActorNode)selection[i]).Actor); - actions[i] = action; - } - Undo.AddAction(new MultiUndoAction(actions)); + var action = BreakPrefabLinkAction.Linked(actorsList[i]); + actions[i] = action; } + Undo.AddAction(new MultiUndoAction(actions)); + + _prefabCreationActor = null; } Editor.Instance.Windows.PropertiesWin.Presenter.BuildLayout(); From 91e0e2011c8fa11260872ba250db04a2dbbdc3e2 Mon Sep 17 00:00:00 2001 From: Mateusz Karbowiak Date: Sat, 22 Jul 2023 18:39:50 +0200 Subject: [PATCH 24/52] Fix visibility of string wrapper --- Source/Engine/Engine/NativeInterop.Managed.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs index 797b02d5b..95146a95c 100644 --- a/Source/Engine/Engine/NativeInterop.Managed.cs +++ b/Source/Engine/Engine/NativeInterop.Managed.cs @@ -274,12 +274,12 @@ namespace FlaxEngine.Interop #if FLAX_EDITOR [HideInEditor] #endif - internal static class ManagedString + public static class ManagedString { internal static ManagedHandle EmptyStringHandle = ManagedHandle.Alloc(string.Empty); [System.Diagnostics.DebuggerStepThrough] - internal static unsafe IntPtr ToNative(string str) + public static unsafe IntPtr ToNative(string str) { if (str == null) return IntPtr.Zero; @@ -290,7 +290,7 @@ namespace FlaxEngine.Interop } [System.Diagnostics.DebuggerStepThrough] - internal static unsafe IntPtr ToNativeWeak(string str) + public static unsafe IntPtr ToNativeWeak(string str) { if (str == null) return IntPtr.Zero; @@ -301,7 +301,7 @@ namespace FlaxEngine.Interop } [System.Diagnostics.DebuggerStepThrough] - internal static string ToManaged(IntPtr ptr) + public static string ToManaged(IntPtr ptr) { if (ptr == IntPtr.Zero) return null; @@ -309,7 +309,7 @@ namespace FlaxEngine.Interop } [System.Diagnostics.DebuggerStepThrough] - internal static void Free(IntPtr ptr) + public static void Free(IntPtr ptr) { if (ptr == IntPtr.Zero) return; From d1a6bdceededa1fdf3f547291dfa68b930159996 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 23 Jul 2023 12:46:18 +0300 Subject: [PATCH 25/52] Add `params` tag to `API_PARAM` for C# variadic parameters support --- .../Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs | 6 ++++++ Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs | 2 +- .../Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs | 3 +++ Source/Tools/Flax.Build/Bindings/FunctionInfo.cs | 3 +++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 920e74f67..087dc898f 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -1203,6 +1203,8 @@ namespace Flax.Build.Bindings contents.Append("ref "); else if (parameterInfo.IsThis) contents.Append("this "); + else if (parameterInfo.IsParams) + contents.Append("params "); contents.Append(managedType); contents.Append(' '); contents.Append(parameterInfo.Name); @@ -1263,6 +1265,8 @@ namespace Flax.Build.Bindings contents.Append("ref "); else if (parameterInfo.IsThis) contents.Append("this "); + else if (parameterInfo.IsParams) + contents.Append("params "); contents.Append(managedType); contents.Append(' '); contents.Append(parameterInfo.Name); @@ -1988,6 +1992,8 @@ namespace Flax.Build.Bindings contents.Append("ref "); else if (parameterInfo.IsThis) contents.Append("this "); + else if (parameterInfo.IsParams) + contents.Append("params "); contents.Append(managedType); contents.Append(' '); contents.Append(parameterInfo.Name); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs index 72896f186..c2a785c9c 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs @@ -19,7 +19,7 @@ namespace Flax.Build.Bindings partial class BindingsGenerator { private static readonly Dictionary TypeCache = new Dictionary(); - private const int CacheVersion = 18; + private const int CacheVersion = 19; internal static void Write(BinaryWriter writer, string e) { diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index 1eeefaf27..d4fcd9629 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -337,6 +337,9 @@ namespace Flax.Build.Bindings case "this": currentParam.IsThis = true; break; + case "params": + currentParam.IsParams = true; + break; case "attributes": currentParam.Attributes = tag.Value; break; diff --git a/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs b/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs index 3676e8e76..bae49e98e 100644 --- a/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs @@ -19,6 +19,7 @@ namespace Flax.Build.Bindings public bool IsRef; public bool IsOut; public bool IsThis; + public bool IsParams; public bool HasDefaultValue => !string.IsNullOrEmpty(DefaultValue); @@ -37,6 +38,7 @@ namespace Flax.Build.Bindings writer.Write(IsRef); writer.Write(IsOut); writer.Write(IsThis); + writer.Write(IsParams); } public void Read(BinaryReader reader) @@ -49,6 +51,7 @@ namespace Flax.Build.Bindings IsRef = reader.ReadBoolean(); IsOut = reader.ReadBoolean(); IsThis = reader.ReadBoolean(); + IsParams = reader.ReadBoolean(); } public override string ToString() From 02d135053f05bdbf5eaafd94179b8ffe872cf8c8 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 23 Jul 2023 15:10:31 -0500 Subject: [PATCH 26/52] Add scroll to selected asset/content item on asset picker select menu open. --- Source/Editor/GUI/AssetPicker.cs | 36 +++++++++++++++++++++-- Source/Editor/GUI/ItemsListContextMenu.cs | 9 ++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index bfded5380..7447e8472 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -475,22 +475,54 @@ namespace FlaxEditor.GUI if (_type != ScriptType.Null) { // Show asset picker popup - AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item => + var popup = AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item => { SelectedItem = item; RootWindow.Focus(); Focus(); }); + if (_selected != null) + { + var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path); + foreach (var child in popup.ItemsPanel.Children) + { + if (child is not ItemsListContextMenu.Item item) + continue; + if (string.Equals(item.Name, selectedAssetName, StringComparison.Ordinal)) + { + // Highlight and scroll to item + item.Focus(); + popup.ScrollViewTo(item); + break; + } + } + } } else { // Show content item picker popup - ContentSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item => + var popup = ContentSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item => { SelectedItem = item; RootWindow.Focus(); Focus(); }); + if (_selectedItem != null) + { + var selectedItemName = _selectedItem.ShortName; + foreach (var child in popup.ItemsPanel.Children) + { + if (child is not ItemsListContextMenu.Item item) + continue; + if (string.Equals(item.Name, selectedItemName, StringComparison.Ordinal)) + { + // Highlight and scroll to item + item.Focus(); + popup.ScrollViewTo(item); + break; + } + } + } } } else if (_selected != null || _selectedItem != null) diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index ae470235d..12de479bf 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -265,6 +265,15 @@ namespace FlaxEditor.GUI _searchBox.Focus(); } + /// + /// Scrolls the scroll panel to a specific Item + /// + /// The item to scroll to. + public void ScrollViewTo(Item item) + { + _scrollPanel.ScrollViewTo(item, true); + } + /// /// Sorts the items list (by item name by default). /// From 12005ad3141c695cdef59bac66920013ec36b925 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 23 Jul 2023 15:20:57 -0500 Subject: [PATCH 27/52] Simplify functionality into function. --- Source/Editor/GUI/AssetPicker.cs | 27 ++--------------------- Source/Editor/GUI/ItemsListContextMenu.cs | 20 +++++++++++++++++ 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 7447e8472..39a06cf0b 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -484,18 +484,7 @@ namespace FlaxEditor.GUI if (_selected != null) { var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path); - foreach (var child in popup.ItemsPanel.Children) - { - if (child is not ItemsListContextMenu.Item item) - continue; - if (string.Equals(item.Name, selectedAssetName, StringComparison.Ordinal)) - { - // Highlight and scroll to item - item.Focus(); - popup.ScrollViewTo(item); - break; - } - } + popup.ScrollToAndHighlightItemByName(selectedAssetName); } } else @@ -509,19 +498,7 @@ namespace FlaxEditor.GUI }); if (_selectedItem != null) { - var selectedItemName = _selectedItem.ShortName; - foreach (var child in popup.ItemsPanel.Children) - { - if (child is not ItemsListContextMenu.Item item) - continue; - if (string.Equals(item.Name, selectedItemName, StringComparison.Ordinal)) - { - // Highlight and scroll to item - item.Focus(); - popup.ScrollViewTo(item); - break; - } - } + popup.ScrollToAndHighlightItemByName(_selectedItem.ShortName); } } } diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index 12de479bf..42d236991 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -274,6 +274,26 @@ namespace FlaxEditor.GUI _scrollPanel.ScrollViewTo(item, true); } + /// + /// Scrolls to the item and focuses it by name. + /// + /// The item name. + public void ScrollToAndHighlightItemByName(string itemName) + { + foreach (var child in ItemsPanel.Children) + { + if (child is not ItemsListContextMenu.Item item) + continue; + if (string.Equals(item.Name, itemName, StringComparison.Ordinal)) + { + // Highlight and scroll to item + item.Focus(); + ScrollViewTo(item); + break; + } + } + } + /// /// Sorts the items list (by item name by default). /// From f80b7ee2a52c26a6a8b60d517f25760f7e994513 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 23 Jul 2023 18:58:47 -0500 Subject: [PATCH 28/52] Fix spacing --- Source/Editor/GUI/AssetPicker.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 39a06cf0b..3e5d22eb0 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -482,9 +482,9 @@ namespace FlaxEditor.GUI Focus(); }); if (_selected != null) - { - var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path); - popup.ScrollToAndHighlightItemByName(selectedAssetName); + { + var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path); + popup.ScrollToAndHighlightItemByName(selectedAssetName); } } else From 11bb6d4364abb928ac3d25d3910ebd4c1d08e252 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 24 Jul 2023 14:23:28 +0200 Subject: [PATCH 29/52] Fix crash when using custom Anim Graph node (.NET 7 regression) --- Source/Engine/Animations/AnimationGraph.cs | 15 ++++- .../Animations/Graph/AnimGraph.Custom.cpp | 10 +--- Source/Engine/Core/Types/Version.h | 57 +------------------ Source/Engine/Engine/NativeInterop.cs | 24 ++++++-- .../Internal/ManagedSerialization.cpp | 2 +- Source/Engine/Scripting/ManagedCLR/MUtils.cpp | 42 +++++++------- Source/Engine/Scripting/Scripting.cs | 26 +++++---- 7 files changed, 72 insertions(+), 104 deletions(-) diff --git a/Source/Engine/Animations/AnimationGraph.cs b/Source/Engine/Animations/AnimationGraph.cs index 46b3acfc1..2e26b8370 100644 --- a/Source/Engine/Animations/AnimationGraph.cs +++ b/Source/Engine/Animations/AnimationGraph.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -94,9 +93,11 @@ namespace FlaxEngine public AnimatedModel Instance; } + [HideInEditor] [CustomMarshaller(typeof(Context), MarshalMode.Default, typeof(ContextMarshaller))] internal static class ContextMarshaller { + [HideInEditor] [StructLayout(LayoutKind.Sequential)] public struct ContextNative { @@ -116,7 +117,7 @@ namespace FlaxEngine internal static Context ToManaged(ContextNative managed) { - return new Context() + return new Context { Graph = managed.Graph, GraphExecutor = managed.GraphExecutor, @@ -129,9 +130,10 @@ namespace FlaxEngine Instance = AnimatedModelMarshaller.ConvertToManaged(managed.Instance), }; } + internal static ContextNative ToNative(Context managed) { - return new ContextNative() + return new ContextNative { Graph = managed.Graph, GraphExecutor = managed.GraphExecutor, @@ -144,6 +146,7 @@ namespace FlaxEngine Instance = AnimatedModelMarshaller.ConvertToUnmanaged(managed.Instance), }; } + internal static void Free(ContextNative unmanaged) { } @@ -243,6 +246,12 @@ namespace FlaxEngine throw new ArgumentNullException(nameof(source)); if (destination == null) throw new ArgumentNullException(nameof(destination)); + if (source->NodesCount <= 0 || source->NodesCount > 4096) + throw new ArgumentOutOfRangeException(nameof(source)); + if (destination->NodesCount <= 0 || destination->NodesCount > 4096) + throw new ArgumentOutOfRangeException(nameof(destination)); + if (source->NodesCount != destination->NodesCount) + throw new ArgumentOutOfRangeException(); destination->NodesCount = source->NodesCount; destination->Unused = source->Unused; Utils.MemoryCopy(new IntPtr(destination->Nodes), new IntPtr(source->Nodes), (ulong)(source->NodesCount * sizeof(Transform))); diff --git a/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp b/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp index d74c523f1..cdd5b03eb 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp @@ -5,7 +5,6 @@ #include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/BinaryModule.h" #include "Engine/Scripting/ManagedCLR/MCore.h" -#include "Engine/Scripting/ManagedCLR/MDomain.h" #include "Engine/Scripting/ManagedCLR/MMethod.h" #include "Engine/Scripting/ManagedCLR/MClass.h" #include "Engine/Scripting/ManagedCLR/MUtils.h" @@ -189,12 +188,8 @@ bool AnimGraph::InitCustomNode(Node* node) return false; } - // Create node values managed array + // Initialization can happen on Content Thread so ensure to have runtime attached MCore::Thread::Attach(); - MArray* values = MCore::Array::New( MCore::TypeCache::Object, node->Values.Count()); - MObject** valuesPtr = MCore::Array::GetAddress(values); - for (int32 i = 0; i < node->Values.Count(); i++) - valuesPtr[i] = MUtils::BoxVariant(node->Values[i]); // Allocate managed node object (create GC handle to prevent destruction) MObject* obj = type->CreateInstance(); @@ -202,7 +197,7 @@ bool AnimGraph::InitCustomNode(Node* node) // Initialize node InternalInitData initData; - initData.Values = values; + initData.Values = MUtils::ToArray(node->Values, MCore::TypeCache::Object); initData.BaseModel = BaseModel.GetManagedInstance(); void* params[1]; params[0] = &initData; @@ -211,7 +206,6 @@ bool AnimGraph::InitCustomNode(Node* node) if (exception) { MCore::GCHandle::Free(handleGC); - MException ex(exception); ex.Log(LogType::Warning, TEXT("AnimGraph")); return false; diff --git a/Source/Engine/Core/Types/Version.h b/Source/Engine/Core/Types/Version.h index 57c6e10fe..1dc36ea38 100644 --- a/Source/Engine/Core/Types/Version.h +++ b/Source/Engine/Core/Types/Version.h @@ -72,15 +72,6 @@ public: return _major; } - /// - /// Gets the high 16 bits of the revision number. - /// - /// A 16-bit signed integer. - FORCE_INLINE int16 MajorRevision() const - { - return static_cast(_revision >> 16); - } - /// /// Gets the value of the minor component of the version number for the current Version object. /// @@ -90,15 +81,6 @@ public: return _minor; } - /// - /// Gets the low 16 bits of the revision number. - /// - /// A 16-bit signed integer. - FORCE_INLINE int16 MinorRevision() const - { - return static_cast(_revision & 65535); - } - /// /// Gets the value of the revision component of the version number for the current Version object. /// @@ -126,61 +108,26 @@ public: return _major == obj._major && _minor == obj._minor && _build == obj._build && _revision == obj._revision; } - /// - /// Determines whether two specified Version objects are equal. - /// - /// The other Version object. - /// True if equals ; otherwise, false. FORCE_INLINE bool operator==(const Version& other) const { return Equals(other); } - - /// - /// Determines whether the first specified Version object is greater than the second specified Version object. - /// - /// The first Version object. - /// True if is greater than ; otherwise, false. - FORCE_INLINE bool operator >(const Version& other) const + FORCE_INLINE bool operator>(const Version& other) const { return other < *this; } - - /// - /// Determines whether the first specified Version object is greater than or equal to the second specified Version object. - /// /summary> - /// The other Version object. - /// True if is greater than or equal to ; otherwise, false. - FORCE_INLINE bool operator >=(const Version& other) const + FORCE_INLINE bool operator>=(const Version& other) const { return other <= *this; } - - /// - /// Determines whether two specified Version objects are not equal. - /// - /// The other Version object. - /// True if does not equal ; otherwise, false. FORCE_INLINE bool operator!=(const Version& other) const { return !(*this == other); } - - /// - /// Determines whether the first specified Version object is less than the second specified Version object. - /// - /// The first other object. - /// True if is less than ; otherwise, false. FORCE_INLINE bool operator<(const Version& other) const { return CompareTo(other) < 0; } - - /// - /// Determines whether the first specified Version object is less than or equal to the second Version object. - /// - /// The other Version object. - /// True if is less than or equal to ; otherwise, false. FORCE_INLINE bool operator<=(const Version& other) const { return CompareTo(other) <= 0; diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index acfd7181b..da372ac90 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -444,7 +444,7 @@ namespace FlaxEngine.Interop } else { - toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedField), bindingFlags); + toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedFieldArray), bindingFlags); toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), bindingFlags); } } @@ -663,6 +663,17 @@ namespace FlaxEngine.Interop MarshalHelperReferenceType.ToManaged(ref fieldValueRef, Unsafe.Read(fieldPtr.ToPointer()), false); } + internal static void ToManagedFieldArray(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + { + fieldOffset = Unsafe.SizeOf(); + IntPtr fieldStartPtr = fieldPtr; + fieldPtr = EnsureAlignment(fieldPtr, IntPtr.Size); + fieldOffset += (fieldPtr - fieldStartPtr).ToInt32(); + + ref TField[] fieldValueRef = ref GetFieldReference(field, ref fieldOwner); + MarshalHelperReferenceType.ToManagedArray(ref fieldValueRef, Unsafe.Read(fieldPtr.ToPointer()), false); + } + internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) { fieldOffset = Unsafe.SizeOf(); @@ -691,14 +702,15 @@ namespace FlaxEngine.Interop internal static void ToManaged(ref T managedValue, IntPtr nativePtr, bool byRef) { Type type = typeof(T); - if (type.IsByRef || byRef) + byRef |= type.IsByRef; + if (byRef) { if (type.IsByRef) type = type.GetElementType(); Assert.IsTrue(type.IsValueType); } - if (type == typeof(IntPtr)) + if (type == typeof(IntPtr) && byRef) managedValue = (T)(object)nativePtr; else if (type == typeof(ManagedHandle)) managedValue = (T)(object)ManagedHandle.FromIntPtr(nativePtr); @@ -778,10 +790,12 @@ namespace FlaxEngine.Interop if (type == typeof(string)) managedValue = Unsafe.As(ManagedString.ToManaged(nativePtr)); + else if (nativePtr == IntPtr.Zero) + managedValue = null; else if (type.IsClass) - managedValue = nativePtr != IntPtr.Zero ? Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target) : null; + managedValue = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); else if (type.IsInterface) // Dictionary - managedValue = nativePtr != IntPtr.Zero ? Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target) : null; + managedValue = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); else throw new NotImplementedException(); } diff --git a/Source/Engine/Scripting/Internal/ManagedSerialization.cpp b/Source/Engine/Scripting/Internal/ManagedSerialization.cpp index f1543a326..f5ea46b5e 100644 --- a/Source/Engine/Scripting/Internal/ManagedSerialization.cpp +++ b/Source/Engine/Scripting/Internal/ManagedSerialization.cpp @@ -108,7 +108,7 @@ void ManagedSerialization::Deserialize(const StringAnsiView& data, MObject* obje // Prepare arguments void* args[3]; args[0] = object; - args[1] = (void*)str; + args[1] = (void*)&str; args[2] = (void*)&len; // Call serialization tool diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp index d97ff92cd..be8e10fab 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp @@ -3,7 +3,6 @@ #include "MUtils.h" #include "MClass.h" #include "MCore.h" -#include "MDomain.h" #include "Engine/Core/Log.h" #include "Engine/Core/Types/DataContainer.h" #include "Engine/Core/Types/Version.h" @@ -449,7 +448,7 @@ Variant MUtils::UnboxVariant(MObject* value) { auto& a = array[i]; a.SetType(elementType); - Platform::MemoryCopy(&a.AsData,(byte*)ptr + elementSize * i, elementSize); + Platform::MemoryCopy(&a.AsData, (byte*)ptr + elementSize * i, elementSize); } break; case VariantType::Transform: @@ -1004,11 +1003,11 @@ MClass* MUtils::GetClass(const Variant& value) case VariantType::Enum: return Scripting::FindClass(StringAnsiView(value.Type.TypeName)); case VariantType::ManagedObject: - { - MObject* obj = (MObject*)value; - if (obj) - return MCore::Object::GetClass(obj); - } + { + MObject* obj = (MObject*)value; + if (obj) + return MCore::Object::GetClass(obj); + } default: ; } return GetClass(value.Type); @@ -1224,10 +1223,10 @@ MObject* MUtils::ToManaged(const Version& value) auto versionToManaged = scriptingClass->GetMethod("VersionToManaged", 4); CHECK_RETURN(versionToManaged, nullptr); - int major = value.Major(); - int minor = value.Minor(); - int build = value.Build(); - int revision = value.Revision(); + int32 major = value.Major(); + int32 minor = value.Minor(); + int32 build = value.Build(); + int32 revision = value.Revision(); void* params[4]; params[0] = &major; @@ -1244,26 +1243,29 @@ MObject* MUtils::ToManaged(const Version& value) Version MUtils::ToNative(MObject* value) { + Version result; if (value) #if USE_NETCORE { - auto ver = Version(); - auto scriptingClass = Scripting::GetStaticClass(); - CHECK_RETURN(scriptingClass, ver); - auto versionToNative = scriptingClass->GetMethod("VersionToNative", 2); - CHECK_RETURN(versionToNative, ver); + CHECK_RETURN(scriptingClass, result); + auto versionToNative = scriptingClass->GetMethod("VersionToNative", 5); + CHECK_RETURN(versionToNative, result); - void* params[2]; + void* params[5]; params[0] = value; - params[1] = &ver; + params[1] = (byte*)&result; + params[2] = (byte*)&result + sizeof(int32); + params[3] = (byte*)&result + sizeof(int32) * 2; + params[4] = (byte*)&result + sizeof(int32) * 3; versionToNative->Invoke(nullptr, params, nullptr); - return ver; + + return result; } #else return *(Version*)MCore::Object::Unbox(value); #endif - return Version(); + return result; } #endif diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index 7a91b2d96..2b49d97d4 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -229,22 +229,24 @@ namespace FlaxEngine return ManagedHandle.Alloc(new CultureInfo(lcid)); } - internal static void VersionToNative(ManagedHandle versionHandle, IntPtr nativePtr) + [StructLayout(LayoutKind.Sequential)] + internal struct VersionNative + { + public int Major; + public int Minor; + public int Build; + public int Revision; + } + + internal static void VersionToNative(ManagedHandle versionHandle, ref int major, ref int minor, ref int build, ref int revision) { Version version = Unsafe.As(versionHandle.Target); if (version != null) { - Marshal.WriteInt32(nativePtr, 0, version.Major); - Marshal.WriteInt32(nativePtr, 4, version.Minor); - Marshal.WriteInt32(nativePtr, 8, version.Build); - Marshal.WriteInt32(nativePtr, 12, version.Revision); - } - else - { - Marshal.WriteInt32(nativePtr, 0, 0); - Marshal.WriteInt32(nativePtr, 4, 0); - Marshal.WriteInt32(nativePtr, 8, -1); - Marshal.WriteInt32(nativePtr, 12, -1); + major = version.Major; + minor = version.Minor; + build = version.Build; + revision = version.Revision; } } From c9254457a980a62755b9d28e39f90a8ccb3b594e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 24 Jul 2023 16:24:39 +0200 Subject: [PATCH 30/52] Code style tweaks --- Source/Engine/Content/Storage/FlaxStorage.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index 52d7c34b5..d530e5456 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -205,9 +205,6 @@ FlaxStorage::~FlaxStorage() { // Validate if has been disposed ASSERT(IsDisposed()); - - // Validate other fields - // Note: disposed storage has no open files CHECK(_chunksLock == 0); CHECK(_refCount == 0); ASSERT(_chunks.IsEmpty()); @@ -216,7 +213,6 @@ FlaxStorage::~FlaxStorage() // Ensure to close any outstanding file handles to prevent file locking in case it failed to load _file.DeleteAll(); #endif - } FlaxStorage::LockData FlaxStorage::LockSafe() @@ -550,11 +546,9 @@ bool FlaxStorage::Load() } break; default: - { LOG(Warning, "Unsupported storage format version: {1}. {0}", ToString(), _version); return true; } - } // Mark as loaded (version number describes 'isLoaded' state) _version = version; @@ -573,7 +567,7 @@ bool FlaxStorage::Reload() // Perform clean reloading Dispose(); - bool failed = Load(); + const bool failed = Load(); OnReloaded(this, failed); @@ -1434,10 +1428,8 @@ void FlaxFile::GetEntries(Array& output) const void FlaxFile::Dispose() { - // Base FlaxStorage::Dispose(); - // Clean _asset.ID = Guid::Empty; } @@ -1482,7 +1474,7 @@ bool FlaxPackage::HasAsset(const Guid& id) const bool FlaxPackage::HasAsset(const AssetInfo& info) const { ASSERT(_path == info.Path); - Entry* e = _entries.TryGet(info.ID); + const Entry* e = _entries.TryGet(info.ID); return e && e->TypeName == info.TypeName; } @@ -1511,10 +1503,8 @@ void FlaxPackage::GetEntries(Array& output) const void FlaxPackage::Dispose() { - // Base FlaxStorage::Dispose(); - // Clean _entries.Clear(); } From 543433440ea0e46fc81712f78744843f77626c16 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 24 Jul 2023 19:21:03 +0200 Subject: [PATCH 31/52] Fix nested animation playrate when the framerate is different #1258 --- Source/Engine/Animations/Graph/AnimGroup.Animation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 31b97fb52..82842376e 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -235,8 +235,9 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* const float nestedAnimLength = nestedAnim.Anim->GetLength(); const float nestedAnimDuration = nestedAnim.Anim->GetDuration(); const float nestedAnimSpeed = nestedAnim.Speed * speed; - nestedAnimPos = nestedAnimPos / nestedAnimDuration * nestedAnimSpeed; - nestedAnimPrevPos = nestedAnimPrevPos / nestedAnimDuration * nestedAnimSpeed; + const float frameRateMatchScale = (float)nestedAnim.Anim->Data.FramesPerSecond / (float)anim->Data.FramesPerSecond; + nestedAnimPos = nestedAnimPos / nestedAnimDuration * nestedAnimSpeed * frameRateMatchScale; + nestedAnimPrevPos = nestedAnimPrevPos / nestedAnimDuration * nestedAnimSpeed * frameRateMatchScale; GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos); ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode); From 3bd8d930e09c06b974db198d8406dd05862a6a40 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Tue, 25 Jul 2023 15:27:18 +0200 Subject: [PATCH 32/52] doc fixes and code corections mathf UnwindRadians has fixed coust added UnwindRadiansAccurate oldversion --- Source/Engine/Core/Math/Mathd.cs | 49 +++++++++++++++----- Source/Engine/Core/Math/Mathf.cs | 78 +++++++++++++++++++++++++------- 2 files changed, 100 insertions(+), 27 deletions(-) diff --git a/Source/Engine/Core/Math/Mathd.cs b/Source/Engine/Core/Math/Mathd.cs index 29bcc9420..d671d310b 100644 --- a/Source/Engine/Core/Math/Mathd.cs +++ b/Source/Engine/Core/Math/Mathd.cs @@ -880,7 +880,19 @@ namespace FlaxEngine /// Valid angle in radians. public static double UnwindRadians(double angle) { - // TODO: make it faster? + //[nori_sc] made it faster has fixed cost but with large angle values starts to lose accuracy floating point problem + // 1 call teaks ~20-30 ns with anny value + var a = angle - Floor(angle / TwoPi) * TwoPi; //loop funcion betwine 0 and TwoPi + return a > Pi ? (a - TwoPi) : a; // change range so it become Pi and -Pi + } + /// + /// the same as but is more computation intensive with large and has better accuracy with large + ///
cost of this funcion is %
+ ///
+ /// Angle in radians to unwind. + /// Valid angle in radians. + public static double UnwindRadiansAccurate(double angle) + { while (angle > Pi) { angle -= TwoPi; @@ -899,14 +911,26 @@ namespace FlaxEngine /// Valid angle in degrees. public static double UnwindDegrees(double angle) { - // TODO: make it faster? - while (angle > 180.0f) + //[nori_sc] made it faster for large values has fixed cost but with large angle values starts to lose accuracy floating point problem + // 1 call teaks ~20 ns with anny value + var a = angle - Floor(angle / 360.0) * 360.0; //loop funcion betwine 0 and 360 + return a > 180 ? (a - 360.0) : a; // change range so it become 180 and -180 + } + /// + /// the same as but is more computation intensive with large and has better accuracy with large + ///
cost of this funcion is % 180.0f
+ ///
+ /// Angle in radians to unwind. + /// Valid angle in radians. + public static double UnwindDegreesAccurate(double angle) + { + while (angle > 180.0) { - angle -= 360.0f; + angle -= 360.0; } - while (angle < -180.0f) + while (angle < -180.0) { - angle += 360.0f; + angle += 360.0; } return angle; } @@ -927,8 +951,9 @@ namespace FlaxEngine /// Interpolates between two values using a linear function by a given amount. ///
/// - /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and - /// http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ + /// See: + ///

+ ///

///
/// Value to interpolate from. /// Value to interpolate to. @@ -944,7 +969,8 @@ namespace FlaxEngine /// Performs smooth (cubic Hermite) interpolation between 0 and 1. ///
/// - /// See https://en.wikipedia.org/wiki/Smoothstep + /// See: + ///

///
/// Value between 0 and 1 indicating interpolation amount. public static double SmoothStep(double amount) @@ -956,7 +982,8 @@ namespace FlaxEngine /// Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints. ///
/// - /// See https://en.wikipedia.org/wiki/Smoothstep + /// See: + ///

///
/// Value between 0 and 1 indicating interpolation amount. public static double SmootherStep(double amount) @@ -1013,7 +1040,7 @@ namespace FlaxEngine /// /// Gauss function. - /// http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function + ///

///
/// Curve amplitude. /// Position X. diff --git a/Source/Engine/Core/Math/Mathf.cs b/Source/Engine/Core/Math/Mathf.cs index 9463c13cb..09000e0cb 100644 --- a/Source/Engine/Core/Math/Mathf.cs +++ b/Source/Engine/Core/Math/Mathf.cs @@ -1180,7 +1180,19 @@ namespace FlaxEngine /// Valid angle in radians. public static float UnwindRadians(float angle) { - // TODO: make it faster? + //[nori_sc] made it faster has fixed cost but with large angle values (100 for example) starts to lose accuracy floating point problem + // 1 call teaks ~20-30 ns with anny value + var a = angle - Mathf.Floor(angle / Mathf.TwoPi) * Mathf.TwoPi; //loop funcion betwine 0 and TwoPi + return a > Mathf.Pi ? (a - Mathf.TwoPi) : a; // change range so it become Pi and -Pi + } + /// + /// the same as but is more computation intensive with large and has better accuracy with large + ///
cost of this funcion is %
+ ///
+ /// Angle in radians to unwind. + /// Valid angle in radians. + public static float UnwindRadiansAccurate(float angle) + { while (angle > Pi) { angle -= TwoPi; @@ -1191,7 +1203,6 @@ namespace FlaxEngine } return angle; } - /// /// Utility to ensure angle is between +/- 180 degrees by unwinding /// @@ -1199,7 +1210,19 @@ namespace FlaxEngine /// Valid angle in degrees. public static float UnwindDegrees(float angle) { - // TODO: make it faster? + //[nori_sc] made it faster for large values has fixed cost but with large angle values (1 000 000 for example) starts to lose accuracy floating point problem + // 1 call teaks ~20 ns with anny value + var a = angle - Floor(angle / 360.0f) * 360.0f; //loop funcion betwine 0 and 360 + return a > 180 ? (a - 360.0f) : a; // change range so it become 180 and -180 + } + /// + /// the same as but is more computation intensive with large and has better accuracy with large + ///
cost of this funcion is % 180.0f
+ ///
+ /// Angle in radians to unwind. + /// Valid angle in radians. + public static float UnwindDegreesAccurate(float angle) + { while (angle > 180.0f) { angle -= 360.0f; @@ -1210,7 +1233,6 @@ namespace FlaxEngine } return angle; } - /// /// Clamps the specified value. /// @@ -1299,8 +1321,12 @@ namespace FlaxEngine /// /// Interpolates between two values using a linear function by a given amount. /// - /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ - /// Value to interpolate from. + /// + /// See: + ///

+ ///

+ ///
+ /// /// Value to interpolate from. /// Value to interpolate to. /// Interpolation amount. /// The result of linear interpolation of values based on the amount. @@ -1312,8 +1338,12 @@ namespace FlaxEngine /// /// Interpolates between two values using a linear function by a given amount. /// - /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ - /// Value to interpolate from. + /// + /// See: + ///

+ ///

+ ///
+ /// /// Value to interpolate from. /// Value to interpolate to. /// Interpolation amount. /// The result of linear interpolation of values based on the amount. @@ -1325,8 +1355,12 @@ namespace FlaxEngine /// /// Interpolates between two values using a linear function by a given amount. /// - /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ - /// Value to interpolate from. + /// + /// See: + ///

+ ///

+ ///
+ /// /// Value to interpolate from. /// Value to interpolate to. /// Interpolation amount. /// The result of linear interpolation of values based on the amount. @@ -1338,7 +1372,10 @@ namespace FlaxEngine /// /// Performs smooth (cubic Hermite) interpolation between 0 and 1. /// - /// See https://en.wikipedia.org/wiki/Smoothstep + /// + /// See: + ///

+ ///
/// Value between 0 and 1 indicating interpolation amount. public static float SmoothStep(float amount) { @@ -1348,7 +1385,10 @@ namespace FlaxEngine /// /// Performs smooth (cubic Hermite) interpolation between 0 and 1. /// - /// See https://en.wikipedia.org/wiki/Smoothstep + /// + /// See: + ///

+ ///
/// Value between 0 and 1 indicating interpolation amount. public static double SmoothStep(double amount) { @@ -1358,7 +1398,10 @@ namespace FlaxEngine /// /// Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints. /// - /// See https://en.wikipedia.org/wiki/Smoothstep + /// + /// See: + ///

+ ///
/// Value between 0 and 1 indicating interpolation amount. public static float SmootherStep(float amount) { @@ -1368,7 +1411,10 @@ namespace FlaxEngine /// /// Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints. /// - /// See https://en.wikipedia.org/wiki/Smoothstep + /// + /// See: + ///

+ ///
/// Value between 0 and 1 indicating interpolation amount. public static double SmootherStep(double amount) { @@ -1446,7 +1492,7 @@ namespace FlaxEngine /// /// Gauss function. - /// http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function + ///

///
/// Curve amplitude. /// Position X. @@ -1463,7 +1509,7 @@ namespace FlaxEngine /// /// Gauss function. - /// http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function + ///

///
/// Curve amplitude. /// Position X. From c2fffbcfdb5b07fccf5403f6a4d6cdcb0a8f6d8c Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 25 Jul 2023 20:48:51 +0300 Subject: [PATCH 33/52] Fix API_INJECT_CODE injecting duplicated code --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 087dc898f..fc3e6a534 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -2059,7 +2059,7 @@ namespace Flax.Build.Bindings if (code.StartsWith("using")) CSharpUsedNamespaces.Add(code.Substring(6)); else if (code.Length > 0) - contents.Append(injectCodeInfo.Code).AppendLine(";"); + contents.Append(code).AppendLine(";"); } } else From fae20daac947a98d3ad1736566b58b4f7de8b4f7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Jul 2023 19:28:55 +0200 Subject: [PATCH 34/52] Cleanup code #1267 --- Source/Engine/Core/Math/Mathd.cs | 34 ++++++++++++------------------ Source/Engine/Core/Math/Mathf.cs | 36 +++++++++++++------------------- 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/Source/Engine/Core/Math/Mathd.cs b/Source/Engine/Core/Math/Mathd.cs index d671d310b..48db547df 100644 --- a/Source/Engine/Core/Math/Mathd.cs +++ b/Source/Engine/Core/Math/Mathd.cs @@ -876,62 +876,54 @@ namespace FlaxEngine /// /// Given a heading which may be outside the +/- PI range, 'unwind' it back into that range. /// + /// Optimized version of that is it faster and has fixed cost but with large angle values (100 for example) starts to lose accuracy floating point problem. /// Angle in radians to unwind. /// Valid angle in radians. public static double UnwindRadians(double angle) { - //[nori_sc] made it faster has fixed cost but with large angle values starts to lose accuracy floating point problem - // 1 call teaks ~20-30 ns with anny value - var a = angle - Floor(angle / TwoPi) * TwoPi; //loop funcion betwine 0 and TwoPi - return a > Pi ? (a - TwoPi) : a; // change range so it become Pi and -Pi + var a = angle - Math.Floor(angle / TwoPi) * TwoPi; // Loop function between 0 and TwoPi + return a > Pi ? a - TwoPi : a; // Change range so it become Pi and -Pi } + /// - /// the same as but is more computation intensive with large and has better accuracy with large - ///
cost of this funcion is %
+ /// The same as but is more computation intensive with large and has better accuracy with large . + ///
cost of this function is %
///
/// Angle in radians to unwind. /// Valid angle in radians. public static double UnwindRadiansAccurate(double angle) { while (angle > Pi) - { angle -= TwoPi; - } while (angle < -Pi) - { angle += TwoPi; - } return angle; } /// - /// Utility to ensure angle is between +/- 180 degrees by unwinding + /// Utility to ensure angle is between +/- 180 degrees by unwinding. /// + /// Optimized version of that is it faster and has fixed cost but with large angle values (100 for example) starts to lose accuracy floating point problem. /// Angle in degrees to unwind. /// Valid angle in degrees. public static double UnwindDegrees(double angle) { - //[nori_sc] made it faster for large values has fixed cost but with large angle values starts to lose accuracy floating point problem - // 1 call teaks ~20 ns with anny value - var a = angle - Floor(angle / 360.0) * 360.0; //loop funcion betwine 0 and 360 - return a > 180 ? (a - 360.0) : a; // change range so it become 180 and -180 + var a = angle - Math.Floor(angle / 360.0) * 360.0; // Loop function between 0 and 360 + return a > 180 ? a - 360.0 : a; // Change range so it become 180 and -180 } + /// - /// the same as but is more computation intensive with large and has better accuracy with large - ///
cost of this funcion is % 180.0f
+ /// The same as but is more computation intensive with large and has better accuracy with large . + ///
cost of this function is % 180.0f
///
/// Angle in radians to unwind. /// Valid angle in radians. public static double UnwindDegreesAccurate(double angle) { while (angle > 180.0) - { angle -= 360.0; - } while (angle < -180.0) - { angle += 360.0; - } return angle; } diff --git a/Source/Engine/Core/Math/Mathf.cs b/Source/Engine/Core/Math/Mathf.cs index 09000e0cb..cea81a779 100644 --- a/Source/Engine/Core/Math/Mathf.cs +++ b/Source/Engine/Core/Math/Mathf.cs @@ -1176,63 +1176,57 @@ namespace FlaxEngine /// /// Given a heading which may be outside the +/- PI range, 'unwind' it back into that range. /// + /// Optimized version of that is it faster and has fixed cost but with large angle values (100 for example) starts to lose accuracy floating point problem. /// Angle in radians to unwind. /// Valid angle in radians. public static float UnwindRadians(float angle) { - //[nori_sc] made it faster has fixed cost but with large angle values (100 for example) starts to lose accuracy floating point problem - // 1 call teaks ~20-30 ns with anny value - var a = angle - Mathf.Floor(angle / Mathf.TwoPi) * Mathf.TwoPi; //loop funcion betwine 0 and TwoPi - return a > Mathf.Pi ? (a - Mathf.TwoPi) : a; // change range so it become Pi and -Pi + var a = angle - (float)Math.Floor(angle / TwoPi) * TwoPi; // Loop function between 0 and TwoPi + return a > Pi ? a - TwoPi : a; // Change range so it become Pi and -Pi } + /// - /// the same as but is more computation intensive with large and has better accuracy with large - ///
cost of this funcion is %
+ /// The same as but is more computation intensive with large and has better accuracy with large . + ///
cost of this function is %
///
/// Angle in radians to unwind. /// Valid angle in radians. public static float UnwindRadiansAccurate(float angle) { while (angle > Pi) - { angle -= TwoPi; - } while (angle < -Pi) - { angle += TwoPi; - } return angle; } + /// - /// Utility to ensure angle is between +/- 180 degrees by unwinding + /// Utility to ensure angle is between +/- 180 degrees by unwinding. /// + /// Optimized version of that is it faster and has fixed cost but with large angle values (100 for example) starts to lose accuracy floating point problem. /// Angle in degrees to unwind. /// Valid angle in degrees. public static float UnwindDegrees(float angle) { - //[nori_sc] made it faster for large values has fixed cost but with large angle values (1 000 000 for example) starts to lose accuracy floating point problem - // 1 call teaks ~20 ns with anny value - var a = angle - Floor(angle / 360.0f) * 360.0f; //loop funcion betwine 0 and 360 - return a > 180 ? (a - 360.0f) : a; // change range so it become 180 and -180 + var a = angle - (float)Math.Floor(angle / 360.0f) * 360.0f; // Loop function between 0 and 360 + return a > 180 ? a - 360.0f : a; // Change range so it become 180 and -180 } + /// - /// the same as but is more computation intensive with large and has better accuracy with large - ///
cost of this funcion is % 180.0f
+ /// The same as but is more computation intensive with large and has better accuracy with large . + ///
cost of this function is % 180.0f
///
/// Angle in radians to unwind. /// Valid angle in radians. public static float UnwindDegreesAccurate(float angle) { while (angle > 180.0f) - { angle -= 360.0f; - } while (angle < -180.0f) - { angle += 360.0f; - } return angle; } + /// /// Clamps the specified value. /// From 77daf85fc1855b32728b243b4d71d95a3a7b930e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Jul 2023 19:31:43 +0200 Subject: [PATCH 35/52] Add unit test for angles unwind math #1267 --- Source/Engine/Tests/TestMath.cs | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Source/Engine/Tests/TestMath.cs diff --git a/Source/Engine/Tests/TestMath.cs b/Source/Engine/Tests/TestMath.cs new file mode 100644 index 000000000..bc1bc1a8d --- /dev/null +++ b/Source/Engine/Tests/TestMath.cs @@ -0,0 +1,47 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#if FLAX_TESTS +using NUnit.Framework; + +namespace FlaxEngine.Tests +{ + /// + /// Tests for and . + /// + [TestFixture] + public class TestMath + { + /// + /// Test unwinding angles. + /// + [Test] + public void TestUnwind() + { + Assert.AreEqual(0.0f, Mathf.UnwindDegreesAccurate(0.0f)); + Assert.AreEqual(45.0f, Mathf.UnwindDegreesAccurate(45.0f)); + Assert.AreEqual(90.0f, Mathf.UnwindDegreesAccurate(90.0f)); + Assert.AreEqual(180.0f, Mathf.UnwindDegreesAccurate(180.0f)); + Assert.AreEqual(0.0f, Mathf.UnwindDegreesAccurate(360.0f)); + Assert.AreEqual(0.0f, Mathf.UnwindDegrees(0.0f)); + Assert.AreEqual(45.0f, Mathf.UnwindDegrees(45.0f)); + Assert.AreEqual(90.0f, Mathf.UnwindDegrees(90.0f)); + Assert.AreEqual(180.0f, Mathf.UnwindDegrees(180.0f)); + Assert.AreEqual(0.0f, Mathf.UnwindDegrees(360.0f)); + var fError = 0.001f; + var dError = 0.00001; + for (float f = -400.0f; f <= 400.0f; f += 0.1f) + { + var f1 = Mathf.UnwindDegreesAccurate(f); + var f2 = Mathf.UnwindDegrees(f); + if (Mathf.Abs(f1 - f2) >= fError) + throw new Exception($"Failed on angle={f}, {f1} != {f2}"); + var d = (double)f; + var d1 = Mathd.UnwindDegreesAccurate(d); + var d2 = Mathd.UnwindDegrees(d); + if (Mathd.Abs(d1 - d2) >= dError) + throw new Exception($"Failed on angle={d}, {d1} != {d2}"); + } + } + } +} +#endif From 95922bb38b180aaa8345a15ba3d8c5f83acc3d54 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Jul 2023 21:18:22 +0200 Subject: [PATCH 36/52] Fix compilation --- Source/Engine/Tests/TestMath.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Tests/TestMath.cs b/Source/Engine/Tests/TestMath.cs index bc1bc1a8d..b8eda936b 100644 --- a/Source/Engine/Tests/TestMath.cs +++ b/Source/Engine/Tests/TestMath.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #if FLAX_TESTS +using System; using NUnit.Framework; namespace FlaxEngine.Tests From 5cd72c9b24937386bc722d73e66fc2a9ca4668df Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 27 Jul 2023 16:51:44 -0500 Subject: [PATCH 37/52] Fix duplicating actor in scene on shift click if the actor has not been moved --- Source/Editor/Gizmo/TransformGizmoBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index d17913dfd..dd5cc6c76 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -469,7 +469,7 @@ namespace FlaxEditor.Gizmo } // Apply transformation (but to the parents, not whole selection pool) - if (anyValid || (!_isTransforming && Owner.UseDuplicate)) + if (anyValid || (_isTransforming && Owner.UseDuplicate)) { StartTransforming(); LastDelta = new Transform(translationDelta, rotationDelta, scaleDelta); From eb641e142ec87d66229461dcc3eedd65b0a43e08 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Sat, 29 Jul 2023 01:00:07 +0300 Subject: [PATCH 38/52] ShadowOfMordor terrain fix --- Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp index 59704800a..26204a94a 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp @@ -151,6 +151,12 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) auto patch = terrain->GetPatch(entry.AsTerrain.PatchIndex); auto chunk = &patch->Chunks[entry.AsTerrain.ChunkIndex]; auto chunkSize = terrain->GetChunkSize(); + if (!patch->Heightmap) + { + LOG(Error, "Terrain actor {0} is missing heightmap for baking, skipping baking stage.", terrain->GetName()); + _wasStageDone = true; + return; + } const auto heightmap = patch->Heightmap.Get()->GetTexture(); Matrix world; From 8250f92af3c742ced56714db3df4aa440f3ec4a9 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Sat, 29 Jul 2023 13:34:46 +0300 Subject: [PATCH 39/52] Add grid scale to the editor viewport settings --- Source/Editor/Gizmo/GridGizmo.cs | 6 +++--- Source/Editor/Options/ViewportOptions.cs | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Gizmo/GridGizmo.cs b/Source/Editor/Gizmo/GridGizmo.cs index 8b4f49986..d9e080982 100644 --- a/Source/Editor/Gizmo/GridGizmo.cs +++ b/Source/Editor/Gizmo/GridGizmo.cs @@ -49,17 +49,17 @@ namespace FlaxEditor.Gizmo float space, size; if (dst <= 500.0f) { - space = 50; + space = Editor.Instance.Options.Options.Viewport.ViewportGridScale; size = 8000; } else if (dst <= 2000.0f) { - space = 100; + space = Editor.Instance.Options.Options.Viewport.ViewportGridScale * 2; size = 8000; } else { - space = 1000; + space = Editor.Instance.Options.Options.Viewport.ViewportGridScale * 20; size = 100000; } diff --git a/Source/Editor/Options/ViewportOptions.cs b/Source/Editor/Options/ViewportOptions.cs index 495aa85d0..cee63a562 100644 --- a/Source/Editor/Options/ViewportOptions.cs +++ b/Source/Editor/Options/ViewportOptions.cs @@ -59,5 +59,12 @@ namespace FlaxEditor.Options [DefaultValue(false)] [EditorDisplay("Defaults"), EditorOrder(150), Tooltip("Invert the panning direction for the viewport camera.")] public bool DefaultInvertPanning { get; set; } = false; + + /// + /// Scales editor viewport grid. + /// + [DefaultValue(50.0f), Limit(25.0f, 500.0f, 5.0f)] + [EditorDisplay("Defaults"), EditorOrder(160), Tooltip("Scales editor viewport grid.")] + public float ViewportGridScale { get; set; } = 50.0f; } } From 383b21c108a7cf387a7834ee150f8cc2782686cd Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski <118038102+Withaust@users.noreply.github.com> Date: Sat, 29 Jul 2023 18:55:29 +0300 Subject: [PATCH 40/52] Softlock fix --- Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp index 26204a94a..afe384d6c 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp @@ -155,6 +155,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) { LOG(Error, "Terrain actor {0} is missing heightmap for baking, skipping baking stage.", terrain->GetName()); _wasStageDone = true; + scene->EntriesLocker.Unlock(); return; } const auto heightmap = patch->Heightmap.Get()->GetTexture(); From 06250fcb6d1ab6101530aae2a15517ad60cae6b1 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 30 Jul 2023 20:39:45 +0300 Subject: [PATCH 41/52] Implement a generic version of `AssetReferenceAttribute` --- .../Editor/AssetReferenceAttribute.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs index 62040d224..6d6107588 100644 --- a/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs @@ -53,4 +53,21 @@ namespace FlaxEngine UseSmallPicker = useSmallPicker; } } + + /// + /// Specifies a options for an asset reference picker in the editor. Allows to customize view or provide custom value assign policy. + /// + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class AssetReferenceAttribute : AssetReferenceAttribute + { + /// + /// Initializes a new instance of the class for generic type T. + /// + /// True if use asset picker with a smaller height (single line), otherwise will use with full icon. + public AssetReferenceAttribute(bool useSmallPicker = false) + : base(typeof(T), useSmallPicker) + { + } + } } From bf351f71bfe933c7a37b06e438298a84c1ebc27d Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 30 Jul 2023 20:48:18 +0300 Subject: [PATCH 42/52] Guard generic `AssetReferenceAttribute` to work only in .NET 7 --- .../Scripting/Attributes/Editor/AssetReferenceAttribute.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs index 6d6107588..5bb79b81b 100644 --- a/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs @@ -54,6 +54,7 @@ namespace FlaxEngine } } +#if USE_NETCORE /// /// Specifies a options for an asset reference picker in the editor. Allows to customize view or provide custom value assign policy. /// @@ -70,4 +71,5 @@ namespace FlaxEngine { } } +#endif } From 3ba05f52df35d56e809f50237a65abb57cb0aed4 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 30 Jul 2023 21:41:32 +0300 Subject: [PATCH 43/52] Add common .NET SDK preprocessor definitions --- Source/Engine/Scripting/Scripting.Build.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Source/Engine/Scripting/Scripting.Build.cs b/Source/Engine/Scripting/Scripting.Build.cs index 21f4477d0..10fc2aff9 100644 --- a/Source/Engine/Scripting/Scripting.Build.cs +++ b/Source/Engine/Scripting/Scripting.Build.cs @@ -17,10 +17,29 @@ public class Scripting : EngineModule { if (EngineConfiguration.WithDotNet(options)) { + void AddFrameworkDefines(string template, int major, int latestMinor) + { + for (int minor = latestMinor; minor >= 0; minor--) + { + options.ScriptingAPI.Defines.Add(string.Format(template, major, minor)); + options.ScriptingAPI.Defines.Add(string.Format($"{template}_OR_GREATER", major, minor)); + } + } + // .NET options.PrivateDependencies.Add("nethost"); options.ScriptingAPI.Defines.Add("USE_NETCORE"); + // .NET SDK + AddFrameworkDefines("NET{0}_{1}", 7, 0); // "NET7_0" and "NET7_0_OR_GREATER" + AddFrameworkDefines("NET{0}_{1}", 6, 0); + AddFrameworkDefines("NET{0}_{1}", 5, 0); + options.ScriptingAPI.Defines.Add("NET"); + AddFrameworkDefines("NETCOREAPP{0}_{1}", 3, 1); // "NETCOREAPP3_1" and "NETCOREAPP3_1_OR_GREATER" + AddFrameworkDefines("NETCOREAPP{0}_{1}", 2, 2); + AddFrameworkDefines("NETCOREAPP{0}_{1}", 1, 1); + options.ScriptingAPI.Defines.Add("NETCOREAPP"); + if (options.Target is EngineTarget engineTarget && engineTarget.UseSeparateMainExecutable(options)) { // Build target doesn't support linking again main executable (eg. Linux) thus additional shared library is used for the engine (eg. libFlaxEditor.so) From 13e11091fc1504ae63d922e9685e230c5d28481f Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 30 Jul 2023 21:43:28 +0300 Subject: [PATCH 44/52] Support user defined .NET analyzers/source generators in Flax.Build --- .../Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs | 6 ++++-- .../Flax.Build/Build/NativeCpp/BuildOptions.cs | 13 ++++++++++--- .../Projects/VisualStudio/CSSDKProjectGenerator.cs | 8 ++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs index 13433f92c..2cdc57686 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs @@ -272,8 +272,10 @@ namespace Flax.Build foreach (var reference in fileReferences) args.Add(string.Format("/reference:\"{0}\"", reference)); #if USE_NETCORE - foreach (var analyzer in buildOptions.ScriptingAPI.SystemAnalyzers) - args.Add(string.Format("/analyzer:\"{0}{1}.dll\"", referenceAnalyzers, analyzer)); + foreach (var systemAnalyzer in buildOptions.ScriptingAPI.SystemAnalyzers) + args.Add(string.Format("/analyzer:\"{0}{1}.dll\"", referenceAnalyzers, systemAnalyzer)); + foreach (var analyzer in buildOptions.ScriptingAPI.Analyzers) + args.Add(string.Format("/analyzer:\"{0}\"", analyzer)); #endif foreach (var sourceFile in sourceFiles) args.Add("\"" + sourceFile + "\""); diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs b/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs index ef365d3b7..e612c0a95 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs @@ -200,14 +200,19 @@ namespace Flax.Build.NativeCpp public HashSet SystemReferences; /// - /// The .Net libraries references (dll or exe files paths). + /// The system analyzers/source generators. + /// + public HashSet SystemAnalyzers; + + /// + /// The .NET libraries references (dll or exe files paths). /// public HashSet FileReferences; /// - /// The .Net libraries references (dll or exe files paths). + /// The .NET analyzers (dll or exe files paths). /// - public HashSet SystemAnalyzers; + public HashSet Analyzers; /// /// True if ignore compilation warnings due to missing code documentation comments. @@ -232,6 +237,7 @@ namespace Flax.Build.NativeCpp Defines.AddRange(other.Defines); SystemReferences.AddRange(other.SystemReferences); FileReferences.AddRange(other.FileReferences); + Analyzers.AddRange(other.Analyzers); IgnoreMissingDocumentationWarnings |= other.IgnoreMissingDocumentationWarnings; } } @@ -305,6 +311,7 @@ namespace Flax.Build.NativeCpp "Microsoft.Interop.SourceGeneration", }, FileReferences = new HashSet(), + Analyzers = new HashSet(), }; /// diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs index efce04776..5fba0dc43 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs @@ -247,6 +247,14 @@ namespace Flax.Build.Projects.VisualStudio csProjectFileContent.AppendLine(" "); } } + foreach (var analyzer in configuration.TargetBuildOptions.ScriptingAPI.Analyzers) + { + csProjectFileContent.AppendLine(string.Format(" ", configuration.Name)); + csProjectFileContent.AppendLine(string.Format(" ", Path.GetFileNameWithoutExtension(analyzer))); + csProjectFileContent.AppendLine(string.Format(" {0}", Utilities.MakePathRelativeTo(analyzer, projectDirectory).Replace('/', '\\'))); + csProjectFileContent.AppendLine(" "); + csProjectFileContent.AppendLine(" "); + } csProjectFileContent.AppendLine(""); } From 735b2e30f00c29aec9ad3906a4bf4fc17dcbc41b Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 30 Jul 2023 21:55:23 +0300 Subject: [PATCH 45/52] Output generated .NET source generator files to Intermediate folder Mostly useful for debugging source generators, VS doesn't seem to utilize these files in any way. --- Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs index 2cdc57686..d6772379f 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs @@ -159,6 +159,7 @@ namespace Flax.Build var outputPath = Path.GetDirectoryName(buildData.Target.GetOutputFilePath(buildOptions)); var outputFile = Path.Combine(outputPath, name + ".dll"); var outputDocFile = Path.Combine(outputPath, name + ".xml"); + var outputGeneratedFiles = Path.Combine(buildOptions.IntermediateFolder); string cscPath, referenceAssemblies; #if USE_NETCORE var dotnetSdk = DotNetSdk.Instance; @@ -263,6 +264,9 @@ namespace Flax.Build #endif args.Add(string.Format("/out:\"{0}\"", outputFile)); args.Add(string.Format("/doc:\"{0}\"", outputDocFile)); +#if USE_NETCORE + args.Add(string.Format("/generatedfilesout:\"{0}\"", outputGeneratedFiles)); +#endif if (buildOptions.ScriptingAPI.Defines.Count != 0) args.Add("/define:" + string.Join(";", buildOptions.ScriptingAPI.Defines)); if (buildData.Configuration == TargetConfiguration.Debug) From 71bce781f7de08e31237b5daa503f2bf9c0b1d10 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 31 Jul 2023 17:24:47 +0200 Subject: [PATCH 46/52] Force disable flot128 in fmt --- Source/ThirdParty/fmt/core.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/ThirdParty/fmt/core.h b/Source/ThirdParty/fmt/core.h index a8b208508..31049d410 100644 --- a/Source/ThirdParty/fmt/core.h +++ b/Source/ThirdParty/fmt/core.h @@ -30,6 +30,7 @@ Customizations done to fmt lib: #define FMT_USE_USER_DEFINED_LITERALS 0 #define FMT_USE_STRING 0 #define FMT_USE_LONG_DOUBLE 0 +#define FMT_USE_FLOAT128 0 #define FMT_USE_ITERATOR 0 #define FMT_USE_LOCALE_GROUPING 0 #define FMT_EXCEPTIONS 0 From 81860e5f189de65f0ca23e954fdf22dee83f40b0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 31 Jul 2023 19:02:53 +0200 Subject: [PATCH 47/52] Cleanup code #1215 and add cook game icon --- Content/Editor/IconsAtlas.flax | 4 +- Source/Editor/EditorIcons.cs | 1 + .../ContextMenuSingleSelectGroup.cs | 104 ++++++-------- Source/Editor/GUI/ToolStrip.cs | 18 +-- Source/Editor/GUI/ToolStripButton.cs | 43 ++---- Source/Editor/Modules/UIModule.cs | 93 ++++++------- Source/Editor/Options/InterfaceOptions.cs | 12 +- Source/Editor/Windows/GameCookerWindow.cs | 130 ++++++++++-------- .../Editor/Windows/Profiler/ProfilerWindow.cs | 2 +- 9 files changed, 186 insertions(+), 221 deletions(-) diff --git a/Content/Editor/IconsAtlas.flax b/Content/Editor/IconsAtlas.flax index 3318b811b..ae46fcf06 100644 --- a/Content/Editor/IconsAtlas.flax +++ b/Content/Editor/IconsAtlas.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d867a994701f09964335ddf51cd4060a54728b9de4420ad3f0bfbd8847bc7602 -size 5611659 +oid sha256:745ce418c493445abde98aea91a34592b00c5c46b68cd7e61604a05b3043c76c +size 5608587 diff --git a/Source/Editor/EditorIcons.cs b/Source/Editor/EditorIcons.cs index 127ba3f6f..fb3c46a41 100644 --- a/Source/Editor/EditorIcons.cs +++ b/Source/Editor/EditorIcons.cs @@ -96,6 +96,7 @@ namespace FlaxEditor public SpriteHandle Link64; public SpriteHandle Build64; public SpriteHandle Add64; + public SpriteHandle ShipIt64; // 96px public SpriteHandle Toolbox96; diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs index 9b58c09a8..5abb52b4a 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs @@ -2,106 +2,92 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEngine; -using FlaxEngine.GUI; namespace FlaxEditor.GUI.ContextMenu { /// - /// Context menu single select group. + /// Context menu for a single selectable option from range of values (eg. enum). /// [HideInEditor] class ContextMenuSingleSelectGroup { - public struct SingleSelectGroupItem + private struct SingleSelectGroupItem { - public string text; - public U value; - public Action onSelected; - public List buttons; + public string Text; + public string Tooltip; + public T Value; + public Action Selected; + public List Buttons; } private List _menus = new List(); - private List> _items = new List>(); - public Action OnSelectionChanged; + private List _items = new List(); + private SingleSelectGroupItem _selectedItem; - public SingleSelectGroupItem activeItem; - - public ContextMenuSingleSelectGroup AddItem(string text, T value, Action onSelected = null) + public T Selected { - var item = new SingleSelectGroupItem + get => _selectedItem.Value; + set { - text = text, - value = value, - onSelected = onSelected, - buttons = new List() + var index = _items.FindIndex(x => x.Value.Equals(value)); + if (index != -1 && !_selectedItem.Value.Equals(value)) + { + SetSelected(_items[index]); + } + } + } + + public Action SelectedChanged; + + public ContextMenuSingleSelectGroup AddItem(string text, T value, Action selected = null, string tooltip = null) + { + var item = new SingleSelectGroupItem + { + Text = text, + Tooltip = tooltip, + Value = value, + Selected = selected, + Buttons = new List() }; _items.Add(item); - foreach (var contextMenu in _menus) AddItemToContextMenu(contextMenu, item); - return this; } public ContextMenuSingleSelectGroup AddItemsToContextMenu(ContextMenu contextMenu) { _menus.Add(contextMenu); - for (int i = 0; i < _items.Count; i++) - { AddItemToContextMenu(contextMenu, _items[i]); - } - return this; } - private void AddItemToContextMenu(ContextMenu contextMenu, SingleSelectGroupItem item) + private void AddItemToContextMenu(ContextMenu contextMenu, SingleSelectGroupItem item) { - var btn = contextMenu.AddButton(item.text, () => - { - SetItemAsActive(item); - }); - - item.buttons.Add(btn); - - if (item.Equals(activeItem)) - { + var btn = contextMenu.AddButton(item.Text, () => { SetSelected(item); }); + if (item.Tooltip != null) + btn.TooltipText = item.Tooltip; + item.Buttons.Add(btn); + if (item.Equals(_selectedItem)) btn.Checked = true; - } } - private void DeselectAll() + private void SetSelected(SingleSelectGroupItem item) { - foreach (var item in _items) + foreach (var e in _items) { - foreach (var btn in item.buttons) btn.Checked = false; + foreach (var btn in e.Buttons) + btn.Checked = false; } - } + _selectedItem = item; - public void SetItemAsActive(T value) - { - var index = _items.FindIndex(x => x.value.Equals(value)); + SelectedChanged?.Invoke(item.Value); + item.Selected?.Invoke(); - if (index == -1) return; - - SetItemAsActive(_items[index]); - } - - private void SetItemAsActive(SingleSelectGroupItem item) - { - DeselectAll(); - activeItem = item; - - var index = _items.IndexOf(item); - OnSelectionChanged?.Invoke(item.value); - item.onSelected?.Invoke(); - - foreach (var btn in item.buttons) - { + foreach (var btn in item.Buttons) btn.Checked = true; - } } } } diff --git a/Source/Editor/GUI/ToolStrip.cs b/Source/Editor/GUI/ToolStrip.cs index 858e21b51..0dac241ed 100644 --- a/Source/Editor/GUI/ToolStrip.cs +++ b/Source/Editor/GUI/ToolStrip.cs @@ -25,12 +25,12 @@ namespace FlaxEditor.GUI /// /// Event fired when button gets clicked with the primary mouse button. /// - public Action ButtonPrimaryClicked; + public Action ButtonClicked; /// /// Event fired when button gets clicked with the secondary mouse button. /// - public Action ButtonSecondaryClicked; + public Action SecondaryButtonClicked; /// /// Tries to get the last button. @@ -96,7 +96,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.PrimaryClicked += onClick; + button.Clicked += onClick; return button; } @@ -115,7 +115,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.PrimaryClicked += onClick; + button.Clicked += onClick; return button; } @@ -133,7 +133,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.PrimaryClicked += onClick; + button.Clicked += onClick; return button; } @@ -146,14 +146,14 @@ namespace FlaxEditor.GUI return AddChild(new ToolStripSeparator(ItemsHeight)); } - internal void OnButtonPrimaryClicked(ToolStripButton button) + internal void OnButtonClicked(ToolStripButton button) { - ButtonPrimaryClicked?.Invoke(button); + ButtonClicked?.Invoke(button); } - internal void OnButtonSecondaryClicked(ToolStripButton button) + internal void OnSecondaryButtonClicked(ToolStripButton button) { - ButtonSecondaryClicked?.Invoke(button); + SecondaryButtonClicked?.Invoke(button); } /// diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index f8d032696..e839a7356 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -11,12 +11,8 @@ namespace FlaxEditor.GUI /// /// [HideInEditor] - public partial class ToolStripButton : Control + public class ToolStripButton : Control { - // TODO: abstracted for potential future in-editor input configuration (ex. for left handed mouse users) - private const MouseButton PRIMARY_MOUSE_BUTTON = MouseButton.Left; - private const MouseButton SECONDARY_MOUSE_BUTTON = MouseButton.Right; - /// /// The default margin for button parts (icon, text, etc.). /// @@ -30,7 +26,7 @@ namespace FlaxEditor.GUI /// /// Event fired when user clicks the button. /// - public Action PrimaryClicked; + public Action Clicked; /// /// Event fired when user clicks the button. @@ -140,13 +136,7 @@ namespace FlaxEditor.GUI if (!string.IsNullOrEmpty(_text)) { textRect.Size.X = Width - DefaultMargin - textRect.Left; - Render2D.DrawText( - style.FontMedium, - _text, - textRect, - enabled ? style.Foreground : style.ForegroundDisabled, - TextAlignment.Near, - TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _text, textRect, enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } } @@ -169,19 +159,15 @@ namespace FlaxEditor.GUI /// public override bool OnMouseDown(Float2 location, MouseButton button) { - if (button == PRIMARY_MOUSE_BUTTON) + if (button == MouseButton.Left) { - // Set flag _primaryMouseDown = true; - Focus(); return true; } - else if (button == SECONDARY_MOUSE_BUTTON) + if (button == MouseButton.Right) { - // Set flag _secondaryMouseDown = true; - Focus(); return true; } @@ -192,29 +178,22 @@ namespace FlaxEditor.GUI /// public override bool OnMouseUp(Float2 location, MouseButton button) { - if (button == PRIMARY_MOUSE_BUTTON && _primaryMouseDown) + if (button == MouseButton.Left && _primaryMouseDown) { - // Clear flag _primaryMouseDown = false; - - // Fire events if (AutoCheck) Checked = !Checked; - PrimaryClicked?.Invoke(); - (Parent as ToolStrip)?.OnButtonPrimaryClicked(this); + Clicked?.Invoke(); + (Parent as ToolStrip)?.OnButtonClicked(this); return true; } - else if (button == SECONDARY_MOUSE_BUTTON && _secondaryMouseDown) + if (button == MouseButton.Right && _secondaryMouseDown) { - // Clear flag _secondaryMouseDown = false; - SecondaryClicked?.Invoke(); - (Parent as ToolStrip)?.OnButtonSecondaryClicked(this); - + (Parent as ToolStrip)?.OnSecondaryButtonClicked(this); ContextMenu?.Show(this, new Float2(0, Height)); - return true; } @@ -224,7 +203,6 @@ namespace FlaxEditor.GUI /// public override void OnMouseLeave() { - // Clear flag _primaryMouseDown = false; _secondaryMouseDown = false; @@ -234,7 +212,6 @@ namespace FlaxEditor.GUI /// public override void OnLostFocus() { - // Clear flag _primaryMouseDown = false; _secondaryMouseDown = false; diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 5e0af20fe..38c61942a 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -21,7 +21,7 @@ using FlaxEngine.Json; using DockHintWindow = FlaxEditor.GUI.Docking.DockHintWindow; using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel; using FlaxEditor.Content.Settings; -using static FlaxEditor.Options.InterfaceOptions; +using FlaxEditor.Options; namespace FlaxEditor.Modules { @@ -203,6 +203,7 @@ namespace FlaxEditor.Modules _toolStripScale.Checked = gizmoMode == TransformGizmoBase.Mode.Scale; // _toolStripBuildScenes.Enabled = (canEditScene && !isPlayMode) || Editor.StateMachine.BuildingScenesState.IsActive; + _toolStripCook.Enabled = Editor.Windows.GameCookerWin.CanBuild(Platform.PlatformType) && !GameCooker.IsRunning; // var play = _toolStripPlay; var pause = _toolStripPause; @@ -418,6 +419,7 @@ namespace FlaxEditor.Modules Editor.Undo.UndoDone += OnUndoEvent; Editor.Undo.RedoDone += OnUndoEvent; Editor.Undo.ActionDone += OnUndoEvent; + GameCooker.Event += OnGameCookerEvent; UpdateToolstrip(); } @@ -433,6 +435,11 @@ namespace FlaxEditor.Modules UpdateStatusBar(); } + private void OnGameCookerEvent(GameCooker.EventType type) + { + UpdateToolstrip(); + } + /// public override void OnExit() { @@ -477,23 +484,18 @@ namespace FlaxEditor.Modules private void InitSharedMenus() { - for (int i = 1; i <= 4; i++) _numberOfClientsGroup.AddItem(i.ToString(), i); + for (int i = 1; i <= 4; i++) + _numberOfClientsGroup.AddItem(i.ToString(), i); - _numberOfClientsGroup.SetItemAsActive(Editor.Options.Options.Interface.NumberOfGameClientsToLaunch); - _numberOfClientsGroup.OnSelectionChanged = (value) => + _numberOfClientsGroup.Selected = Editor.Options.Options.Interface.NumberOfGameClientsToLaunch; + _numberOfClientsGroup.SelectedChanged = value => { var options = Editor.Options.Options; options.Interface.NumberOfGameClientsToLaunch = value; Editor.Options.Apply(options); }; - Editor.Options.OptionsChanged += (options) => - { - if (options.Interface.NumberOfGameClientsToLaunch != _numberOfClientsGroup.activeItem.value) - { - _numberOfClientsGroup.SetItemAsActive(options.Interface.NumberOfGameClientsToLaunch); - } - }; + Editor.Options.OptionsChanged += options => { _numberOfClientsGroup.Selected = options.Interface.NumberOfGameClientsToLaunch; }; } private void InitMainMenu(RootControl mainWindow) @@ -665,43 +667,33 @@ namespace FlaxEditor.Modules _toolStripRotate = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Rotate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip("Change Gizmo tool mode to Rotate (2)"); _toolStripScale = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip("Change Gizmo tool mode to Scale (3)"); ToolStrip.AddSeparator(); - _toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options (Ctrl+F10)"); - ToolStrip.AddSeparator(); - // build - _toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.BuildSettings128, CookAndRun).LinkTooltip("Cook and run"); + // Cook scenes + _toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options (Ctrl+F10)"); + + // Cook and run + _toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, CookAndRun).LinkTooltip("Cook & Run - build game for the current platform and run it locally"); _toolStripCook.ContextMenu = new ContextMenu(); _toolStripCook.ContextMenu.AddButton("Run cooked game", RunCookedGame); _toolStripCook.ContextMenu.AddSeparator(); var numberOfClientsMenu = _toolStripCook.ContextMenu.AddChildMenu("Number of game clients"); _numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu); - // play + ToolStrip.AddSeparator(); + + // Play _toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play64, OnPlayPressed).LinkTooltip("Play Game"); _toolStripPlay.ContextMenu = new ContextMenu(); - ContextMenuChildMenu playSubMenu; - - // play - button action - playSubMenu = _toolStripPlay.ContextMenu.AddChildMenu("Play button action"); - var playActionGroup = new ContextMenuSingleSelectGroup(); - playActionGroup.AddItem("Play Game", PlayAction.PlayGame); - playActionGroup.AddItem("Play Scenes", PlayAction.PlayScenes); + var playSubMenu = _toolStripPlay.ContextMenu.AddChildMenu("Play button action"); + var playActionGroup = new ContextMenuSingleSelectGroup(); + playActionGroup.AddItem("Play Game", InterfaceOptions.PlayAction.PlayGame, null, "Launches the game from the First Scene defined in the project settings."); + playActionGroup.AddItem("Play Scenes", InterfaceOptions.PlayAction.PlayScenes, null, "Launches the game using the scenes currently loaded in the editor."); playActionGroup.AddItemsToContextMenu(playSubMenu.ContextMenu); - playActionGroup.SetItemAsActive(Editor.Options.Options.Interface.PlayButtonAction); - // important to add the handler after setting the initial item as active above or the editor will crash - playActionGroup.OnSelectionChanged = SetPlayAction; - // TODO: there is a hole in the syncing of these values: - // - when changing in the editor, the options will be updated, but the options UI in-editor will not + playActionGroup.Selected = Editor.Options.Options.Interface.PlayButtonAction; + playActionGroup.SelectedChanged = SetPlayAction; + Editor.Options.OptionsChanged += options => { playActionGroup.Selected = options.Interface.PlayButtonAction; }; - Editor.Options.OptionsChanged += (options) => - { - if (options.Interface.PlayButtonAction != playActionGroup.activeItem.value) - { - playActionGroup.SetItemAsActive(options.Interface.PlayButtonAction); - } - }; - - _toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game({inputOptions.Pause.ToString()})"); + _toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game({inputOptions.Pause})"); _toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game"); UpdateToolstrip(); @@ -736,10 +728,7 @@ namespace FlaxEditor.Modules var defaultTextColor = StatusBar.TextColor; _outputLogButton.HoverBegin += () => StatusBar.TextColor = Style.Current.BackgroundSelected; _outputLogButton.HoverEnd += () => StatusBar.TextColor = defaultTextColor; - _outputLogButton.Clicked += () => - { - Editor.Windows.OutputLogWin.FocusOrShow(); - }; + _outputLogButton.Clicked += () => { Editor.Windows.OutputLogWin.FocusOrShow(); }; // Progress bar with label const float progressBarWidth = 120.0f; @@ -1043,7 +1032,7 @@ namespace FlaxEditor.Modules projectInfo.Save(); } - private void SetPlayAction(PlayAction newPlayAction) + private void SetPlayAction(InterfaceOptions.PlayAction newPlayAction) { var options = Editor.Options.Options; options.Interface.PlayButtonAction = newPlayAction; @@ -1054,23 +1043,25 @@ namespace FlaxEditor.Modules { switch (Editor.Options.Options.Interface.PlayButtonAction) { - case PlayAction.PlayGame: - if (Editor.IsPlayMode) - Editor.Simulation.RequestStopPlay(); - else - PlayGame(); - return; - case PlayAction.PlayScenes: PlayScenes(); return; + case InterfaceOptions.PlayAction.PlayGame: + if (Editor.IsPlayMode) + Editor.Simulation.RequestStopPlay(); + else + PlayGame(); + return; + case InterfaceOptions.PlayAction.PlayScenes: + PlayScenes(); + return; } } private void PlayGame() { var firstScene = GameSettings.Load().FirstScene; - if (firstScene == Guid.Empty) { - if (Level.IsAnySceneLoaded) Editor.Simulation.RequestStartPlay(); + if (Level.IsAnySceneLoaded) + Editor.Simulation.RequestStartPlay(); return; } diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 7d97fecd6..5fd8c31c7 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -83,10 +83,11 @@ namespace FlaxEditor.Options /// Launches the game from the First Scene defined in the project settings. /// PlayGame, + /// /// Launches the game using the scenes currently loaded in the editor. /// - PlayScenes + PlayScenes, } /// @@ -97,14 +98,12 @@ namespace FlaxEditor.Options public float InterfaceScale { get; set; } = 1.0f; #if PLATFORM_WINDOWS - /// /// Gets or sets a value indicating whether use native window title bar. Editor restart required. /// [DefaultValue(false)] [EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar. Editor restart required.")] public bool UseNativeWindowSystem { get; set; } = false; - #endif /// @@ -215,15 +214,14 @@ namespace FlaxEditor.Options /// Gets or sets a value indicating what action should be taken upon pressing the play button. /// [DefaultValue(PlayAction.PlayScenes)] - [EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(410), Tooltip("Determines the action taken when the play button is pressed.")] + [EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(410)] public PlayAction PlayButtonAction { get; set; } = PlayAction.PlayScenes; /// /// Gets or sets a value indicating the number of game clients to launch when building and/or running cooked game. /// - [DefaultValue(1)] - [EditorDisplay("Cook & Run", "Number Of GameClients To Launch"), EditorOrder(500), Tooltip("Determines the number of game clients to launch when building and/or running cooked game.")] - [Range(1, 4)] + [DefaultValue(1), Range(1, 4)] + [EditorDisplay("Cook & Run"), EditorOrder(500)] public int NumberOfGameClientsToLaunch = 1; private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index f88fa36f5..5ece067a0 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -110,14 +110,14 @@ namespace FlaxEditor.Windows #if PLATFORM_WINDOWS switch (BuildPlatform) { - case BuildPlatform.MacOSx64: - case BuildPlatform.MacOSARM64: - case BuildPlatform.iOSARM64: - IsSupported = false; - break; - default: - IsSupported = true; - break; + case BuildPlatform.MacOSx64: + case BuildPlatform.MacOSARM64: + case BuildPlatform.iOSARM64: + IsSupported = false; + break; + default: + IsSupported = true; + break; } #elif PLATFORM_LINUX switch (BuildPlatform) @@ -160,17 +160,17 @@ namespace FlaxEditor.Windows { switch (BuildPlatform) { - case BuildPlatform.Windows32: - case BuildPlatform.Windows64: - case BuildPlatform.UWPx86: - case BuildPlatform.UWPx64: - case BuildPlatform.LinuxX64: - case BuildPlatform.AndroidARM64: - layout.Label("Use Flax Launcher and download the required package.", TextAlignment.Center); - break; - default: - layout.Label("Engine source is required to target this platform.", TextAlignment.Center); - break; + case BuildPlatform.Windows32: + case BuildPlatform.Windows64: + case BuildPlatform.UWPx86: + case BuildPlatform.UWPx64: + case BuildPlatform.LinuxX64: + case BuildPlatform.AndroidARM64: + layout.Label("Use Flax Launcher and download the required package.", TextAlignment.Center); + break; + default: + layout.Label("Engine source is required to target this platform.", TextAlignment.Center); + break; } } else @@ -238,6 +238,7 @@ namespace FlaxEditor.Windows { [EditorDisplay(null, "arm64")] ARM64, + [EditorDisplay(null, "x64")] x64, } @@ -272,43 +273,43 @@ namespace FlaxEditor.Windows string name; switch (_platform) { - case PlatformType.Windows: - name = "Windows"; - break; - case PlatformType.XboxOne: - name = "Xbox One"; - break; - case PlatformType.UWP: - name = "Windows Store"; - layout.Label("UWP (Windows Store) platform has been deprecated and is no longer supported", TextAlignment.Center).Label.TextColor = Color.Red; - break; - case PlatformType.Linux: - name = "Linux"; - break; - case PlatformType.PS4: - name = "PlayStation 4"; - break; - case PlatformType.XboxScarlett: - name = "Xbox Scarlett"; - break; - case PlatformType.Android: - name = "Android"; - break; - case PlatformType.Switch: - name = "Switch"; - break; - case PlatformType.PS5: - name = "PlayStation 5"; - break; - case PlatformType.Mac: - name = "Mac"; - break; - case PlatformType.iOS: - name = "iOS"; - break; - default: - name = Utilities.Utils.GetPropertyNameUI(_platform.ToString()); - break; + case PlatformType.Windows: + name = "Windows"; + break; + case PlatformType.XboxOne: + name = "Xbox One"; + break; + case PlatformType.UWP: + name = "Windows Store"; + layout.Label("UWP (Windows Store) platform has been deprecated and is no longer supported", TextAlignment.Center).Label.TextColor = Color.Red; + break; + case PlatformType.Linux: + name = "Linux"; + break; + case PlatformType.PS4: + name = "PlayStation 4"; + break; + case PlatformType.XboxScarlett: + name = "Xbox Scarlett"; + break; + case PlatformType.Android: + name = "Android"; + break; + case PlatformType.Switch: + name = "Switch"; + break; + case PlatformType.PS5: + name = "PlayStation 5"; + break; + case PlatformType.Mac: + name = "Mac"; + break; + case PlatformType.iOS: + name = "iOS"; + break; + default: + name = Utilities.Utils.GetPropertyNameUI(_platform.ToString()); + break; } var group = layout.Group(name); @@ -614,6 +615,18 @@ namespace FlaxEditor.Windows _exitOnBuildEnd = true; } + /// + /// Returns true if can build for the given platform (both supported and available). + /// + /// The platform. + /// True if can build, otherwise false. + public bool CanBuild(PlatformType platformType) + { + if (_buildTabProxy.PerPlatformOptions.TryGetValue(platformType, out var platform)) + return platform.IsAvailable && platform.IsSupported; + return false; + } + /// /// Builds all the targets from the given preset. /// @@ -660,11 +673,11 @@ namespace FlaxEditor.Windows Editor.Log("Building and running"); GameCooker.GetCurrentPlatform(out var platform, out var buildPlatform, out var buildConfiguration); var numberOfClients = Editor.Options.Options.Interface.NumberOfGameClientsToLaunch; - for (int i = 0; i < numberOfClients; i++) { var buildOptions = BuildOptions.AutoRun; - if (i > 0) buildOptions |= BuildOptions.NoCook; + if (i > 0) + buildOptions |= BuildOptions.NoCook; _buildingQueue.Enqueue(new QueueItem { @@ -687,7 +700,6 @@ namespace FlaxEditor.Windows Editor.Log("Running cooked build"); GameCooker.GetCurrentPlatform(out var platform, out var buildPlatform, out var buildConfiguration); var numberOfClients = Editor.Options.Options.Interface.NumberOfGameClientsToLaunch; - for (int i = 0; i < numberOfClients; i++) { _buildingQueue.Enqueue(new QueueItem diff --git a/Source/Editor/Windows/Profiler/ProfilerWindow.cs b/Source/Editor/Windows/Profiler/ProfilerWindow.cs index b35641bf6..f5a5c6f86 100644 --- a/Source/Editor/Windows/Profiler/ProfilerWindow.cs +++ b/Source/Editor/Windows/Profiler/ProfilerWindow.cs @@ -93,7 +93,7 @@ namespace FlaxEditor.Windows.Profiler _liveRecordingButton = toolstrip.AddButton(editor.Icons.Play64); _liveRecordingButton.LinkTooltip("Live profiling events recording"); _liveRecordingButton.AutoCheck = true; - _liveRecordingButton.PrimaryClicked += () => _liveRecordingButton.Icon = LiveRecording ? editor.Icons.Stop64 : editor.Icons.Play64; + _liveRecordingButton.Clicked += () => _liveRecordingButton.Icon = LiveRecording ? editor.Icons.Stop64 : editor.Icons.Play64; _clearButton = toolstrip.AddButton(editor.Icons.Rotate32, Clear); _clearButton.LinkTooltip("Clear data"); toolstrip.AddSeparator(); From 600659e4536e3cc6932f52358f93ce0e9ec8b16f Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:34:50 -0400 Subject: [PATCH 48/52] Move DrawSpline from Spline.cpp to SplineNode.cs --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 29 ++++++++++++ Source/Engine/Level/Actors/Spline.cpp | 47 ------------------- Source/Engine/Level/Actors/Spline.h | 4 -- 3 files changed, 29 insertions(+), 51 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index fe96ad4e8..24ea60f21 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -415,6 +415,35 @@ namespace FlaxEditor.SceneGraph.Actors return distance * nodeSize; } + public override void OnDebugDraw(ViewportDebugDrawData data) + { + DrawSpline((Spline)Actor, Color.White, Actor.Transform, false); + } + + private void DrawSpline(Spline spline, Color color, Transform transform, bool depthTest) + { + var count = spline.SplineKeyframes.Length; + if (count == 0) + return; + var keyframes = spline.SplineKeyframes; + var pointIndex = 0; + var prev = spline.GetSplineKeyframe(0); + + Vector3 prevPos = transform.LocalToWorld(prev.Value.Translation); + DebugDraw.DrawWireSphere(new BoundingSphere(prevPos, 5.0f), color, 0.0f, depthTest); + for (int i = 1; i < count; i++) + { + var next = keyframes[pointIndex]; + Vector3 nextPos = transform.LocalToWorld(next.Value.Translation); + DebugDraw.DrawWireSphere(new BoundingSphere(nextPos, 5.0f), color, 0.0f, depthTest); + var d = (next.Time - prev.Time) / 3.0f; + DebugDraw.DrawBezier(prevPos, prevPos + prev.TangentOut.Translation * d, nextPos + next.TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); + prev = next; + prevPos = nextPos; + pointIndex++; + } + } + /// public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal) { diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index dbd7ef045..2923463b0 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -455,53 +455,6 @@ void Spline::SetKeyframes(MArray* data) #endif -#if USE_EDITOR - -#include "Engine/Debug/DebugDraw.h" - -namespace -{ - void DrawSpline(Spline* spline, const Color& color, const Transform& transform, bool depthTest) - { - const int32 count = spline->Curve.GetKeyframes().Count(); - if (count == 0) - return; - Spline::Keyframe* prev = spline->Curve.GetKeyframes().Get(); - Vector3 prevPos = transform.LocalToWorld(prev->Value.Translation); - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(prevPos, 5.0f), color, 0.0f, depthTest); - for (int32 i = 1; i < count; i++) - { - Spline::Keyframe* next = prev + 1; - Vector3 nextPos = transform.LocalToWorld(next->Value.Translation); - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(nextPos, 5.0f), color, 0.0f, depthTest); - const float d = (next->Time - prev->Time) / 3.0f; - DEBUG_DRAW_BEZIER(prevPos, prevPos + prev->TangentOut.Translation * d, nextPos + next->TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); - prev = next; - prevPos = nextPos; - } - } -} - -void Spline::OnDebugDraw() -{ - const Color color = GetSplineColor(); - DrawSpline(this, color.AlphaMultiplied(0.7f), _transform, true); - - // Base - Actor::OnDebugDraw(); -} - -void Spline::OnDebugDrawSelected() -{ - const Color color = GetSplineColor(); - DrawSpline(this, color.AlphaMultiplied(0.3f), _transform, false); - - // Base - Actor::OnDebugDrawSelected(); -} - -#endif - void Spline::OnTransformChanged() { // Base diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index ea3cf8569..426304fb9 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -369,10 +369,6 @@ private: public: // [Actor] -#if USE_EDITOR - void OnDebugDraw() override; - void OnDebugDrawSelected() override; -#endif void OnTransformChanged() override; void Initialize() override; void Serialize(SerializeStream& stream, const void* otherObj) override; From f511259a33be31ee45c9bac00823bacb971c54df Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:43:33 -0400 Subject: [PATCH 49/52] draw spline point size by distance from camera --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 24ea60f21..cbc8911a8 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -417,7 +417,7 @@ namespace FlaxEditor.SceneGraph.Actors public override void OnDebugDraw(ViewportDebugDrawData data) { - DrawSpline((Spline)Actor, Color.White, Actor.Transform, false); + DrawSpline((Spline)Actor, Color.White, Actor.Transform, true); } private void DrawSpline(Spline spline, Color color, Transform transform, bool depthTest) @@ -428,15 +428,16 @@ namespace FlaxEditor.SceneGraph.Actors var keyframes = spline.SplineKeyframes; var pointIndex = 0; var prev = spline.GetSplineKeyframe(0); - - Vector3 prevPos = transform.LocalToWorld(prev.Value.Translation); - DebugDraw.DrawWireSphere(new BoundingSphere(prevPos, 5.0f), color, 0.0f, depthTest); - for (int i = 1; i < count; i++) + var prevPos = transform.LocalToWorld(prev.Value.Translation); + var pointSize = NodeSizeByDistance(spline.GetSplinePoint(0), PointNodeSize); + DebugDraw.DrawWireSphere(new BoundingSphere(prevPos, pointSize), color, 0.0f, depthTest); + for (int i = 0; i < count; i++) { var next = keyframes[pointIndex]; - Vector3 nextPos = transform.LocalToWorld(next.Value.Translation); - DebugDraw.DrawWireSphere(new BoundingSphere(nextPos, 5.0f), color, 0.0f, depthTest); + var nextPos = transform.LocalToWorld(next.Value.Translation); var d = (next.Time - prev.Time) / 3.0f; + pointSize = NodeSizeByDistance(spline.GetSplinePoint(i), PointNodeSize); + DebugDraw.DrawWireSphere(new BoundingSphere(nextPos, pointSize), color, 0.0f, depthTest); DebugDraw.DrawBezier(prevPos, prevPos + prev.TangentOut.Translation * d, nextPos + next.TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); prev = next; prevPos = nextPos; From cfab58ccc69968202577ec74e7a82deb43dcb497 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Aug 2023 09:57:38 +0200 Subject: [PATCH 50/52] Fix some actor assets into soft checks --- Source/Engine/Level/Actor.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 08fb77223..8185f3b9d 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -788,7 +788,9 @@ void Actor::BreakPrefabLink() void Actor::Initialize() { - ASSERT(!IsDuringPlay()); +#if ENABLE_ASSERTION + CHECK(!IsDuringPlay()); +#endif // Cache if (_parent) @@ -802,7 +804,9 @@ void Actor::Initialize() void Actor::BeginPlay(SceneBeginData* data) { - ASSERT(!IsDuringPlay()); +#if ENABLE_ASSERTION + CHECK(!IsDuringPlay()); +#endif // Set flag Flags |= ObjectFlags::IsDuringPlay; @@ -832,7 +836,9 @@ void Actor::BeginPlay(SceneBeginData* data) void Actor::EndPlay() { - ASSERT(IsDuringPlay()); +#if ENABLE_ASSERTION + CHECK(IsDuringPlay()); +#endif // Fire event for scripting if (IsActiveInHierarchy() && GetScene()) @@ -1059,7 +1065,9 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) void Actor::OnEnable() { - ASSERT(!_isEnabled); +#if ENABLE_ASSERTION + CHECK(!_isEnabled); +#endif _isEnabled = true; for (int32 i = 0; i < Scripts.Count(); i++) @@ -1079,7 +1087,9 @@ void Actor::OnEnable() void Actor::OnDisable() { - ASSERT(_isEnabled); +#if ENABLE_ASSERTION + CHECK(_isEnabled); +#endif _isEnabled = false; for (int32 i = Scripts.Count() - 1; i >= 0; i--) From 911cdb3f2d8d245f836c4eeedf1b81c87d90e7a7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Aug 2023 10:01:00 +0200 Subject: [PATCH 51/52] Softlock fix --- Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp index afe384d6c..11d136d62 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp @@ -178,7 +178,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) DrawCall drawCall; if (TerrainManager::GetChunkGeometry(drawCall, chunkSize, 0)) - return; + break; context->UpdateCB(cb, &shaderData); context->BindCB(0, cb); From f29cd1b7b855b49712b3aeeffdd5cb230c4918a9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Aug 2023 10:03:47 +0200 Subject: [PATCH 52/52] Simplify code #1275 --- Source/Editor/Gizmo/GridGizmo.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Gizmo/GridGizmo.cs b/Source/Editor/Gizmo/GridGizmo.cs index d9e080982..3174d23c1 100644 --- a/Source/Editor/Gizmo/GridGizmo.cs +++ b/Source/Editor/Gizmo/GridGizmo.cs @@ -46,20 +46,19 @@ namespace FlaxEditor.Gizmo var plane = new Plane(Vector3.Zero, Vector3.UnitY); var dst = CollisionsHelper.DistancePlanePoint(ref plane, ref viewPos); - float space, size; + float space = Editor.Instance.Options.Options.Viewport.ViewportGridScale, size; if (dst <= 500.0f) { - space = Editor.Instance.Options.Options.Viewport.ViewportGridScale; size = 8000; } else if (dst <= 2000.0f) { - space = Editor.Instance.Options.Options.Viewport.ViewportGridScale * 2; + space *= 2; size = 8000; } else { - space = Editor.Instance.Options.Options.Viewport.ViewportGridScale * 20; + space *= 20; size = 100000; }