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/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/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 new file mode 100644 index 000000000..5abb52b4a --- /dev/null +++ b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs @@ -0,0 +1,93 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; +using System.Collections.Generic; +using FlaxEngine; + +namespace FlaxEditor.GUI.ContextMenu +{ + /// + /// Context menu for a single selectable option from range of values (eg. enum). + /// + [HideInEditor] + class ContextMenuSingleSelectGroup + { + private struct SingleSelectGroupItem + { + 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(); + private SingleSelectGroupItem _selectedItem; + + public T Selected + { + get => _selectedItem.Value; + set + { + 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) + { + 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 SetSelected(SingleSelectGroupItem item) + { + foreach (var e in _items) + { + foreach (var btn in e.Buttons) + btn.Checked = false; + } + _selectedItem = item; + + SelectedChanged?.Invoke(item.Value); + item.Selected?.Invoke(); + + foreach (var btn in item.Buttons) + btn.Checked = true; + } + } +} diff --git a/Source/Editor/GUI/ToolStrip.cs b/Source/Editor/GUI/ToolStrip.cs index 6a881281f..0dac241ed 100644 --- a/Source/Editor/GUI/ToolStrip.cs +++ b/Source/Editor/GUI/ToolStrip.cs @@ -23,10 +23,15 @@ 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; + /// + /// Event fired when button gets clicked with the secondary mouse button. + /// + public Action SecondaryButtonClicked; + /// /// Tries to get the last button. /// @@ -146,6 +151,11 @@ namespace FlaxEditor.GUI ButtonClicked?.Invoke(button); } + internal void OnSecondaryButtonClicked(ToolStripButton button) + { + SecondaryButtonClicked?.Invoke(button); + } + /// protected override void PerformLayoutBeforeChildren() { diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index 09a26b940..e839a7356 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -20,13 +20,19 @@ 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; + /// + /// Event fired when user clicks the button. + /// + public Action SecondaryClicked; + /// /// The checked state. /// @@ -63,6 +69,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 +119,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) @@ -124,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); } } @@ -155,9 +161,13 @@ namespace FlaxEditor.GUI { if (button == MouseButton.Left) { - // Set flag - _mouseDown = true; - + _primaryMouseDown = true; + Focus(); + return true; + } + if (button == MouseButton.Right) + { + _secondaryMouseDown = true; Focus(); return true; } @@ -168,12 +178,9 @@ namespace FlaxEditor.GUI /// public override bool OnMouseUp(Float2 location, MouseButton button) { - if (button == MouseButton.Left && _mouseDown) + if (button == MouseButton.Left && _primaryMouseDown) { - // Clear flag - _mouseDown = false; - - // Fire events + _primaryMouseDown = false; if (AutoCheck) Checked = !Checked; Clicked?.Invoke(); @@ -181,6 +188,14 @@ namespace FlaxEditor.GUI return true; } + if (button == MouseButton.Right && _secondaryMouseDown) + { + _secondaryMouseDown = false; + SecondaryClicked?.Invoke(); + (Parent as ToolStrip)?.OnSecondaryButtonClicked(this); + ContextMenu?.Show(this, new Float2(0, Height)); + return true; + } return base.OnMouseUp(location, button); } @@ -188,8 +203,8 @@ namespace FlaxEditor.GUI /// public override void OnMouseLeave() { - // Clear flag - _mouseDown = false; + _primaryMouseDown = false; + _secondaryMouseDown = false; base.OnMouseLeave(); } @@ -197,8 +212,8 @@ namespace FlaxEditor.GUI /// public override void OnLostFocus() { - // Clear flag - _mouseDown = false; + _primaryMouseDown = false; + _secondaryMouseDown = false; base.OnLostFocus(); } diff --git a/Source/Editor/Gizmo/GridGizmo.cs b/Source/Editor/Gizmo/GridGizmo.cs index 8b4f49986..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 = 50; size = 8000; } else if (dst <= 2000.0f) { - space = 100; + space *= 2; size = 8000; } else { - space = 1000; + space *= 20; size = 100000; } 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); 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(); diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 858ef2b1a..38c61942a 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 FlaxEditor.Options; 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; @@ -195,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; @@ -351,6 +360,7 @@ namespace FlaxEditor.Modules // Update window background mainWindow.BackgroundColor = Style.Current.Background; + InitSharedMenus(); InitMainMenu(mainWindow); InitToolstrip(mainWindow); InitStatusBar(mainWindow); @@ -409,6 +419,7 @@ namespace FlaxEditor.Modules Editor.Undo.UndoDone += OnUndoEvent; Editor.Undo.RedoDone += OnUndoEvent; Editor.Undo.ActionDone += OnUndoEvent; + GameCooker.Event += OnGameCookerEvent; UpdateToolstrip(); } @@ -424,6 +435,11 @@ namespace FlaxEditor.Modules UpdateStatusBar(); } + private void OnGameCookerEvent(GameCooker.EventType type) + { + UpdateToolstrip(); + } + /// public override void OnExit() { @@ -466,6 +482,22 @@ namespace FlaxEditor.Modules return dialog; } + private void InitSharedMenus() + { + for (int i = 1; i <= 4; i++) + _numberOfClientsGroup.AddItem(i.ToString(), i); + + _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 => { _numberOfClientsGroup.Selected = options.Interface.NumberOfGameClientsToLaunch; }; + } + private void InitMainMenu(RootControl mainWindow) { MainMenu = new MainMenu(mainWindow) @@ -523,11 +555,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 +643,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 +651,8 @@ namespace FlaxEditor.Modules private void InitToolstrip(RootControl mainWindow) { + var inputOptions = Editor.Options.Options.Input; + ToolStrip = new ToolStrip(34.0f, MainMenu.Bottom) { Parent = mainWindow, @@ -625,10 +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(); + + // 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); + 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)"); + + // Play + _toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play64, OnPlayPressed).LinkTooltip("Play Game"); + _toolStripPlay.ContextMenu = new ContextMenu(); + 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.Selected = Editor.Options.Options.Interface.PlayButtonAction; + playActionGroup.SelectedChanged = SetPlayAction; + Editor.Options.OptionsChanged += options => { playActionGroup.Selected = options.Interface.PlayButtonAction; }; + + _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(); @@ -663,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; @@ -786,7 +848,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 +1032,72 @@ namespace FlaxEditor.Modules projectInfo.Save(); } + private void SetPlayAction(InterfaceOptions.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 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(); + 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..5fd8c31c7 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -74,6 +74,22 @@ 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. /// @@ -82,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 /// @@ -196,30 +210,44 @@ 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)] + 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), Range(1, 4)] + [EditorDisplay("Cook & Run"), EditorOrder(500)] + 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/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; } } diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index f3fd753d1..5ece067a0 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -238,6 +238,7 @@ namespace FlaxEditor.Windows { [EditorDisplay(null, "arm64")] ARM64, + [EditorDisplay(null, "x64")] x64, } @@ -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. /// @@ -659,16 +672,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 +699,20 @@ 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/Engine/Core/Math/Mathd.cs b/Source/Engine/Core/Math/Mathd.cs index 29bcc9420..48db547df 100644 --- a/Source/Engine/Core/Math/Mathd.cs +++ b/Source/Engine/Core/Math/Mathd.cs @@ -876,38 +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) { - // TODO: make it faster? + 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 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) { - // TODO: make it faster? - while (angle > 180.0f) - { - angle -= 360.0f; - } - while (angle < -180.0f) - { - angle += 360.0f; - } + 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 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; } @@ -927,8 +943,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 +961,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 +974,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 +1032,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..cea81a779 100644 --- a/Source/Engine/Core/Math/Mathf.cs +++ b/Source/Engine/Core/Math/Mathf.cs @@ -1176,38 +1176,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 float UnwindRadians(float angle) { - // TODO: make it faster? + 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 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) { - // TODO: make it faster? + 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 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; } @@ -1299,8 +1315,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 +1332,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 +1349,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 +1366,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 +1379,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 +1392,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 +1405,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 +1486,7 @@ namespace FlaxEngine /// /// Gauss function. - /// http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function + ///

///
/// Curve amplitude. /// Position X. @@ -1463,7 +1503,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/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--) diff --git a/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs index 62040d224..5bb79b81b 100644 --- a/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/AssetReferenceAttribute.cs @@ -53,4 +53,23 @@ namespace FlaxEngine UseSmallPicker = useSmallPicker; } } + +#if USE_NETCORE + /// + /// 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) + { + } + } +#endif } 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) diff --git a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp index 59704800a..11d136d62 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp @@ -151,6 +151,13 @@ 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; + scene->EntriesLocker.Unlock(); + return; + } const auto heightmap = patch->Heightmap.Get()->GetTexture(); Matrix world; @@ -171,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); diff --git a/Source/Engine/Tests/TestMath.cs b/Source/Engine/Tests/TestMath.cs new file mode 100644 index 000000000..b8eda936b --- /dev/null +++ b/Source/Engine/Tests/TestMath.cs @@ -0,0 +1,48 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#if FLAX_TESTS +using System; +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 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 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 diff --git a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs index 13433f92c..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) @@ -272,8 +276,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(""); }