diff --git a/Content/Shaders/VolumetricFog.flax b/Content/Shaders/VolumetricFog.flax index 3613c79b6..6612a2c6a 100644 --- a/Content/Shaders/VolumetricFog.flax +++ b/Content/Shaders/VolumetricFog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ccc821da8613409f4c829f14958534f87c142edc6ac0f0d73d8a9e6e3fc6efc -size 13299 +oid sha256:ebbfff2b1ca68e630dff99b58b00de77f3e19e32fae3ad04dc7fae6ae95117ef +size 13293 diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index 4a1793982..23f493c65 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -223,7 +223,7 @@ namespace FlaxEditor.GUI } } - private readonly TextBox _searchBox; + private readonly SearchBox _searchBox; private readonly Panel _scrollPanel; private List _categoryPanels; private bool _waitingForInput; @@ -260,6 +260,7 @@ namespace FlaxEditor.GUI Width = Width - 3, }; _searchBox.TextChanged += OnSearchFilterChanged; + _searchBox.ClearSearchButton.Clicked += () => PerformLayout(); // Panel with scrollbar _scrollPanel = new Panel(ScrollBars.Vertical) diff --git a/Source/Editor/GUI/Timeline/SceneAnimationTimeline.cs b/Source/Editor/GUI/Timeline/SceneAnimationTimeline.cs index 10c4474fa..ceac9931b 100644 --- a/Source/Editor/GUI/Timeline/SceneAnimationTimeline.cs +++ b/Source/Editor/GUI/Timeline/SceneAnimationTimeline.cs @@ -197,7 +197,7 @@ namespace FlaxEditor.GUI.Timeline continue; var track = (NestedSceneAnimationTrack)timeline.NewTrack(NestedSceneAnimationTrack.GetArchetype()); track.Asset = sceneAnimation; - track.TrackMedia.Duration = sceneAnimation.Duration; + track.TrackMedia.DurationFrames = (int)(sceneAnimation.Duration * timeline.FramesPerSecond); track.Rename(assetItem.ShortName); timeline.AddTrack(track); } @@ -208,7 +208,7 @@ namespace FlaxEditor.GUI.Timeline continue; var track = (AudioTrack)timeline.NewTrack(AudioTrack.GetArchetype()); track.Asset = audioClip; - track.TrackMedia.Duration = audioClip.Length; + track.TrackMedia.DurationFrames = (int)(audioClip.Length * timeline.FramesPerSecond); track.Rename(assetItem.ShortName); timeline.AddTrack(track); } diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs index 81fe950b9..556c4dcdb 100644 --- a/Source/Editor/GUI/Timeline/Timeline.cs +++ b/Source/Editor/GUI/Timeline/Timeline.cs @@ -1288,10 +1288,7 @@ namespace FlaxEditor.GUI.Timeline public virtual void AddTrack(Track track, bool withUndo = true) { // Ensure name is unique - int idx = 1; - var name = track.Name; - while (!IsTrackNameValid(track.Name)) - track.Name = string.Format("{0} {1}", name, idx++); + track.Name = GetValidTrackName(track.Name); // Add it to the timeline _tracks.Add(track); @@ -1843,11 +1840,7 @@ namespace FlaxEditor.GUI.Timeline /// The track name. public string GetValidTrackName(string name) { - string newName = name; - int count = 0; - while (!IsTrackNameValid(newName)) - newName = string.Format("{0} {1}", name, count++); - return newName; + return Utilities.Utils.IncrementNameNumber(name, IsTrackNameValid); } /// diff --git a/Source/Editor/GUI/Timeline/Track.cs b/Source/Editor/GUI/Timeline/Track.cs index 929b213e4..ec2dd63d8 100644 --- a/Source/Editor/GUI/Timeline/Track.cs +++ b/Source/Editor/GUI/Timeline/Track.cs @@ -854,11 +854,9 @@ namespace FlaxEditor.GUI.Timeline /// The base name. public void Rename(string name) { - string newName = name; - int count = 0; - while (_timeline != null && !_timeline.IsTrackNameValid(newName)) - newName = string.Format("{0} {1}", name, count++); - OnRename(newName); + if (_timeline != null) + name = _timeline.GetValidTrackName(name); + OnRename(name); } /// diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index a90a19321..c6cb5fa4a 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -107,6 +107,21 @@ namespace FlaxEditor.GUI return this; } + /// + /// Links the tooltip with input binding info. + /// + /// The text. + /// The input key binding. + /// This tooltip. + public ToolStripButton LinkTooltip(string text, ref Options.InputBinding inputBinding) + { + var input = inputBinding.ToString(); + if (input.Length != 0) + text = $"{text} ({input})"; + LinkTooltip(text); + return this; + } + /// public override void Draw() { diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 4282d5305..6a197c62f 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -707,18 +707,18 @@ namespace FlaxEditor.Modules Parent = mainWindow, }; - _toolStripSaveAll = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Save64, Editor.SaveAll).LinkTooltip($"Save all ({inputOptions.Save})"); + _toolStripSaveAll = ToolStrip.AddButton(Editor.Icons.Save64, Editor.SaveAll).LinkTooltip("Save all", ref inputOptions.Save); ToolStrip.AddSeparator(); - _toolStripUndo = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Undo64, Editor.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})"); - _toolStripRedo = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Redo64, Editor.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})"); + _toolStripUndo = ToolStrip.AddButton(Editor.Icons.Undo64, Editor.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); + _toolStripRedo = ToolStrip.AddButton(Editor.Icons.Redo64, Editor.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); ToolStrip.AddSeparator(); - _toolStripTranslate = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Translate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate).LinkTooltip($"Change Gizmo tool mode to Translate ({inputOptions.TranslateMode})"); - _toolStripRotate = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Rotate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip($"Change Gizmo tool mode to Rotate ({inputOptions.RotateMode})"); - _toolStripScale = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip($"Change Gizmo tool mode to Scale ({inputOptions.ScaleMode})"); + _toolStripTranslate = ToolStrip.AddButton(Editor.Icons.Translate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate).LinkTooltip("Change Gizmo tool mode to Translate", ref inputOptions.TranslateMode); + _toolStripRotate = ToolStrip.AddButton(Editor.Icons.Rotate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip("Change Gizmo tool mode to Rotate", ref inputOptions.RotateMode); + _toolStripScale = ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip("Change Gizmo tool mode to Scale", ref inputOptions.ScaleMode); ToolStrip.AddSeparator(); // Play - _toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play64, Editor.Simulation.DelegatePlayOrStopPlayInEditor).LinkTooltip($"Play In Editor ({inputOptions.Play})"); + _toolStripPlay = ToolStrip.AddButton(Editor.Icons.Play64, Editor.Simulation.DelegatePlayOrStopPlayInEditor).LinkTooltip("Play In Editor", ref inputOptions.Play); _toolStripPlay.ContextMenu = new ContextMenu(); var playSubMenu = _toolStripPlay.ContextMenu.AddChildMenu("Play button action"); var playActionGroup = new ContextMenuSingleSelectGroup(); @@ -739,16 +739,16 @@ namespace FlaxEditor.Modules windowModesGroup.SelectedChanged = SetGameWindowMode; Editor.Options.OptionsChanged += options => { windowModesGroup.Selected = options.Interface.DefaultGameWindowMode; }; - _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 ({inputOptions.StepFrame})"); + _toolStripPause = ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip("Pause/Resume game", ref inputOptions.Pause); + _toolStripStep = ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game", ref inputOptions.StepFrame); ToolStrip.AddSeparator(); // Build 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 ({inputOptions.BuildScenesData})"); + _toolStripBuildScenes = ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options", ref inputOptions.BuildScenesData); // Cook and run - _toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip($"Cook & Run - build game for the current platform and run it locally ({inputOptions.CookAndRun})"); + _toolStripCook = ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip("Cook & Run - build game for the current platform and run it locally", ref inputOptions.CookAndRun); _toolStripCook.ContextMenu = new ContextMenu(); _toolStripCook.ContextMenu.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked); _toolStripCook.ContextMenu.AddSeparator(); diff --git a/Source/Editor/Surface/SurfaceControl.cs b/Source/Editor/Surface/SurfaceControl.cs index 29d7c1768..5b39c6dae 100644 --- a/Source/Editor/Surface/SurfaceControl.cs +++ b/Source/Editor/Surface/SurfaceControl.cs @@ -132,7 +132,7 @@ namespace FlaxEditor.Surface public virtual void OnSurfaceLoaded(SurfaceNodeActions action) { // Snap bounds (with ceil) when using grid snapping - if (Surface.GridSnappingEnabled) + if (Surface != null && Surface.GridSnappingEnabled) { var bounds = Bounds; bounds.Location = Surface.SnapToGrid(bounds.Location, false); diff --git a/Source/Editor/Surface/SurfaceUtils.cs b/Source/Editor/Surface/SurfaceUtils.cs index f19adbff5..990b3ea8a 100644 --- a/Source/Editor/Surface/SurfaceUtils.cs +++ b/Source/Editor/Surface/SurfaceUtils.cs @@ -572,12 +572,12 @@ namespace FlaxEditor.Surface var undo = surface.Undo; // Toolstrip - saveButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Save64, window.Save).LinkTooltip("Save"); + saveButton = toolStrip.AddButton(editor.Icons.Save64, window.Save).LinkTooltip("Save", ref inputOptions.Save); toolStrip.AddSeparator(); - undoButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})"); - redoButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})"); + undoButton = toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); + redoButton = toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); toolStrip.AddSeparator(); - toolStrip.AddButton(editor.Icons.Search64, editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})"); + toolStrip.AddButton(editor.Icons.Search64, editor.ContentFinding.ShowSearch).LinkTooltip("Open content search tool", ref inputOptions.Search); toolStrip.AddButton(editor.Icons.CenterView64, surface.ShowWholeGraph).LinkTooltip("Show whole graph"); var gridSnapButton = toolStrip.AddButton(editor.Icons.Grid32, surface.ToggleGridSnapping); gridSnapButton.LinkTooltip("Toggle grid snapping for nodes."); diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs index 45627eaf2..d0e6a3fe9 100644 --- a/Source/Editor/Windows/Assets/AnimationWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationWindow.cs @@ -281,10 +281,10 @@ namespace FlaxEditor.Windows.Assets _propertiesPresenter.Select(_properties); // Toolstrip - _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save"); + _saveButton = _toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); _toolstrip.AddSeparator(); - _undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})"); - _redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})"); + _undoButton = _toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); + _redoButton = _toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); _toolstrip.AddSeparator(); _toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/animation/index.html")).LinkTooltip("See documentation to learn more"); diff --git a/Source/Editor/Windows/Assets/GameplayGlobalsWindow.cs b/Source/Editor/Windows/Assets/GameplayGlobalsWindow.cs index 477be532c..3dda281a6 100644 --- a/Source/Editor/Windows/Assets/GameplayGlobalsWindow.cs +++ b/Source/Editor/Windows/Assets/GameplayGlobalsWindow.cs @@ -422,10 +422,10 @@ namespace FlaxEditor.Windows.Assets _proxy = new PropertiesProxy(); _propertiesEditor.Select(_proxy); - _saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip($"Save asset ({inputOptions.Save})"); + _saveButton = _toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); _toolstrip.AddSeparator(); - _undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})"); - _redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})"); + _undoButton = _toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); + _redoButton = _toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); _toolstrip.AddSeparator(); _resetButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Rotate32, Reset).LinkTooltip("Resets the variables values to the default values"); diff --git a/Source/Editor/Windows/Assets/JsonAssetWindow.cs b/Source/Editor/Windows/Assets/JsonAssetWindow.cs index c1ce59f75..dc1e1e71f 100644 --- a/Source/Editor/Windows/Assets/JsonAssetWindow.cs +++ b/Source/Editor/Windows/Assets/JsonAssetWindow.cs @@ -95,10 +95,10 @@ namespace FlaxEditor.Windows.Assets _undo.ActionDone += OnUndoRedo; // Toolstrip - _saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save"); + _saveButton = _toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); _toolstrip.AddSeparator(); - _undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})"); - _redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})"); + _undoButton = _toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); + _redoButton = _toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); // Panel var panel = new Panel(ScrollBars.Vertical) diff --git a/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs b/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs index 45752f734..09ab43b39 100644 --- a/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs +++ b/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs @@ -135,10 +135,10 @@ namespace FlaxEditor.Windows.Assets _undo.ActionDone += OnUndoRedo; // Toolstrip - _saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save"); + _saveButton = _toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); _toolstrip.AddSeparator(); - _undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})"); - _redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})"); + _undoButton = _toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); + _redoButton = _toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); _toolstrip.AddSeparator(); _toolstrip.AddButton(Editor.Icons.Up64, OnExport).LinkTooltip("Export localization table entries for translation to .pot file"); diff --git a/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs b/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs index 63471a446..d025c9d6d 100644 --- a/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs +++ b/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs @@ -387,10 +387,10 @@ namespace FlaxEditor.Windows.Assets _undo.ActionDone += OnAction; // Toolstrip - _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save"); + _saveButton = _toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); _toolstrip.AddSeparator(); - _undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})"); - _redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})"); + _undoButton = _toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); + _redoButton = _toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); _toolstrip.AddSeparator(); _toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/materials/instanced-materials/index.html")).LinkTooltip("See documentation to learn more"); diff --git a/Source/Editor/Windows/Assets/ModelBaseWindow.cs b/Source/Editor/Windows/Assets/ModelBaseWindow.cs index dc9b04ee7..ee355c9de 100644 --- a/Source/Editor/Windows/Assets/ModelBaseWindow.cs +++ b/Source/Editor/Windows/Assets/ModelBaseWindow.cs @@ -105,8 +105,10 @@ namespace FlaxEditor.Windows.Assets protected ModelBaseWindow(Editor editor, AssetItem item) : base(editor, item) { + var inputOptions = Editor.Options.Options.Input; + // Toolstrip - _saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save"); + _saveButton = _toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); // Split Panel _split = new SplitPanel(Orientation.Horizontal, ScrollBars.None, ScrollBars.None) diff --git a/Source/Editor/Windows/Assets/ParticleSystemWindow.cs b/Source/Editor/Windows/Assets/ParticleSystemWindow.cs index 39ce93918..2708f8c87 100644 --- a/Source/Editor/Windows/Assets/ParticleSystemWindow.cs +++ b/Source/Editor/Windows/Assets/ParticleSystemWindow.cs @@ -359,10 +359,10 @@ namespace FlaxEditor.Windows.Assets propertiesEditor.Select(new GeneralProxy(this)); // Toolstrip - _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save"); + _saveButton = _toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); _toolstrip.AddSeparator(); - _undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})"); - _redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})"); + _undoButton = _toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); + _redoButton = _toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); _toolstrip.AddSeparator(); _toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more"); diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 8aa1fbbad..7ee8ecff0 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -181,14 +181,14 @@ namespace FlaxEditor.Windows.Assets _propertiesEditor.Modified += MarkAsEdited; // Toolstrip - _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save"); + _saveButton = _toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); _toolstrip.AddSeparator(); - _toolStripUndo = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})"); - _toolStripRedo = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})"); + _toolStripUndo = _toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); + _toolStripRedo = _toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); _toolstrip.AddSeparator(); - _toolStripTranslate = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Translate32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate).LinkTooltip($"Change Gizmo tool mode to Translate ({inputOptions.TranslateMode})"); - _toolStripRotate = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Rotate32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip($"Change Gizmo tool mode to Rotate ({inputOptions.RotateMode})"); - _toolStripScale = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Scale32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip($"Change Gizmo tool mode to Scale ({inputOptions.ScaleMode})"); + _toolStripTranslate = _toolstrip.AddButton(Editor.Icons.Translate32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate).LinkTooltip("Change Gizmo tool mode to Translate", ref inputOptions.TranslateMode); + _toolStripRotate = _toolstrip.AddButton(Editor.Icons.Rotate32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip("Change Gizmo tool mode to Rotate", ref inputOptions.RotateMode); + _toolStripScale = _toolstrip.AddButton(Editor.Icons.Scale32, () => _viewport.TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip("Change Gizmo tool mode to Scale", ref inputOptions.ScaleMode); _toolstrip.AddSeparator(); _toolStripLiveReload = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Refresh64, () => LiveReload = !LiveReload).SetChecked(true).SetAutoCheck(true).LinkTooltip("Live changes preview (applies prefab changes on modification by auto)"); diff --git a/Source/Editor/Windows/Assets/SceneAnimationWindow.cs b/Source/Editor/Windows/Assets/SceneAnimationWindow.cs index 878eb3b00..9b0875752 100644 --- a/Source/Editor/Windows/Assets/SceneAnimationWindow.cs +++ b/Source/Editor/Windows/Assets/SceneAnimationWindow.cs @@ -652,10 +652,10 @@ namespace FlaxEditor.Windows.Assets _timeline.SetNoTracksText("Loading..."); // Toolstrip - _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save"); + _saveButton = _toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); _toolstrip.AddSeparator(); - _undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})"); - _redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})"); + _undoButton = _toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo", ref inputOptions.Undo); + _redoButton = _toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo", ref inputOptions.Redo); _toolstrip.AddSeparator(); _previewButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Refresh64, OnPreviewButtonClicked).SetAutoCheck(true).LinkTooltip("If checked, enables live-preview of the animation on a scene while editing"); _renderButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Build64, OnRenderButtonClicked).LinkTooltip("Open the scene animation rendering utility..."); diff --git a/Source/Editor/Windows/Assets/SkeletonMaskWindow.cs b/Source/Editor/Windows/Assets/SkeletonMaskWindow.cs index 0b9cf2b5c..ff2446439 100644 --- a/Source/Editor/Windows/Assets/SkeletonMaskWindow.cs +++ b/Source/Editor/Windows/Assets/SkeletonMaskWindow.cs @@ -195,8 +195,10 @@ namespace FlaxEditor.Windows.Assets public SkeletonMaskWindow(Editor editor, AssetItem item) : base(editor, item) { + var inputOptions = Editor.Options.Options.Input; + // Toolstrip - _saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save asset to the file"); + _saveButton = _toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); _toolstrip.AddSeparator(); _toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/skeleton-mask.html")).LinkTooltip("See documentation to learn more"); diff --git a/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs b/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs index 9d32e5077..9601555d2 100644 --- a/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs +++ b/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs @@ -273,6 +273,8 @@ namespace FlaxEditor.Windows.Assets public SpriteAtlasWindow(Editor editor, AssetItem item) : base(editor, item) { + var inputOptions = Editor.Options.Options.Input; + // Split Panel _split = new SplitPanel(Orientation.Horizontal, ScrollBars.None, ScrollBars.Vertical) { @@ -296,7 +298,7 @@ namespace FlaxEditor.Windows.Assets _propertiesEditor.Modified += MarkAsEdited; // Toolstrip - _saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save"); + _saveButton = _toolstrip.AddButton(editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); _toolstrip.AddButton(editor.Icons.Import64, () => Editor.ContentImporting.Reimport((BinaryAssetItem)Item)).LinkTooltip("Reimport"); _toolstrip.AddSeparator(); _toolstrip.AddButton(editor.Icons.AddFile64, () => diff --git a/Source/Editor/Windows/Assets/TextureWindow.cs b/Source/Editor/Windows/Assets/TextureWindow.cs index 9d4b94024..68f138978 100644 --- a/Source/Editor/Windows/Assets/TextureWindow.cs +++ b/Source/Editor/Windows/Assets/TextureWindow.cs @@ -27,7 +27,7 @@ namespace FlaxEditor.Windows.Assets public class PropertiesProxyBase { internal TextureWindow _window; - + /// /// Gathers parameters from the specified texture. /// @@ -37,7 +37,7 @@ namespace FlaxEditor.Windows.Assets // Link _window = window; } - + /// /// Clears temporary data. /// @@ -83,7 +83,7 @@ namespace FlaxEditor.Windows.Assets } } } - + /// /// The texture import properties proxy object. /// @@ -129,7 +129,7 @@ namespace FlaxEditor.Windows.Assets public void DiscardChanges() { } - + private sealed class ProxyEditor : GenericEditor { public override void Initialize(LayoutElementsContainer layout) @@ -151,7 +151,7 @@ namespace FlaxEditor.Windows.Assets /// The presenter to use in the tab. /// public CustomEditorPresenter Presenter; - + /// /// The proxy to use in the tab. /// @@ -214,6 +214,8 @@ namespace FlaxEditor.Windows.Assets public TextureWindow(Editor editor, AssetItem item) : base(editor, item) { + var inputOptions = Editor.Options.Options.Input; + // Split Panel _split = new SplitPanel(Orientation.Horizontal, ScrollBars.None, ScrollBars.Vertical) { @@ -228,7 +230,7 @@ namespace FlaxEditor.Windows.Assets { Parent = _split.Panel1 }; - + // Properties tabs _tabs = new() { @@ -244,7 +246,7 @@ namespace FlaxEditor.Windows.Assets _tabs.AddTab(new ImportTab(this)); // Toolstrip - _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save"); + _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save", ref inputOptions.Save); _toolstrip.AddButton(Editor.Icons.Import64, () => Editor.ContentImporting.Reimport((BinaryAssetItem)Item)).LinkTooltip("Reimport"); _toolstrip.AddSeparator(); _toolstrip.AddButton(Editor.Icons.CenterView64, _preview.CenterView).LinkTooltip("Center view"); diff --git a/Source/Editor/Windows/Assets/VisualScriptWindow.cs b/Source/Editor/Windows/Assets/VisualScriptWindow.cs index b424ecffc..f41936dd0 100644 --- a/Source/Editor/Windows/Assets/VisualScriptWindow.cs +++ b/Source/Editor/Windows/Assets/VisualScriptWindow.cs @@ -603,11 +603,11 @@ namespace FlaxEditor.Windows.Assets _debugToolstripControls = new[] { _toolstrip.AddSeparator(), - _toolstrip.AddButton(editor.Icons.Play64, OnDebuggerContinue).LinkTooltip($"Continue ({inputOptions.DebuggerContinue})"), + _toolstrip.AddButton(editor.Icons.Play64, OnDebuggerContinue).LinkTooltip("Continue", ref inputOptions.DebuggerContinue), _toolstrip.AddButton(editor.Icons.Search64, OnDebuggerNavigateToCurrentNode).LinkTooltip("Navigate to the current stack trace node"), - _toolstrip.AddButton(editor.Icons.Right64, OnDebuggerStepOver).LinkTooltip($"Step Over ({inputOptions.DebuggerStepOver})"), - _toolstrip.AddButton(editor.Icons.Down64, OnDebuggerStepInto).LinkTooltip($"Step Into ({inputOptions.DebuggerStepInto})"), - _toolstrip.AddButton(editor.Icons.Up64, OnDebuggerStepOut).LinkTooltip($"Step Out ({inputOptions.DebuggerStepOut})"), + _toolstrip.AddButton(editor.Icons.Right64, OnDebuggerStepOver).LinkTooltip("Step Over", ref inputOptions.DebuggerStepOver), + _toolstrip.AddButton(editor.Icons.Down64, OnDebuggerStepInto).LinkTooltip("Step Into", ref inputOptions.DebuggerStepInto), + _toolstrip.AddButton(editor.Icons.Up64, OnDebuggerStepOut).LinkTooltip("Step Out", ref inputOptions.DebuggerStepOut), _toolstrip.AddButton(editor.Icons.Stop64, OnDebuggerStop).LinkTooltip("Stop debugging"), }; foreach (var control in _debugToolstripControls) diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 5d37ce865..f8c4ea9b5 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -65,11 +65,6 @@ namespace FlaxEditor.Windows /// public OutputLogWindow Window; - /// - /// The input actions collection to processed during user input. - /// - public InputActionsContainer InputActions = new InputActionsContainer(); - /// /// The default text style. /// @@ -88,7 +83,7 @@ namespace FlaxEditor.Windows /// public override bool OnKeyDown(KeyboardKeys key) { - if (InputActions.Process(Editor.Instance, this, key)) + if (Window.InputActions.Process(Editor.Instance, this, key)) return true; return base.OnKeyDown(key); } @@ -214,9 +209,8 @@ namespace FlaxEditor.Windows // Setup editor options Editor.Options.OptionsChanged += OnEditorOptionsChanged; OnEditorOptionsChanged(Editor.Options.Options); - - _output.InputActions.Add(options => options.Search, () => _searchBox.Focus()); - InputActions.Add(options => options.Search, () => _searchBox.Focus()); + + InputActions.Add(options => options.Search, _searchBox.Focus); GameCooker.Event += OnGameCookerEvent; ScriptsBuilder.CompilationFailed += OnScriptsCompilationFailed; diff --git a/Source/Editor/Windows/VisualScriptDebuggerWindow.cs b/Source/Editor/Windows/VisualScriptDebuggerWindow.cs index 00acd7ba1..6104d41bb 100644 --- a/Source/Editor/Windows/VisualScriptDebuggerWindow.cs +++ b/Source/Editor/Windows/VisualScriptDebuggerWindow.cs @@ -409,7 +409,7 @@ namespace FlaxEditor.Windows _debugToolstripControls = new[] { toolstrip.AddSeparator(), - toolstrip.AddButton(editor.Icons.Play64, OnDebuggerContinue).LinkTooltip($"Continue ({inputOptions.DebuggerContinue})"), + toolstrip.AddButton(editor.Icons.Play64, OnDebuggerContinue).LinkTooltip("Continue", ref inputOptions.DebuggerContinue), toolstrip.AddButton(editor.Icons.Search64, OnDebuggerNavigateToCurrentNode).LinkTooltip("Navigate to the current stack trace node"), toolstrip.AddButton(editor.Icons.Stop64, OnDebuggerStop).LinkTooltip("Stop debugging"), }; diff --git a/Source/Engine/AI/BehaviorTreeNodes.cpp b/Source/Engine/AI/BehaviorTreeNodes.cpp index 2833cf624..cf4b4d65a 100644 --- a/Source/Engine/AI/BehaviorTreeNodes.cpp +++ b/Source/Engine/AI/BehaviorTreeNodes.cpp @@ -444,7 +444,6 @@ BehaviorUpdateResult BehaviorTreeMoveToNode::Update(const BehaviorUpdateContext& else goalLocation = TargetLocation.Get(context.Knowledge); repath |= Vector3::Distance(goalLocation, state->GoalLocation) > TargetGoalUpdateTolerance; - state->GoalLocation = goalLocation; } if (repath) @@ -490,6 +489,7 @@ BehaviorUpdateResult BehaviorTreeMoveToNode::Update(const BehaviorUpdateContext& state->HasPath = true; state->TargetPathIndex = 1; state->Result = BehaviorUpdateResult::Running; + state->GoalLocation = goalLocation; // TODO: add path debugging in Editor (eg. via BT window) diff --git a/Source/Engine/Core/Types/StringView.cpp b/Source/Engine/Core/Types/StringView.cpp index 39404179b..24a3978f8 100644 --- a/Source/Engine/Core/Types/StringView.cpp +++ b/Source/Engine/Core/Types/StringView.cpp @@ -35,7 +35,7 @@ StringView StringView::Left(int32 count) const StringView StringView::Right(int32 count) const { const int32 countClamped = count < 0 ? 0 : count < Length() ? count : Length(); - return StringView(**this + Length() - countClamped); + return StringView(**this + countClamped, Length() - countClamped); } StringView StringView::Substring(int32 startIndex) const diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 3dd403d5f..3aea34368 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -2449,7 +2449,9 @@ void Variant::SetType(const VariantType& type) case VariantType::Structure: AllocStructure(); break; - default: ; + default: + AsUint64 = 0; + break; } } @@ -3065,6 +3067,69 @@ void Variant::DeleteValue() SetType(VariantType(VariantType::Null)); } +Variant Variant::Parse(const StringView& text, const VariantType& type) +{ + Variant result; + result.SetType(type); + if (text.IsEmpty()) + return result; + if (type != VariantType()) + { + switch (type.Type) + { + case VariantType::Bool: + if (text == TEXT("1") || text.Compare(StringView(TEXT("true"), 4), StringSearchCase::IgnoreCase) == 0) + result.AsBool = true; + break; + case VariantType::Int16: + StringUtils::Parse(text.Get(), &result.AsInt16); + break; + case VariantType::Uint16: + StringUtils::Parse(text.Get(), &result.AsUint16); + break; + case VariantType::Int: + StringUtils::Parse(text.Get(), &result.AsInt); + break; + case VariantType::Uint: + StringUtils::Parse(text.Get(), &result.AsUint); + break; + case VariantType::Int64: + StringUtils::Parse(text.Get(), &result.AsInt64); + break; + case VariantType::Uint64: + case VariantType::Enum: + StringUtils::Parse(text.Get(), &result.AsUint64); + break; + case VariantType::Float: + StringUtils::Parse(text.Get(), &result.AsFloat); + break; + case VariantType::Double: + StringUtils::Parse(text.Get(), &result.AsFloat); + result.AsDouble = (float)result.AsFloat; + break; + case VariantType::String: + result.SetString(text); + default: + break; + } + } + else + { + // Parse as number + int32 valueInt; + if (!StringUtils::Parse(text.Get(), text.Length(), &valueInt)) + { + result = valueInt; + } + else + { + // Fallback to string + result.SetString(text); + } + } + return result; +} + bool Variant::CanCast(const Variant& v, const VariantType& to) { if (v.Type == to) diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h index fa7f748fe..cd05fc038 100644 --- a/Source/Engine/Core/Types/Variant.h +++ b/Source/Engine/Core/Types/Variant.h @@ -381,6 +381,9 @@ public: // Frees the object or data owned by this Variant container (eg. structure or object). void DeleteValue(); + // Parses the text into the Variant value. Allows to specify explicit value type. + static Variant Parse(const StringView& text, const VariantType& type = VariantType()); + FORCE_INLINE Variant Cast(const VariantType& to) const { return Cast(*this, to); diff --git a/Source/Engine/Debug/DebugCommands.cpp b/Source/Engine/Debug/DebugCommands.cpp new file mode 100644 index 000000000..e4af0268e --- /dev/null +++ b/Source/Engine/Debug/DebugCommands.cpp @@ -0,0 +1,293 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +#include "DebugCommands.h" +#include "Engine/Core/Log.h" +#include "Engine/Core/Collections/Array.h" +#include "Engine/Engine/EngineService.h" +#include "Engine/Threading/Threading.h" +#include "Engine/Profiler/ProfilerCPU.h" +#include "Engine/Scripting/BinaryModule.h" +#include "Engine/Scripting/Scripting.h" +#include "Engine/Scripting/ManagedCLR/MAssembly.h" +#include "Engine/Scripting/ManagedCLR/MClass.h" +#include "Engine/Scripting/ManagedCLR/MMethod.h" +#include "Engine/Scripting/ManagedCLR/MField.h" +#include "Engine/Scripting/ManagedCLR/MProperty.h" +#include "FlaxEngine.Gen.h" + +struct CommandData +{ + String Name; + BinaryModule* Module; + void* Method = nullptr; + void* MethodGet = nullptr; + void* MethodSet = nullptr; + void* Field = nullptr; + + void Invoke(StringView args) const + { + PROFILE_CPU(); + + // Get command signature + Array> sigParams; + if (Method) + { + ScriptingTypeMethodSignature sig; + Module->GetMethodSignature(Method, sig); + sigParams = MoveTemp(sig.Params); + } + else if (Field) + { + ScriptingTypeFieldSignature sig; + Module->GetFieldSignature(Field, sig); + auto& p = sigParams.AddOne(); + p.IsOut = false; + p.Type = sig.ValueType; + } + else if (MethodSet && args.HasChars()) + { + ScriptingTypeMethodSignature sig; + Module->GetMethodSignature(MethodSet, sig); + sigParams = MoveTemp(sig.Params); + sigParams.Resize(1); + } + + // Parse arguments + Array params; + params.Resize(sigParams.Count()); + Array argsSeparated; + String argsStr(args); + argsStr.Split(' ', argsSeparated); + for (int32 i = 0; i < argsSeparated.Count() && i < params.Count(); i++) + { + params[i] = Variant::Parse(argsSeparated[i], sigParams[i].Type); + } + + // Call command + Variant result; + if (Method) + { + Module->InvokeMethod(Method, Variant::Null, ToSpan(params), result); + } + else if (Field) + { + if (args.IsEmpty()) + Module->GetFieldValue(Field, Variant::Null, result); + else + Module->SetFieldValue(Field, Variant::Null, params[0]); + } + else if (MethodGet && args.IsEmpty()) + { + Module->InvokeMethod(MethodGet, Variant::Null, ToSpan(params), result); + } + else if (MethodSet && args.HasChars()) + { + Module->InvokeMethod(MethodSet, Variant::Null, ToSpan(params), result); + } + + // Print result + if (result != Variant()) + { + LOG_STR(Info, result.ToString()); + } + } +}; + +namespace +{ + CriticalSection Locker; + bool Inited = false; + Array Commands; + + void OnBinaryModuleLoaded(BinaryModule* module) + { + if (module == GetBinaryModuleCorlib()) + return; + +#if USE_CSHARP + if (auto* managedModule = dynamic_cast(module)) + { + const MClass* attribute = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEngine.DebugCommand"); + ASSERT_LOW_LAYER(attribute); + const auto& classes = managedModule->Assembly->GetClasses(); + for (auto e : classes) + { + MClass* mclass = e.Value; + if (mclass->IsGeneric() || + mclass->IsInterface() || + mclass->IsEnum()) + continue; + const bool useClass = mclass->HasAttribute(attribute); + // TODO: optimize this via stack-based format buffer and then convert Ansi to UTF16 +#define BUILD_NAME(commandData, itemName) commandData.Name = String(mclass->GetName()) + TEXT(".") + String(itemName) + + // Process methods + const auto& methods = mclass->GetMethods(); + for (MMethod* method : methods) + { + if (!method->IsStatic()) + continue; + const StringAnsi& name = method->GetName(); + if (name.Contains("Internal_") || + mclass->GetFullName().Contains(".Interop.")) + continue; + if (name.StartsWith("get_") || + name.StartsWith("set_") || + name.StartsWith("op_") || + name.StartsWith("add_") || + name.StartsWith("remove_")) + continue; + if (!useClass && !method->HasAttribute(attribute)) + continue; + + auto& commandData = Commands.AddOne(); + BUILD_NAME(commandData, method->GetName()); + commandData.Module = module; + commandData.Method = method; + } + + // Process fields + const auto& fields = mclass->GetFields(); + for (MField* field : fields) + { + if (!field->IsStatic()) + continue; + if (!useClass && !field->HasAttribute(attribute)) + continue; + + auto& commandData = Commands.AddOne(); + BUILD_NAME(commandData, field->GetName()); + commandData.Module = module; + commandData.Field = field; + } + + // Process properties + const auto& properties = mclass->GetProperties(); + for (MProperty* property : properties) + { + if (!property->IsStatic()) + continue; + if (!useClass && !property->HasAttribute(attribute)) + continue; + + auto& commandData = Commands.AddOne(); + BUILD_NAME(commandData, property->GetName()); + commandData.Module = module; + commandData.MethodGet = property->GetGetMethod(); + commandData.MethodSet = property->GetSetMethod(); + } + } +#undef BUILD_NAME + } + else +#endif + { + // TODO: implement generic search for other modules (eg. Visual Scripts) + } + } + + void OnScriptsReloading() + { + // Reset + Inited = false; + Commands.Clear(); + } + + void InitCommands() + { + PROFILE_CPU(); + Inited = true; + const auto& modules = BinaryModule::GetModules(); + for (BinaryModule* module : modules) + { + OnBinaryModuleLoaded(module); + } + Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded); + Scripting::ScriptsReloading.Bind(&OnScriptsReloading); + } +} + +class DebugCommandsService : public EngineService +{ +public: + DebugCommandsService() + : EngineService(TEXT("DebugCommands"), 0) + { + } + + void Dispose() override + { + // Cleanup + ScopeLock lock(Locker); + Scripting::BinaryModuleLoaded.Unbind(&OnBinaryModuleLoaded); + Scripting::ScriptsReloading.Unbind(&OnScriptsReloading); + Commands.Clear(); + Inited = true; + } +}; + +DebugCommandsService DebugCommandsServiceInstance; + +void DebugCommands::Execute(StringView command) +{ + // Preprocess command text + while (command.HasChars() && StringUtils::IsWhitespace(command[0])) + command = StringView(command.Get() + 1, command.Length() - 1); + while (command.HasChars() && StringUtils::IsWhitespace(command[command.Length() - 1])) + command = StringView(command.Get(), command.Length() - 1); + if (command.IsEmpty()) + return; + StringView name = command; + StringView args; + int32 argsStart = name.Find(' '); + if (argsStart != -1) + { + name = command.Left(argsStart); + args = command.Right(argsStart + 1); + } + + // Ensure that commands cache has been created + ScopeLock lock(Locker); + if (!Inited) + InitCommands(); + + // Find command to run + for (const CommandData& command : Commands) + { + if (name.Length() == command.Name.Length() && + StringUtils::CompareIgnoreCase(name.Get(), command.Name.Get(), name.Length()) == 0) + { + command.Invoke(args); + return; + } + } + + LOG(Error, "Unknown command '{}'", name); +} + +bool DebugCommands::Iterate(const StringView& searchText, int32& index) +{ + ScopeLock lock(Locker); + if (index >= 0) + { + if (!Inited) + InitCommands(); + while (index < Commands.Count()) + { + auto& command = Commands.Get()[index]; + if (command.Name.StartsWith(searchText, StringSearchCase::IgnoreCase)) + { + return true; + } + index++; + } + } + return false; +} + +String DebugCommands::GetCommandName(int32 index) +{ + ScopeLock lock(Locker); + CHECK_RETURN(Commands.IsValidIndex(index), String::Empty); + return Commands.Get()[index].Name; +} diff --git a/Source/Engine/Debug/DebugCommands.cs b/Source/Engine/Debug/DebugCommands.cs new file mode 100644 index 000000000..3c67ca31e --- /dev/null +++ b/Source/Engine/Debug/DebugCommands.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine +{ + /// + /// Marks static method as debug command that can be executed from the command line or via console. + /// + [Serializable] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public sealed class DebugCommand : Attribute + { + } +} diff --git a/Source/Engine/Debug/DebugCommands.h b/Source/Engine/Debug/DebugCommands.h new file mode 100644 index 000000000..cedf39f57 --- /dev/null +++ b/Source/Engine/Debug/DebugCommands.h @@ -0,0 +1,24 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Scripting/ScriptingType.h" + +/// +/// Debug commands and console variables system. +/// +API_CLASS(static) class FLAXENGINE_API DebugCommands +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(DebugCommands); + +public: + /// + /// Executees the command. + /// + /// The command line (optionally with arguments). + API_FUNCTION() static void Execute(StringView command); + +public: + static bool Iterate(const StringView& searchText, int32& index); + static String GetCommandName(int32 index); +}; diff --git a/Source/Engine/Engine/Engine.h b/Source/Engine/Engine/Engine.h index 85ffefda0..2f1728aa2 100644 --- a/Source/Engine/Engine/Engine.h +++ b/Source/Engine/Engine/Engine.h @@ -97,7 +97,7 @@ public: /// Exits the engine. /// /// The exit code. - static void Exit(int32 exitCode = -1); + API_FUNCTION(Attributes="DebugCommand") static void Exit(int32 exitCode = -1); /// /// Requests normal engine exit. diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 09a5b9108..427e2260b 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -775,7 +775,7 @@ void Foliage::OnFoliageTypeModelLoaded(int32 index) } BoundingSphere::FromBox(_box, _sphere); if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Bounds); } { PROFILE_CPU_NAMED("Create Clusters"); @@ -912,7 +912,7 @@ void Foliage::RebuildClusters() _box = totalBounds; BoundingSphere::FromBox(_box, _sphere); if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Bounds); } // Insert all instances to the clusters @@ -986,6 +986,12 @@ void Foliage::UpdateCullDistance() #endif } +void Foliage::RemoveAllInstances() +{ + Instances.Clear(); + RebuildClusters(); +} + static float GlobalDensityScale = 1.0f; float Foliage::GetGlobalDensityScale() @@ -1441,7 +1447,7 @@ void Foliage::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie void Foliage::OnLayerChanged() { if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Layer); } void Foliage::OnEnable() diff --git a/Source/Engine/Foliage/Foliage.h b/Source/Engine/Foliage/Foliage.h index d10a75a7e..c062279a8 100644 --- a/Source/Engine/Foliage/Foliage.h +++ b/Source/Engine/Foliage/Foliage.h @@ -134,6 +134,11 @@ public: /// API_FUNCTION() void UpdateCullDistance(); + /// + /// Clears all foliage instances. Preserves the foliage types and other properties. + /// + API_FUNCTION() void RemoveAllInstances(); + public: /// /// Gets the global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices. diff --git a/Source/Engine/Graphics/Graphics.h b/Source/Engine/Graphics/Graphics.h index 4c5e0ad26..fdcc706f7 100644 --- a/Source/Engine/Graphics/Graphics.h +++ b/Source/Engine/Graphics/Graphics.h @@ -9,7 +9,7 @@ /// /// Graphics device manager that creates, manages and releases graphics device and related objects. /// -API_CLASS(Static) class FLAXENGINE_API Graphics +API_CLASS(Static, Attributes="DebugCommand") class FLAXENGINE_API Graphics { DECLARE_SCRIPTING_TYPE_NO_SPAWN(Graphics); public: diff --git a/Source/Engine/Graphics/Materials/MaterialShader.cpp b/Source/Engine/Graphics/Materials/MaterialShader.cpp index 63faaf300..ef119245d 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShader.cpp @@ -87,6 +87,7 @@ void IMaterial::BindParameters::BindViewData() void IMaterial::BindParameters::BindDrawData() { // Write draw call to the object buffer + ASSERT(DrawCall); auto& objectBuffer = RenderContext.List->TempObjectBuffer; objectBuffer.Clear(); ShaderObjectData objData; diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index a13085651..b09b76394 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -758,7 +758,7 @@ void AnimatedModel::UpdateBounds() } BoundingSphere::FromBox(_box, _sphere); if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Bounds); } void AnimatedModel::UpdateSockets() diff --git a/Source/Engine/Level/Actors/Decal.cpp b/Source/Engine/Level/Actors/Decal.cpp index dbec91b57..2e42fb6a9 100644 --- a/Source/Engine/Level/Actors/Decal.cpp +++ b/Source/Engine/Level/Actors/Decal.cpp @@ -64,7 +64,7 @@ BoundingBox Decal::GetEditorBox() const void Decal::OnLayerChanged() { if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Layer); } void Decal::Draw(RenderContext& renderContext) diff --git a/Source/Engine/Level/Actors/EnvironmentProbe.cpp b/Source/Engine/Level/Actors/EnvironmentProbe.cpp index ada74d410..8d7155dee 100644 --- a/Source/Engine/Level/Actors/EnvironmentProbe.cpp +++ b/Source/Engine/Level/Actors/EnvironmentProbe.cpp @@ -166,7 +166,7 @@ void EnvironmentProbe::UpdateBounds() _sphere = BoundingSphere(GetPosition(), GetScaledRadius()); BoundingBox::FromSphere(_sphere, _box); if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Bounds); } void EnvironmentProbe::Draw(RenderContext& renderContext) @@ -220,7 +220,7 @@ void EnvironmentProbe::OnDebugDrawSelected() void EnvironmentProbe::OnLayerChanged() { if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Layer); } void EnvironmentProbe::Serialize(SerializeStream& stream, const void* otherObj) diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.cpp b/Source/Engine/Level/Actors/ModelInstanceActor.cpp index 054e07b15..6021daadc 100644 --- a/Source/Engine/Level/Actors/ModelInstanceActor.cpp +++ b/Source/Engine/Level/Actors/ModelInstanceActor.cpp @@ -20,7 +20,7 @@ void ModelInstanceActor::SetEntries(const Array& value) Entries[i] = value[i]; } if (anyChanged && _sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Visual); } void ModelInstanceActor::SetMaterial(int32 entryIndex, MaterialBase* material) @@ -33,7 +33,7 @@ void ModelInstanceActor::SetMaterial(int32 entryIndex, MaterialBase* material) return; Entries[entryIndex].Material = material; if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Visual); } MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32 entryIndex) @@ -44,7 +44,7 @@ MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32 MaterialInstance* result = material->CreateVirtualInstance(); Entries[entryIndex].Material = result; if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Visual); return result; } @@ -55,7 +55,13 @@ void ModelInstanceActor::WaitForModelLoad() void ModelInstanceActor::OnLayerChanged() { if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Layer); +} + +void ModelInstanceActor::OnStaticFlagsChanged() +{ + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::StaticFlags); } void ModelInstanceActor::OnTransformChanged() diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.h b/Source/Engine/Level/Actors/ModelInstanceActor.h index c7cf2001b..f3c6cd5e1 100644 --- a/Source/Engine/Level/Actors/ModelInstanceActor.h +++ b/Source/Engine/Level/Actors/ModelInstanceActor.h @@ -142,6 +142,7 @@ protected: public: // [Actor] void OnLayerChanged() override; + void OnStaticFlagsChanged() override; void OnTransformChanged() override; protected: diff --git a/Source/Engine/Level/Actors/PointLight.cpp b/Source/Engine/Level/Actors/PointLight.cpp index fbe3fd9e6..6d26df2d9 100644 --- a/Source/Engine/Level/Actors/PointLight.cpp +++ b/Source/Engine/Level/Actors/PointLight.cpp @@ -67,7 +67,7 @@ void PointLight::UpdateBounds() BoundingBox::FromSphere(_sphere, _box); if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Bounds); } void PointLight::OnTransformChanged() @@ -163,7 +163,7 @@ void PointLight::DrawLightsDebug(RenderView& view) void PointLight::OnLayerChanged() { if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Layer); } void PointLight::Serialize(SerializeStream& stream, const void* otherObj) diff --git a/Source/Engine/Level/Actors/SkyLight.cpp b/Source/Engine/Level/Actors/SkyLight.cpp index 19152a557..88f083a4c 100644 --- a/Source/Engine/Level/Actors/SkyLight.cpp +++ b/Source/Engine/Level/Actors/SkyLight.cpp @@ -101,7 +101,7 @@ void SkyLight::UpdateBounds() _sphere = BoundingSphere(GetPosition(), GetScaledRadius()); BoundingBox::FromSphere(_sphere, _box); if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Bounds); } void SkyLight::Draw(RenderContext& renderContext) diff --git a/Source/Engine/Level/Actors/SpotLight.cpp b/Source/Engine/Level/Actors/SpotLight.cpp index 9494c14fa..54846e156 100644 --- a/Source/Engine/Level/Actors/SpotLight.cpp +++ b/Source/Engine/Level/Actors/SpotLight.cpp @@ -115,7 +115,7 @@ void SpotLight::UpdateBounds() BoundingBox::FromSphere(_sphere, _box); if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Bounds); } void SpotLight::OnTransformChanged() diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index 81f30df0c..3840ab5ce 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -283,7 +283,7 @@ void StaticModel::UpdateBounds() } BoundingSphere::FromBox(_box, _sphere); if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Bounds); } void StaticModel::FlushVertexColors() diff --git a/Source/Engine/Level/Scene/SceneRendering.cpp b/Source/Engine/Level/Scene/SceneRendering.cpp index 69d8edf4c..3dbbd113f 100644 --- a/Source/Engine/Level/Scene/SceneRendering.cpp +++ b/Source/Engine/Level/Scene/SceneRendering.cpp @@ -162,7 +162,7 @@ void SceneRendering::AddActor(Actor* a, int32& key) listener->OnSceneRenderingAddActor(a); } -void SceneRendering::UpdateActor(Actor* a, int32& key) +void SceneRendering::UpdateActor(Actor* a, int32& key, ISceneRenderingListener::UpdateFlags flags) { const int32 category = a->_drawCategory; ScopeLock lock(Locker); @@ -173,9 +173,11 @@ void SceneRendering::UpdateActor(Actor* a, int32& key) if (e.Actor == a) { for (auto* listener : _listeners) - listener->OnSceneRenderingUpdateActor(a, e.Bounds); - e.LayerMask = a->GetLayerMask(); - e.Bounds = a->GetSphere(); + listener->OnSceneRenderingUpdateActor(a, e.Bounds, flags); + if (flags & ISceneRenderingListener::Layer) + e.LayerMask = a->GetLayerMask(); + if (flags & ISceneRenderingListener::Bounds) + e.Bounds = a->GetSphere(); } } diff --git a/Source/Engine/Level/Scene/SceneRendering.h b/Source/Engine/Level/Scene/SceneRendering.h index 45c8d51cd..88716d9e8 100644 --- a/Source/Engine/Level/Scene/SceneRendering.h +++ b/Source/Engine/Level/Scene/SceneRendering.h @@ -48,12 +48,22 @@ private: public: ~ISceneRenderingListener(); + // Actor properties that were modified. + enum UpdateFlags + { + Visual = 1, + Bounds = 2, + Layer = 4, + StaticFlags = 8, + Auto = Visual | Bounds | Layer, + }; + // Starts listening to the scene rendering events. void ListenSceneRendering(SceneRendering* scene); // Events called by Scene Rendering virtual void OnSceneRenderingAddActor(Actor* a) = 0; - virtual void OnSceneRenderingUpdateActor(Actor* a, const BoundingSphere& prevBounds) = 0; + virtual void OnSceneRenderingUpdateActor(Actor* a, const BoundingSphere& prevBounds, UpdateFlags flags = Auto) = 0; virtual void OnSceneRenderingRemoveActor(Actor* a) = 0; virtual void OnSceneRenderingClear(SceneRendering* scene) = 0; }; @@ -125,7 +135,7 @@ public: public: void AddActor(Actor* a, int32& key); - void UpdateActor(Actor* a, int32& key); + void UpdateActor(Actor* a, int32& key, ISceneRenderingListener::UpdateFlags flags = ISceneRenderingListener::Auto); void RemoveActor(Actor* a, int32& key); FORCE_INLINE void AddPostFxProvider(IPostFxSettingsProvider* obj) diff --git a/Source/Engine/Networking/Drivers/NetworkLagDriver.cpp b/Source/Engine/Networking/Drivers/NetworkLagDriver.cpp index 35d1ac5d4..81c02c6f4 100644 --- a/Source/Engine/Networking/Drivers/NetworkLagDriver.cpp +++ b/Source/Engine/Networking/Drivers/NetworkLagDriver.cpp @@ -132,9 +132,10 @@ void NetworkLagDriver::SendMessage(const NetworkChannelType channelType, const N auto& msg = _messages.AddOne(); msg.Lag = (double)Lag; + msg.ChannelType = channelType; msg.Type = 0; - msg.Message = message; msg.MessageData.Set(message.Buffer, message.Length); + msg.MessageLength = message.Length; } void NetworkLagDriver::SendMessage(NetworkChannelType channelType, const NetworkMessage& message, NetworkConnection target) @@ -147,10 +148,11 @@ void NetworkLagDriver::SendMessage(NetworkChannelType channelType, const Network auto& msg = _messages.AddOne(); msg.Lag = (double)Lag; + msg.ChannelType = channelType; msg.Type = 1; - msg.Message = message; msg.Target = target; msg.MessageData.Set(message.Buffer, message.Length); + msg.MessageLength = message.Length; } void NetworkLagDriver::SendMessage(const NetworkChannelType channelType, const NetworkMessage& message, const Array& targets) @@ -163,10 +165,11 @@ void NetworkLagDriver::SendMessage(const NetworkChannelType channelType, const N auto& msg = _messages.AddOne(); msg.Lag = (double)Lag; + msg.ChannelType = channelType; msg.Type = 2; - msg.Message = message; msg.Targets = targets; msg.MessageData.Set(message.Buffer, message.Length); + msg.MessageLength = message.Length; } NetworkDriverStats NetworkLagDriver::GetStats() @@ -197,19 +200,21 @@ void NetworkLagDriver::OnUpdate() if (msg.Lag > 0.0) continue; - // Fix message to point to the current buffer - msg.Message.Buffer = msg.MessageData.Get(); + // Use this helper message as a container to send the stored data and length to the ENet driver + NetworkMessage message; + message.Buffer = msg.MessageData.Get(); + message.Length = msg.MessageLength; switch (msg.Type) { case 0: - _driver->SendMessage(msg.ChannelType, msg.Message); + _driver->SendMessage(msg.ChannelType, message); break; case 1: - _driver->SendMessage(msg.ChannelType, msg.Message, msg.Target); + _driver->SendMessage(msg.ChannelType, message, msg.Target); break; case 2: - _driver->SendMessage(msg.ChannelType, msg.Message, msg.Targets); + _driver->SendMessage(msg.ChannelType, message, msg.Targets); break; } _messages.RemoveAt(i); diff --git a/Source/Engine/Networking/Drivers/NetworkLagDriver.h b/Source/Engine/Networking/Drivers/NetworkLagDriver.h index 1fd1a455a..36a89fcbb 100644 --- a/Source/Engine/Networking/Drivers/NetworkLagDriver.h +++ b/Source/Engine/Networking/Drivers/NetworkLagDriver.h @@ -22,10 +22,10 @@ private: double Lag; int32 Type; NetworkChannelType ChannelType; - NetworkMessage Message; NetworkConnection Target; Array Targets; Array MessageData; // TODO: use message buffers cache (of size config.MessageSize) to reduce dynamic memory allocations + uint32 MessageLength; }; struct LagEvent diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp index 224ee9f31..765bb57b9 100644 --- a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp @@ -74,6 +74,17 @@ bool NetworkReplicationNode::GetObject(ScriptingObject* obj, NetworkReplicationH return false; } +bool NetworkReplicationNode::SetObject(const NetworkReplicationHierarchyObject& value) +{ + const int32 index = Objects.Find(value.Object.Get()); + if (index != -1) + { + Objects[index] = value; + return true; + } + return false; +} + bool NetworkReplicationNode::DirtyObject(ScriptingObject* obj) { const int32 index = Objects.Find(obj); @@ -212,11 +223,17 @@ bool NetworkReplicationGridNode::GetObject(ScriptingObject* obj, NetworkReplicat { return false; } - if (_children[coord].Node->GetObject(obj, result)) + return _children[coord].Node->GetObject(obj, result); +} + +bool NetworkReplicationGridNode::SetObject(const NetworkReplicationHierarchyObject& value) +{ + Int3 coord; + if (!_objectToCell.TryGet(value.Object.Get(), coord)) { - return true; + return false; } - return false; + return _children[coord].Node->SetObject(value); } bool NetworkReplicationGridNode::DirtyObject(ScriptingObject* obj) diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.h b/Source/Engine/Networking/NetworkReplicationHierarchy.h index 78f9cad1f..b58602254 100644 --- a/Source/Engine/Networking/NetworkReplicationHierarchy.h +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.h @@ -206,7 +206,14 @@ API_CLASS(Abstract, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API Ne /// The object to get. /// The hierarchy object to retrieve. /// True on successful retrieval, otherwise false. - API_FUNCTION() virtual bool GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result); + API_FUNCTION() virtual bool GetObject(ScriptingObject* obj, API_PARAM(Out) NetworkReplicationHierarchyObject& result); + + /// + /// Sets object properties in the hierarchy. Can be used to modify replication settings at runtime. + /// + /// The object data to update. + /// True on successful update, otherwise false (eg, if specific object has not been added to this node). + API_FUNCTION() virtual bool SetObject(const NetworkReplicationHierarchyObject& value); /// /// Force replicates the object during the next update. Resets any internal tracking state to force the synchronization. @@ -257,6 +264,7 @@ public: void AddObject(NetworkReplicationHierarchyObject obj) override; bool RemoveObject(ScriptingObject* obj) override; bool GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result) override; + bool SetObject(const NetworkReplicationHierarchyObject& value) override; bool DirtyObject(ScriptingObject* obj) override; void Update(NetworkReplicationHierarchyUpdateResult* result) override; }; diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp index e517ab9b9..a2c8ba03c 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp @@ -16,6 +16,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParameters(Box* box, Node* nod switch (node->TypeID) { // Get + case 1: case 2: { int32 paramIndex; diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp index 92f5b94a0..f06a9b945 100644 --- a/Source/Engine/Particles/ParticleEffect.cpp +++ b/Source/Engine/Particles/ParticleEffect.cpp @@ -589,7 +589,7 @@ void ParticleEffect::OnDebugDrawSelected() void ParticleEffect::OnLayerChanged() { if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Layer); } void ParticleEffect::Serialize(SerializeStream& stream, const void* otherObj) diff --git a/Source/Engine/Platform/Android/AndroidFileSystem.cpp b/Source/Engine/Platform/Android/AndroidFileSystem.cpp index 1cea6715e..88114c899 100644 --- a/Source/Engine/Platform/Android/AndroidFileSystem.cpp +++ b/Source/Engine/Platform/Android/AndroidFileSystem.cpp @@ -80,7 +80,7 @@ namespace // Determinate a full path of an entry char full_path[256]; ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(full_path)); - strcpy(full_path, path); + strcpy(full_path, pathStr); strcat(full_path, "/"); strcat(full_path, entry->d_name); diff --git a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp index 69afa23a4..cb4fb15cd 100644 --- a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp +++ b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp @@ -227,7 +227,7 @@ bool DeleteUnixPathTree(const char* path) // Determinate a full path of an entry char full_path[256]; ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(full_path)); - strcpy(full_path, path); + strcpy(full_path, pathStr); strcat(full_path, "/"); strcat(full_path, entry->d_name); diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index c4a39820b..be5992bde 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -499,7 +499,7 @@ public: { } - void OnSceneRenderingUpdateActor(Actor* a, const BoundingSphere& prevBounds) override + void OnSceneRenderingUpdateActor(Actor* a, const BoundingSphere& prevBounds, UpdateFlags flags) override { // Dirty static objects to redraw when changed (eg. material modification) if (a->HasStaticFlag(StaticFlags::Lightmap)) diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp index 173604069..017146f8b 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp @@ -399,7 +399,7 @@ public: } } - void OnSceneRenderingUpdateActor(Actor* a, const BoundingSphere& prevBounds) override + void OnSceneRenderingUpdateActor(Actor* a, const BoundingSphere& prevBounds, UpdateFlags flags) override { if (GLOBAL_SDF_ACTOR_IS_STATIC(a) && ObjectTypes.Contains(a->GetTypeHandle())) { diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index a8cf19542..b30005b51 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -392,7 +392,7 @@ public: DirtyStaticBounds(a->GetSphere()); } - void OnSceneRenderingUpdateActor(Actor* a, const BoundingSphere& prevBounds) override + void OnSceneRenderingUpdateActor(Actor* a, const BoundingSphere& prevBounds, UpdateFlags flags) override { // Dirty static objects to redraw when changed (eg. material modification) if (a->HasStaticFlag(StaticFlags::Shadow)) @@ -400,6 +400,10 @@ public: DirtyStaticBounds(prevBounds); DirtyStaticBounds(a->GetSphere()); } + else if (flags & StaticFlags) + { + DirtyStaticBounds(a->GetSphere()); + } } void OnSceneRenderingRemoveActor(Actor* a) override diff --git a/Source/Engine/Renderer/VolumetricFogPass.cpp b/Source/Engine/Renderer/VolumetricFogPass.cpp index 2b9a4b441..d9a77bc37 100644 --- a/Source/Engine/Renderer/VolumetricFogPass.cpp +++ b/Source/Engine/Renderer/VolumetricFogPass.cpp @@ -60,9 +60,10 @@ bool VolumetricFogPass::setupResources() REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data); return true; } - if (shader->GetCB(1)->GetSize() != sizeof(PerLight)) + // CB1 is used for per-draw info (ObjectIndex) + if (shader->GetCB(2)->GetSize() != sizeof(PerLight)) { - REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, 1, PerLight); + REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, 2, PerLight); return true; } @@ -254,7 +255,7 @@ GPUTextureView* VolumetricFogPass::GetLocalShadowedLightScattering(RenderContext } template -void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUContext* context, RenderView& view, VolumetricFogOptions& options, T& light, PerLight& perLight, GPUConstantBuffer* cb1) +void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUContext* context, RenderView& view, VolumetricFogOptions& options, T& light, PerLight& perLight, GPUConstantBuffer* cb2) { const Float3 center = light.Position; const float radius = light.Radius; @@ -281,8 +282,8 @@ void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUConte light.SetShaderData(perLight.LocalLight, withShadow); // Upload data - context->UpdateCB(cb1, &perLight); - context->BindCB(1, cb1); + context->UpdateCB(cb2, &perLight); + context->BindCB(2, cb2); // Ensure to have valid buffers created if (_vbCircleRasterize == nullptr || _ibCircleRasterize == nullptr) @@ -414,6 +415,8 @@ void VolumetricFogPass::Render(RenderContext& renderContext) customData.VolumetricFogMaxDistance = cache.Data.VolumetricFogMaxDistance; bindParams.CustomData = &customData; bindParams.BindViewData(); + bindParams.DrawCall = &renderContext.List->VolumetricFogParticles.First(); + bindParams.BindDrawData(); for (auto& drawCall : renderContext.List->VolumetricFogParticles) { @@ -439,7 +442,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext) // Setup volumetric shader data PerLight perLight; - auto cb1 = _shader->GetShader()->GetCB(1); + auto cb2 = _shader->GetShader()->GetCB(2); perLight.SliceToDepth.X = cache.Data.GridSize.Z; perLight.SliceToDepth.Y = cache.Data.VolumetricFogMaxDistance; perLight.MinZ = volumeZBoundsMin; @@ -447,8 +450,8 @@ void VolumetricFogPass::Render(RenderContext& renderContext) Matrix::Transpose(renderContext.View.Projection, perLight.ViewToVolumeClip); // Upload data - context->UpdateCB(cb1, &perLight); - context->BindCB(1, cb1); + context->UpdateCB(cb2, &perLight); + context->BindCB(2, cb2); // Call rendering to the volume const int32 instanceCount = volumeZBoundsMax - volumeZBoundsMin; @@ -495,7 +498,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext) PerLight perLight; perLight.SliceToDepth.X = cache.Data.GridSize.Z; perLight.SliceToDepth.Y = cache.Data.VolumetricFogMaxDistance; - auto cb1 = _shader->GetShader()->GetCB(1); + auto cb2 = _shader->GetShader()->GetCB(2); // Bind the output context->SetRenderTarget(localShadowedLightScattering); @@ -505,12 +508,12 @@ void VolumetricFogPass::Render(RenderContext& renderContext) context->BindSR(0, shadowMap); context->BindSR(1, shadowsBuffer); for (int32 i = 0; i < pointLights.Count(); i++) - RenderRadialLight(renderContext, context, view, options, renderContext.List->PointLights[pointLights[i]], perLight, cb1); + RenderRadialLight(renderContext, context, view, options, renderContext.List->PointLights[pointLights[i]], perLight, cb2); for (int32 i = 0; i < spotLights.Count(); i++) - RenderRadialLight(renderContext, context, view, options, renderContext.List->SpotLights[spotLights[i]], perLight, cb1); + RenderRadialLight(renderContext, context, view, options, renderContext.List->SpotLights[spotLights[i]], perLight, cb2); // Cleanup - context->UnBindCB(1); + context->UnBindCB(2); context->ResetRenderTarget(); context->FlushState(); } diff --git a/Source/Engine/Renderer/VolumetricFogPass.h b/Source/Engine/Renderer/VolumetricFogPass.h index 0489bb88a..156f485f8 100644 --- a/Source/Engine/Renderer/VolumetricFogPass.h +++ b/Source/Engine/Renderer/VolumetricFogPass.h @@ -157,7 +157,7 @@ private: GPUTextureView* GetLocalShadowedLightScattering(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options) const; void InitCircleBuffer(); template - void RenderRadialLight(RenderContext& renderContext, GPUContext* context, RenderView& view, VolumetricFogOptions& options, T& light, PerLight& perLight, GPUConstantBuffer* cb1); + void RenderRadialLight(RenderContext& renderContext, GPUContext* context, RenderView& view, VolumetricFogOptions& options, T& light, PerLight& perLight, GPUConstantBuffer* cb2); #if COMPILE_WITH_DEV_ENV void OnShaderReloading(Asset* obj) { diff --git a/Source/Engine/Scripting/ManagedCLR/MClass.h b/Source/Engine/Scripting/ManagedCLR/MClass.h index 258ea9b08..7d25c4069 100644 --- a/Source/Engine/Scripting/ManagedCLR/MClass.h +++ b/Source/Engine/Scripting/ManagedCLR/MClass.h @@ -305,9 +305,9 @@ public: /// /// Checks if class has an attribute of the specified type. /// - /// The attribute class to check. + /// The attribute class to check. /// True if has attribute of that class type, otherwise false. - bool HasAttribute(const MClass* monoClass) const; + bool HasAttribute(const MClass* klass) const; /// /// Checks if class has an attribute of any type. @@ -318,9 +318,9 @@ public: /// /// Returns an instance of an attribute of the specified type. Returns null if the class doesn't have such an attribute. /// - /// The attribute class to take. + /// The attribute class to take. /// The attribute object. - MObject* GetAttribute(const MClass* monoClass) const; + MObject* GetAttribute(const MClass* klass) const; /// /// Returns an instance of all attributes connected with given class. Returns null if the class doesn't have any attributes. diff --git a/Source/Engine/Scripting/ManagedCLR/MEvent.h b/Source/Engine/Scripting/ManagedCLR/MEvent.h index 03779344c..5692f3dc6 100644 --- a/Source/Engine/Scripting/ManagedCLR/MEvent.h +++ b/Source/Engine/Scripting/ManagedCLR/MEvent.h @@ -99,9 +99,9 @@ public: /// /// Checks if event has an attribute of the specified type. /// - /// The attribute class to check. + /// The attribute class to check. /// True if has attribute of that class type, otherwise false. - bool HasAttribute(MClass* monoClass) const; + bool HasAttribute(const MClass* klass) const; /// /// Checks if event has an attribute of any type. @@ -112,9 +112,9 @@ public: /// /// Returns an instance of an attribute of the specified type. Returns null if the event doesn't have such an attribute. /// - /// The attribute class to take. + /// The attribute class to take. /// The attribute object. - MObject* GetAttribute(MClass* monoClass) const; + MObject* GetAttribute(const MClass* klass) const; /// /// Returns an instance of all attributes connected with given event. Returns null if the event doesn't have any attributes. diff --git a/Source/Engine/Scripting/ManagedCLR/MField.h b/Source/Engine/Scripting/ManagedCLR/MField.h index 8d4eb6a12..5475f4535 100644 --- a/Source/Engine/Scripting/ManagedCLR/MField.h +++ b/Source/Engine/Scripting/ManagedCLR/MField.h @@ -134,9 +134,9 @@ public: /// /// Checks if field has an attribute of the specified type. /// - /// The attribute class to check. + /// The attribute class to check. /// True if has attribute of that class type, otherwise false. - bool HasAttribute(MClass* monoClass) const; + bool HasAttribute(const MClass* klass) const; /// /// Checks if field has an attribute of any type. @@ -147,9 +147,9 @@ public: /// /// Returns an instance of an attribute of the specified type. Returns null if the field doesn't have such an attribute. /// - /// The attribute class to take. + /// The attribute class to take. /// The attribute object. - MObject* GetAttribute(MClass* monoClass) const; + MObject* GetAttribute(const MClass* klass) const; /// /// Returns an instance of all attributes connected with given field. Returns null if the field doesn't have any attributes. diff --git a/Source/Engine/Scripting/ManagedCLR/MMethod.h b/Source/Engine/Scripting/ManagedCLR/MMethod.h index 249c71e4e..f2b127871 100644 --- a/Source/Engine/Scripting/ManagedCLR/MMethod.h +++ b/Source/Engine/Scripting/ManagedCLR/MMethod.h @@ -175,9 +175,9 @@ public: /// /// Checks if method has an attribute of the specified type. /// - /// The attribute class to check. + /// The attribute class to check. /// True if has attribute of that class type, otherwise false. - bool HasAttribute(MClass* monoClass) const; + bool HasAttribute(const MClass* klass) const; /// /// Checks if method has an attribute of any type. @@ -188,9 +188,9 @@ public: /// /// Returns an instance of an attribute of the specified type. Returns null if the method doesn't have such an attribute. /// - /// The attribute Class to take. + /// The attribute Class to take. /// The attribute object. - MObject* GetAttribute(MClass* monoClass) const; + MObject* GetAttribute(const MClass* klass) const; /// /// Returns an instance of all attributes connected with given method. Returns null if the method doesn't have any attributes. diff --git a/Source/Engine/Scripting/ManagedCLR/MProperty.h b/Source/Engine/Scripting/ManagedCLR/MProperty.h index bd3bebddf..a9ce918f8 100644 --- a/Source/Engine/Scripting/ManagedCLR/MProperty.h +++ b/Source/Engine/Scripting/ManagedCLR/MProperty.h @@ -111,9 +111,9 @@ public: /// /// Checks if property has an attribute of the specified type. /// - /// The attribute class to check. + /// The attribute class to check. /// True if has attribute of that class type, otherwise false. - bool HasAttribute(MClass* monoClass) const; + bool HasAttribute(const MClass* klass) const; /// /// Checks if property has an attribute of any type. @@ -124,9 +124,9 @@ public: /// /// Returns an instance of an attribute of the specified type. Returns null if the property doesn't have such an attribute. /// - /// The attribute class to take. + /// The attribute class to take. /// The attribute object. - MObject* GetAttribute(MClass* monoClass) const; + MObject* GetAttribute(const MClass* klass) const; /// /// Returns an instance of all attributes connected with given property. Returns null if the property doesn't have any attributes. diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 25213ec46..26b011855 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -1119,9 +1119,9 @@ const Array& MClass::GetInterfaces() const return _interfaces; } -bool MClass::HasAttribute(const MClass* monoClass) const +bool MClass::HasAttribute(const MClass* klass) const { - return GetCustomAttribute(this, monoClass) != nullptr; + return GetCustomAttribute(this, klass) != nullptr; } bool MClass::HasAttribute() const @@ -1129,9 +1129,9 @@ bool MClass::HasAttribute() const return !GetAttributes().IsEmpty(); } -MObject* MClass::GetAttribute(const MClass* monoClass) const +MObject* MClass::GetAttribute(const MClass* klass) const { - return (MObject*)GetCustomAttribute(this, monoClass); + return (MObject*)GetCustomAttribute(this, klass); } const Array& MClass::GetAttributes() const @@ -1185,7 +1185,7 @@ MMethod* MEvent::GetRemoveMethod() const return nullptr; // TODO: implement MEvent in .NET } -bool MEvent::HasAttribute(MClass* monoClass) const +bool MEvent::HasAttribute(const MClass* klass) const { return false; // TODO: implement MEvent in .NET } @@ -1195,7 +1195,7 @@ bool MEvent::HasAttribute() const return false; // TODO: implement MEvent in .NET } -MObject* MEvent::GetAttribute(MClass* monoClass) const +MObject* MEvent::GetAttribute(const MClass* klass) const { return nullptr; // TODO: implement MEvent in .NET } @@ -1307,7 +1307,7 @@ void MField::SetValue(MObject* instance, void* value) const CallStaticMethod(FieldSetValuePtr, instance, _handle, value); } -bool MField::HasAttribute(MClass* monoClass) const +bool MField::HasAttribute(const MClass* klass) const { // TODO: implement MField attributes in .NET return false; @@ -1319,7 +1319,7 @@ bool MField::HasAttribute() const return false; } -MObject* MField::GetAttribute(MClass* monoClass) const +MObject* MField::GetAttribute(const MClass* klass) const { // TODO: implement MField attributes in .NET return nullptr; @@ -1469,7 +1469,7 @@ bool MMethod::GetParameterIsOut(int32 paramIdx) const return CallStaticMethod(GetMethodParameterIsOutPtr, _handle, paramIdx); } -bool MMethod::HasAttribute(MClass* monoClass) const +bool MMethod::HasAttribute(const MClass* klass) const { // TODO: implement MMethod attributes in .NET return false; @@ -1481,7 +1481,7 @@ bool MMethod::HasAttribute() const return false; } -MObject* MMethod::GetAttribute(MClass* monoClass) const +MObject* MMethod::GetAttribute(const MClass* klass) const { // TODO: implement MMethod attributes in .NET return nullptr; @@ -1546,7 +1546,7 @@ void MProperty::SetValue(MObject* instance, void* value, MObject** exception) co _setMethod->Invoke(instance, params, exception); } -bool MProperty::HasAttribute(MClass* monoClass) const +bool MProperty::HasAttribute(const MClass* klass) const { // TODO: implement MProperty attributes in .NET return false; @@ -1558,7 +1558,7 @@ bool MProperty::HasAttribute() const return false; } -MObject* MProperty::GetAttribute(MClass* monoClass) const +MObject* MProperty::GetAttribute(const MClass* klass) const { // TODO: implement MProperty attributes in .NET return nullptr; diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp index 40fa8f4bf..351e1569b 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -1517,10 +1517,10 @@ const Array& MClass::GetInterfaces() const return _interfaces; } -bool MClass::HasAttribute(const MClass* monoClass) const +bool MClass::HasAttribute(const MClass* klass) const { MonoCustomAttrInfo* attrInfo = GET_CUSTOM_ATTR(); - return attrInfo != nullptr && mono_custom_attrs_has_attr(attrInfo, monoClass->GetNative()) != 0; + return attrInfo != nullptr && mono_custom_attrs_has_attr(attrInfo, klass->GetNative()) != 0; } bool MClass::HasAttribute() const @@ -1529,10 +1529,10 @@ bool MClass::HasAttribute() const return attrInfo && attrInfo->num_attrs > 0; } -MObject* MClass::GetAttribute(const MClass* monoClass) const +MObject* MClass::GetAttribute(const MClass* klass) const { MonoCustomAttrInfo* attrInfo = GET_CUSTOM_ATTR(); - return attrInfo ? mono_custom_attrs_get_attr(attrInfo, monoClass->GetNative()) : nullptr; + return attrInfo ? mono_custom_attrs_get_attr(attrInfo, klass->GetNative()) : nullptr; } const Array& MClass::GetAttributes() const @@ -1618,14 +1618,14 @@ MMethod* MEvent::GetRemoveMethod() const return _removeMethod; } -bool MEvent::HasAttribute(MClass* monoClass) const +bool MEvent::HasAttribute(const MClass* klass) const { MonoClass* parentClass = mono_event_get_parent(_monoEvent); MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_event(parentClass, _monoEvent); if (attrInfo == nullptr) return false; - const bool hasAttr = mono_custom_attrs_has_attr(attrInfo, monoClass->GetNative()) != 0; + const bool hasAttr = mono_custom_attrs_has_attr(attrInfo, klass->GetNative()) != 0; mono_custom_attrs_free(attrInfo); return hasAttr; } @@ -1646,14 +1646,14 @@ bool MEvent::HasAttribute() const return false; } -MObject* MEvent::GetAttribute(MClass* monoClass) const +MObject* MEvent::GetAttribute(const MClass* klass) const { MonoClass* parentClass = mono_event_get_parent(_monoEvent); MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_event(parentClass, _monoEvent); if (attrInfo == nullptr) return nullptr; - MonoObject* foundAttr = mono_custom_attrs_get_attr(attrInfo, monoClass->GetNative()); + MonoObject* foundAttr = mono_custom_attrs_get_attr(attrInfo, klass->GetNative()); mono_custom_attrs_free(attrInfo); return foundAttr; } @@ -1771,14 +1771,14 @@ void MField::SetValue(MObject* instance, void* value) const mono_field_set_value(instance, _monoField, value); } -bool MField::HasAttribute(MClass* monoClass) const +bool MField::HasAttribute(const MClass* klass) const { MonoClass* parentClass = mono_field_get_parent(_monoField); MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_field(parentClass, _monoField); if (attrInfo == nullptr) return false; - const bool hasAttr = mono_custom_attrs_has_attr(attrInfo, monoClass->GetNative()) != 0; + const bool hasAttr = mono_custom_attrs_has_attr(attrInfo, klass->GetNative()) != 0; mono_custom_attrs_free(attrInfo); return hasAttr; } @@ -1799,14 +1799,14 @@ bool MField::HasAttribute() const return false; } -MObject* MField::GetAttribute(MClass* monoClass) const +MObject* MField::GetAttribute(const MClass* klass) const { MonoClass* parentClass = mono_field_get_parent(_monoField); MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_field(parentClass, _monoField); if (attrInfo == nullptr) return nullptr; - MonoObject* foundAttr = mono_custom_attrs_get_attr(attrInfo, monoClass->GetNative()); + MonoObject* foundAttr = mono_custom_attrs_get_attr(attrInfo, klass->GetNative()); mono_custom_attrs_free(attrInfo); return foundAttr; } @@ -1947,13 +1947,13 @@ bool MMethod::GetParameterIsOut(int32 paramIdx) const return mono_signature_param_is_out(sig, paramIdx) != 0; } -bool MMethod::HasAttribute(MClass* monoClass) const +bool MMethod::HasAttribute(const MClass* klass) const { MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_method(_monoMethod); if (attrInfo == nullptr) return false; - const bool hasAttr = mono_custom_attrs_has_attr(attrInfo, monoClass->GetNative()) != 0; + const bool hasAttr = mono_custom_attrs_has_attr(attrInfo, klass->GetNative()) != 0; mono_custom_attrs_free(attrInfo); return hasAttr; } @@ -1973,13 +1973,13 @@ bool MMethod::HasAttribute() const return false; } -MObject* MMethod::GetAttribute(MClass* monoClass) const +MObject* MMethod::GetAttribute(const MClass* klass) const { MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_method(_monoMethod); if (attrInfo == nullptr) return nullptr; - MonoObject* foundAttr = mono_custom_attrs_get_attr(attrInfo, monoClass->GetNative()); + MonoObject* foundAttr = mono_custom_attrs_get_attr(attrInfo, klass->GetNative()); mono_custom_attrs_free(attrInfo); return foundAttr; } @@ -2074,14 +2074,14 @@ void MProperty::SetValue(MObject* instance, void* value, MObject** exception) co mono_property_set_value(_monoProperty, instance, params, exception); } -bool MProperty::HasAttribute(MClass* monoClass) const +bool MProperty::HasAttribute(const MClass* klass) const { MonoClass* parentClass = mono_property_get_parent(_monoProperty); MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_property(parentClass, _monoProperty); if (attrInfo == nullptr) return false; - const bool hasAttr = mono_custom_attrs_has_attr(attrInfo, monoClass->GetNative()) != 0; + const bool hasAttr = mono_custom_attrs_has_attr(attrInfo, klass->GetNative()) != 0; mono_custom_attrs_free(attrInfo); return hasAttr; } @@ -2102,14 +2102,14 @@ bool MProperty::HasAttribute() const return false; } -MObject* MProperty::GetAttribute(MClass* monoClass) const +MObject* MProperty::GetAttribute(const MClass* klass) const { MonoClass* parentClass = mono_property_get_parent(_monoProperty); MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_property(parentClass, _monoProperty); if (attrInfo == nullptr) return nullptr; - MonoObject* foundAttr = mono_custom_attrs_get_attr(attrInfo, monoClass->GetNative()); + MonoObject* foundAttr = mono_custom_attrs_get_attr(attrInfo, klass->GetNative()); mono_custom_attrs_free(attrInfo); return foundAttr; } diff --git a/Source/Engine/Scripting/Runtime/None.cpp b/Source/Engine/Scripting/Runtime/None.cpp index f4cdd92e2..8731ed00d 100644 --- a/Source/Engine/Scripting/Runtime/None.cpp +++ b/Source/Engine/Scripting/Runtime/None.cpp @@ -388,7 +388,7 @@ const Array& MClass::GetProperties() const return _properties; } -bool MClass::HasAttribute(const MClass* monoClass) const +bool MClass::HasAttribute(const MClass* klass) const { return false; } @@ -398,7 +398,7 @@ bool MClass::HasAttribute() const return false; } -MObject* MClass::GetAttribute(const MClass* monoClass) const +MObject* MClass::GetAttribute(const MClass* klass) const { return nullptr; } @@ -430,7 +430,7 @@ MMethod* MEvent::GetRemoveMethod() const return _removeMethod; } -bool MEvent::HasAttribute(MClass* monoClass) const +bool MEvent::HasAttribute(const MClass* klass) const { return false; } @@ -440,7 +440,7 @@ bool MEvent::HasAttribute() const return false; } -MObject* MEvent::GetAttribute(MClass* monoClass) const +MObject* MEvent::GetAttribute(const MClass* klass) const { return nullptr; } @@ -482,7 +482,7 @@ void MField::SetValue(MObject* instance, void* value) const { } -bool MField::HasAttribute(MClass* monoClass) const +bool MField::HasAttribute(const MClass* klass) const { return false; } @@ -492,7 +492,7 @@ bool MField::HasAttribute() const return false; } -MObject* MField::GetAttribute(MClass* monoClass) const +MObject* MField::GetAttribute(const MClass* klass) const { return nullptr; } @@ -537,7 +537,7 @@ bool MMethod::GetParameterIsOut(int32 paramIdx) const return false; } -bool MMethod::HasAttribute(MClass* monoClass) const +bool MMethod::HasAttribute(const MClass* klass) const { return false; } @@ -547,7 +547,7 @@ bool MMethod::HasAttribute() const return false; } -MObject* MMethod::GetAttribute(MClass* monoClass) const +MObject* MMethod::GetAttribute(const MClass* klass) const { return nullptr; } @@ -584,7 +584,7 @@ void MProperty::SetValue(MObject* instance, void* value, MObject** exception) co { } -bool MProperty::HasAttribute(MClass* monoClass) const +bool MProperty::HasAttribute(const MClass* klass) const { return false; } @@ -594,7 +594,7 @@ bool MProperty::HasAttribute() const return false; } -MObject* MProperty::GetAttribute(MClass* monoClass) const +MObject* MProperty::GetAttribute(const MClass* klass) const { return nullptr; } diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index dfb89fadc..7f9d92369 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -277,10 +277,10 @@ void ScriptingObject::ChangeID(const Guid& newId) // Update managed instance const auto managedInstance = GetManagedInstance(); - const auto monoClass = GetClass(); - if (managedInstance && monoClass) + const auto klass = GetClass(); + if (managedInstance && klass) { - const MField* monoIdField = monoClass->GetField(ScriptingObject_id); + const MField* monoIdField = klass->GetField(ScriptingObject_id); if (monoIdField) monoIdField->SetValue(managedInstance, &_id); } @@ -344,10 +344,10 @@ bool ScriptingObject::CreateManaged() #endif { // Other thread already created the object before - if (const auto monoClass = GetClass()) + if (const auto klass = GetClass()) { // Reset managed to unmanaged pointer - MCore::ScriptingObject::SetInternalValues(monoClass, managedInstance, nullptr, nullptr); + MCore::ScriptingObject::SetInternalValues(klass, managedInstance, nullptr, nullptr); } MCore::GCHandle::Free(handle); return true; @@ -366,17 +366,17 @@ bool ScriptingObject::CreateManaged() MObject* ScriptingObject::CreateManagedInternal() { // Get class - MClass* monoClass = GetClass(); - if (monoClass == nullptr) + MClass* klass = GetClass(); + if (klass == nullptr) { LOG(Warning, "Missing managed class for object with id {0}", GetID()); return nullptr; } - MObject* managedInstance = MCore::ScriptingObject::CreateScriptingObject(monoClass, this, &_id); + MObject* managedInstance = MCore::ScriptingObject::CreateScriptingObject(klass, this, &_id); if (managedInstance == nullptr) { - LOG(Warning, "Failed to create new instance of the object of type {0}", String(monoClass->GetFullName())); + LOG(Warning, "Failed to create new instance of the object of type {0}", String(klass->GetFullName())); } return managedInstance; @@ -393,9 +393,9 @@ void ScriptingObject::DestroyManaged() // Reset managed to unmanaged pointer if (managedInstance) { - if (const auto monoClass = GetClass()) + if (const auto klass = GetClass()) { - MCore::ScriptingObject::SetInternalValues(monoClass, managedInstance, nullptr, nullptr); + MCore::ScriptingObject::SetInternalValues(klass, managedInstance, nullptr, nullptr); } } @@ -522,10 +522,10 @@ bool ManagedScriptingObject::CreateManaged() #endif { // Other thread already created the object before - if (const auto monoClass = GetClass()) + if (const auto klass = GetClass()) { // Reset managed to unmanaged pointer - MCore::ScriptingObject::SetInternalValues(monoClass, managedInstance, nullptr, nullptr); + MCore::ScriptingObject::SetInternalValues(klass, managedInstance, nullptr, nullptr); } MCore::GCHandle::Free(handle); return true; @@ -684,9 +684,9 @@ DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceCreated(MObject* manage actor->SetName(String(typeClass->GetName())); } - MClass* monoClass = obj->GetClass(); + MClass* klass = obj->GetClass(); const Guid id = obj->GetID(); - MCore::ScriptingObject::SetInternalValues(monoClass, managedInstance, obj, &id); + MCore::ScriptingObject::SetInternalValues(klass, managedInstance, obj, &id); // Register object if (!obj->IsRegistered()) diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index 10c137d33..204a78359 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -52,7 +52,7 @@ void Terrain::UpdateBounds() } BoundingSphere::FromBox(_box, _sphere); if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Bounds); } void Terrain::CacheNeighbors() @@ -905,7 +905,7 @@ void Terrain::OnLayerChanged() UpdateLayerBits(); if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Layer); } void Terrain::OnActiveInTreeChanged() diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 985ee193b..df5582cc3 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -233,7 +233,8 @@ namespace FlaxEngine.GUI if (IsMouseOver || IsNavFocused) backColor = BackgroundSelectedColor; Render2D.FillRectangle(rect, backColor); - Render2D.DrawRectangle(rect, IsFocused ? BorderSelectedColor : BorderColor); + if (HasBorder) + Render2D.DrawRectangle(rect, IsFocused ? BorderSelectedColor : BorderColor, BorderThickness); // Apply view offset and clip mask if (ClipText) diff --git a/Source/Engine/UI/SpriteRender.cpp b/Source/Engine/UI/SpriteRender.cpp index 4f28551b7..f0ddaaf60 100644 --- a/Source/Engine/UI/SpriteRender.cpp +++ b/Source/Engine/UI/SpriteRender.cpp @@ -186,7 +186,7 @@ void SpriteRender::Deserialize(DeserializeStream& stream, ISerializeModifier* mo void SpriteRender::OnLayerChanged() { if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Layer); } void SpriteRender::OnEndPlay() diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index 5a18d915c..05c6cdf73 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -415,7 +415,7 @@ void TextRender::OnDebugDrawSelected() void TextRender::OnLayerChanged() { if (_sceneRenderingKey != -1) - GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); + GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Layer); } bool TextRender::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) diff --git a/Source/Engine/Video/VideoPlayer.cpp b/Source/Engine/Video/VideoPlayer.cpp index 5bdcad7bf..80c0ed8ce 100644 --- a/Source/Engine/Video/VideoPlayer.cpp +++ b/Source/Engine/Video/VideoPlayer.cpp @@ -152,6 +152,18 @@ int32 VideoPlayer::GetFramesCount() const return _player.FramesCount; } +#if USE_EDITOR +#include "Engine/Debug/DebugDraw.h" + +void VideoPlayer::OnDebugDrawSelected() +{ + // Draw influence range + if (_isSpatial) + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(_transform.Translation, _minDistance), Color::CornflowerBlue, 0, true); + Actor::OnDebugDrawSelected(); +} +#endif + bool VideoPlayer::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) { return false; diff --git a/Source/Engine/Video/VideoPlayer.h b/Source/Engine/Video/VideoPlayer.h index 87f75327b..02e751ac9 100644 --- a/Source/Engine/Video/VideoPlayer.h +++ b/Source/Engine/Video/VideoPlayer.h @@ -221,6 +221,8 @@ public: const Vector3 size(50); return BoundingBox(_transform.Translation - size, _transform.Translation + size); } + + void OnDebugDrawSelected() override; #endif bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override; diff --git a/Source/Shaders/VolumetricFog.shader b/Source/Shaders/VolumetricFog.shader index 0032149b2..d04a2eb92 100644 --- a/Source/Shaders/VolumetricFog.shader +++ b/Source/Shaders/VolumetricFog.shader @@ -60,7 +60,7 @@ SkyLightData SkyLight; DDGIData DDGI; META_CB_END -META_CB_BEGIN(1, PerLight) +META_CB_BEGIN(2, PerLight) float2 SliceToDepth; int MinZ; float LocalLightScatteringIntensity; diff --git a/Source/Tools/Flax.Build/Build/Builder.Projects.cs b/Source/Tools/Flax.Build/Build/Builder.Projects.cs index 917cc9325..d0ab1dd8a 100644 --- a/Source/Tools/Flax.Build/Build/Builder.Projects.cs +++ b/Source/Tools/Flax.Build/Build/Builder.Projects.cs @@ -50,6 +50,9 @@ namespace Flax.Build var platform = Platform.BuildPlatform; var architecture = TargetArchitecture.AnyCPU; var architectureName = "AnyCPU"; + + if (!platform.CanBuildArchitecture(architecture)) + continue; var toolchain = platform.TryGetToolchain(architecture); var configuration = TargetConfiguration.Debug; diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs index ae6819859..215bb32e8 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatformBase.cs @@ -433,7 +433,12 @@ namespace Flax.Build.Platforms { case WindowsPlatformToolset.v140: { - if (hostArchitecture != TargetArchitecture.x86) + if (architecture == TargetArchitecture.ARM64) + { + Log.Verbose(string.Format("Unsupported {0} architecture for v140 toolset", hostArchitecture.ToString())); + return null; + } + else if (hostArchitecture == TargetArchitecture.x64) { string nativeCompilerPath = Path.Combine(vcToolChainDir, "bin", "amd64", "cl.exe"); if (File.Exists(nativeCompilerPath)) @@ -446,12 +451,17 @@ namespace Flax.Build.Platforms Log.Verbose(string.Format("No {0} host compiler toolchain found in {1} or {2}", hostArchitecture.ToString(), nativeCompilerPath, crossCompilerPath)); return null; } - else + else if (hostArchitecture == TargetArchitecture.x86) { string compilerPath = Path.Combine(vcToolChainDir, "bin", "cl.exe"); if (File.Exists(compilerPath)) return Path.GetDirectoryName(compilerPath); - Log.Verbose(string.Format("No {0} host compiler toolchain found in {1}", hostArchitecture.ToString())); + Log.Verbose(string.Format("No {0} host compiler toolchain found in {1}", hostArchitecture.ToString(), compilerPath)); + return null; + } + else + { + Log.Verbose(string.Format("Unsupported {0} host compiler for v140 toolset", hostArchitecture.ToString())); return null; } }