From 0f5a177be2b2237f4e5c08c8e30a18b27dfbe88b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 14 Oct 2023 21:32:58 -0500 Subject: [PATCH 01/55] Improve collection editor. --- .../CustomEditors/Editors/CollectionEditor.cs | 301 ++++++++++++++---- .../Editors/ModelInstanceEntryEditor.cs | 18 +- .../Core/Config/LayersAndTagsSettings.cs | 2 +- .../Localization/LocalizationSettings.h | 2 +- .../Attributes/CollectionAttribute.cs | 26 ++ 5 files changed, 281 insertions(+), 68 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index 8922e2d25..281f6b206 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -3,9 +3,9 @@ using System; using System.Collections; using System.Linq; -using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.GUI; using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.GUI.Input; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; @@ -34,6 +34,9 @@ namespace FlaxEditor.CustomEditors.Editors /// The index of the item (zero-based). /// public readonly int Index; + + private Image _moveUpImage; + private Image _moveDownImage; /// /// Initializes a new instance of the class. @@ -46,13 +49,61 @@ namespace FlaxEditor.CustomEditors.Editors Editor = editor; Index = index; + var icons = FlaxEditor.Editor.Instance.Icons; + + var style = FlaxEngine.GUI.Style.Current; + var imageSize = 18 - Margin.Height; + _moveDownImage = new Image + { + Brush = new SpriteBrush(icons.Down32), + TooltipText = "Move down", + IsScrollable = false, + AnchorPreset = AnchorPresets.MiddleLeft, + Bounds = new Rectangle(imageSize + 2, -Height * 0.5f, imageSize, imageSize), + Color = style.ForegroundGrey, + Margin = new Margin(1), + Parent = this, + }; + _moveDownImage.Clicked += MoveDownImageOnClicked; + _moveDownImage.Enabled = Index + 1 < Editor.Count; + + _moveUpImage = new Image + { + Brush = new SpriteBrush(icons.Up32), + TooltipText = "Move up", + IsScrollable = false, + AnchorPreset = AnchorPresets.MiddleLeft, + Bounds = new Rectangle(0, -Height * 0.5f, imageSize, imageSize), + Color = style.ForegroundGrey, + Margin = new Margin(1), + Parent = this, + }; + _moveUpImage.Clicked += MoveUpImageOnClicked; + _moveUpImage.Enabled = Index > 0; + + Margin = new Margin(_moveDownImage.Right + 2, Margin.Right, Margin.Top, Margin.Bottom); SetupContextMenu += OnSetupContextMenu; } + + private void MoveUpImageOnClicked(Image image, MouseButton button) + { + OnMoveUpClicked(); + } + + private void MoveDownImageOnClicked(Image image, MouseButton button) + { + OnMoveDownClicked(); + } private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkedEditor) { + menu.ItemsContainer.RemoveChildren(); + + menu.AddButton("Copy", linkedEditor.Copy); + var paste = menu.AddButton("Paste", linkedEditor.Paste); + paste.Enabled = linkedEditor.CanPaste; + menu.AddSeparator(); - var moveUpButton = menu.AddButton("Move up", OnMoveUpClicked); moveUpButton.Enabled = Index > 0; @@ -62,17 +113,140 @@ namespace FlaxEditor.CustomEditors.Editors menu.AddButton("Remove", OnRemoveClicked); } - private void OnMoveUpClicked(ContextMenuButton button) + private void OnMoveUpClicked() { Editor.Move(Index, Index - 1); } - private void OnMoveDownClicked(ContextMenuButton button) + private void OnMoveDownClicked() { Editor.Move(Index, Index + 1); } - private void OnRemoveClicked(ContextMenuButton button) + private void OnRemoveClicked() + { + Editor.Remove(Index); + } + } + + private class CollectionDropPanel : DropPanel + { + /// + /// The collection editor. + /// + public CollectionEditor Editor; + + /// + /// The index of the item (zero-based). + /// + public int Index { get; private set; } + + /// + /// The linked editor. + /// + public CustomEditor LinkedEditor; + + private bool _canReorder = true; + private Image _moveUpImage; + private Image _moveDownImage; + + public void Setup(CollectionEditor editor, int index, bool canReorder = true) + { + HeaderHeight = 18; + _canReorder = canReorder; + EnableDropDownIcon = true; + var icons = FlaxEditor.Editor.Instance.Icons; + ArrowImageClosed = new SpriteBrush(icons.ArrowRight12); + ArrowImageOpened = new SpriteBrush(icons.ArrowDown12); + HeaderText = $"Element {index}"; + IsClosed = false; + Editor = editor; + Index = index; + Offsets = new Margin(7, 7, 0, 0); + + MouseButtonRightClicked += OnMouseButtonRightClicked; + if (_canReorder) + { + var imageSize = HeaderHeight; + var style = FlaxEngine.GUI.Style.Current; + _moveDownImage = new Image + { + Brush = new SpriteBrush(icons.Down32), + TooltipText = "Move down", + IsScrollable = false, + Bounds = new Rectangle(imageSize * 2 + ItemsMargin.Left + 2, -HeaderHeight, imageSize, imageSize), + Color = style.ForegroundGrey, + Margin = new Margin(1), + Parent = this, + }; + _moveDownImage.Clicked += MoveDownImageOnClicked; + _moveDownImage.Enabled = Index + 1 < Editor.Count; + + _moveUpImage = new Image + { + Brush = new SpriteBrush(icons.Up32), + TooltipText = "Move up", + IsScrollable = false, + Bounds = new Rectangle(imageSize + ItemsMargin.Left, -HeaderHeight, imageSize, imageSize), + Color = style.ForegroundGrey, + Margin = new Margin(1), + Parent = this, + }; + _moveUpImage.Clicked += MoveUpImageOnClicked; + _moveUpImage.Enabled = Index > 0; + + HeaderTextMargin = new Margin(_moveDownImage.Right - 12, HeaderTextMargin.Right, HeaderTextMargin.Top, HeaderTextMargin.Bottom); + } + } + + private void MoveUpImageOnClicked(Image image, MouseButton button) + { + OnMoveUpClicked(); + } + + private void MoveDownImageOnClicked(Image image, MouseButton button) + { + OnMoveDownClicked(); + } + + private void OnMouseButtonRightClicked(DropPanel panel, Float2 location) + { + if (LinkedEditor == null) + return; + var linkedEditor = LinkedEditor; + var menu = new ContextMenu(); + + menu.AddButton("Copy", linkedEditor.Copy); + var paste = menu.AddButton("Paste", linkedEditor.Paste); + paste.Enabled = linkedEditor.CanPaste; + + if (_canReorder) + { + menu.AddSeparator(); + + var moveUpButton = menu.AddButton("Move up", OnMoveUpClicked); + moveUpButton.Enabled = Index > 0; + + var moveDownButton = menu.AddButton("Move down", OnMoveDownClicked); + moveDownButton.Enabled = Index + 1 < Editor.Count; + } + + menu.AddButton("Remove", OnRemoveClicked); + + menu.Show(panel, location); + } + + private void OnMoveUpClicked() + { + Editor.Move(Index, Index - 1); + } + + private void OnMoveDownClicked() + { + Editor.Move(Index, Index + 1); + } + + private void OnRemoveClicked() { Editor.Remove(Index); } @@ -82,12 +256,13 @@ namespace FlaxEditor.CustomEditors.Editors /// Determines if value of collection can be null. /// protected bool NotNullItems; - - private IntegerValueElement _size; + + private IntValueBox _sizeBox; private Color _background; private int _elementsCount; private bool _readOnly; private bool _canReorderItems; + private CollectionAttribute.DisplayType _displayType; /// /// Gets the length of the collection. @@ -117,12 +292,13 @@ namespace FlaxEditor.CustomEditors.Editors _readOnly = false; _canReorderItems = true; _background = FlaxEngine.GUI.Style.Current.CollectionBackgroundColor; + _displayType = CollectionAttribute.DisplayType.Header; NotNullItems = false; // Try get CollectionAttribute for collection editor meta var attributes = Values.GetAttributes(); Type overrideEditorType = null; - float spacing = 10.0f; + float spacing = 1.0f; var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute); if (collection != null) { @@ -133,20 +309,40 @@ namespace FlaxEditor.CustomEditors.Editors _background = collection.BackgroundColor.Value; overrideEditorType = TypeUtils.GetType(collection.OverrideEditorTypeName).Type; spacing = collection.Spacing; + _displayType = collection.Display; } // Size - if (_readOnly || (NotNullItems && size == 0)) + if (layout.ContainerControl is DropPanel dropPanel) { - layout.Label("Size", size.ToString()); - } - else - { - _size = layout.IntegerValue("Size"); - _size.IntValue.MinValue = 0; - _size.IntValue.MaxValue = ushort.MaxValue; - _size.IntValue.Value = size; - _size.IntValue.EditEnd += OnSizeChanged; + var height = dropPanel.HeaderHeight - dropPanel.HeaderTextMargin.Height; + var y = -dropPanel.HeaderHeight + dropPanel.HeaderTextMargin.Top; + _sizeBox = new IntValueBox(size) + { + MinValue = 0, + MaxValue = ushort.MaxValue, + AnchorPreset = AnchorPresets.TopRight, + Bounds = new Rectangle(-40 - dropPanel.ItemsMargin.Right, y, 40, height), + Parent = dropPanel, + }; + + var label = new Label + { + Text = "Size", + AnchorPreset = AnchorPresets.TopRight, + Bounds = new Rectangle(-_sizeBox.Width - 40 - dropPanel.ItemsMargin.Right - 2, y, 40, height), + Parent = dropPanel + }; + + if (_readOnly || (NotNullItems && size == 0)) + { + _sizeBox.IsReadOnly = true; + _sizeBox.Enabled = false; + } + else + { + _sizeBox.EditEnd += OnSizeChanged; + } } // Elements @@ -155,55 +351,42 @@ namespace FlaxEditor.CustomEditors.Editors var panel = layout.VerticalPanel(); panel.Panel.BackgroundColor = _background; var elementType = ElementType; + bool single = elementType.IsPrimitive || + elementType.Equals(new ScriptType(typeof(string))) || + elementType.IsEnum || + (elementType.GetFields().Length == 1 && elementType.GetProperties().Length == 0) || + (elementType.GetProperties().Length == 1 && elementType.GetFields().Length == 0) || + elementType.Equals(new ScriptType(typeof(JsonAsset))) || + elementType.Equals(new ScriptType(typeof(SettingsBase))); // Use separate layout cells for each collection items to improve layout updates for them in separation var useSharedLayout = elementType.IsPrimitive || elementType.IsEnum; - if (_canReorderItems) + for (int i = 0; i < size; i++) { - for (int i = 0; i < size; i++) + if (i > 0 && i < size && spacing > 0) { - if (i != 0 && spacing > 0f) - { - if (panel.Children.Count > 0 && panel.Children[panel.Children.Count - 1] is PropertiesListElement propertiesListElement) - { - if (propertiesListElement.Labels.Count > 0) - { - var label = propertiesListElement.Labels[propertiesListElement.Labels.Count - 1]; - var margin = label.Margin; - margin.Bottom += spacing; - label.Margin = margin; - } - propertiesListElement.Space(spacing); - } - else - { - panel.Space(spacing); - } - } - - var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null; - var property = panel.AddPropertyItem(new CollectionItemLabel(this, i)); - var itemLayout = useSharedLayout ? (LayoutElementsContainer)property : property.VerticalPanel(); - itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor); + panel.Space(spacing); } - } - else - { - for (int i = 0; i < size; i++) + var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null; + if (_displayType == CollectionAttribute.DisplayType.Inline || (collection == null && single) || (_displayType == CollectionAttribute.DisplayType.Default && single)) { - if (i != 0 && spacing > 0f) - { - if (panel.Children.Count > 0 && panel.Children[panel.Children.Count - 1] is PropertiesListElement propertiesListElement) - propertiesListElement.Space(spacing); - else - panel.Space(spacing); - } - - var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null; - var property = panel.AddPropertyItem("Element " + i); + PropertyNameLabel itemLabel; + if (_canReorderItems) + itemLabel = new CollectionItemLabel(this, i); + else + itemLabel = new PropertyNameLabel("Element " + i); + var property = panel.AddPropertyItem(itemLabel); var itemLayout = useSharedLayout ? (LayoutElementsContainer)property : property.VerticalPanel(); - itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor); + itemLabel.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor); + itemLabel.Parent.Offsets = new Margin(7, 7, 0, 0); + } + else if (_displayType == CollectionAttribute.DisplayType.Header || (_displayType == CollectionAttribute.DisplayType.Default && !single)) + { + var cdp = panel.CustomContainer(); + cdp.CustomControl.Setup(this, i, _canReorderItems); + var itemLayout = useSharedLayout ? (LayoutElementsContainer)cdp : cdp.VerticalPanel(); + cdp.CustomControl.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor); } } } @@ -269,7 +452,7 @@ namespace FlaxEditor.CustomEditors.Editors if (IsSetBlocked) return; - Resize(_size.IntValue.Value); + Resize(_sizeBox.Value); } /// diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index 9607680f2..3f55506fb 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -4,6 +4,7 @@ using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.GUI; using FlaxEditor.Scripting; using FlaxEngine; +using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Editors { @@ -13,7 +14,7 @@ namespace FlaxEditor.CustomEditors.Editors [CustomEditor(typeof(ModelInstanceEntry)), DefaultEditor] public sealed class ModelInstanceEntryEditor : GenericEditor { - private GroupElement _group; + private DropPanel _mainPanel; private bool _updateName; private int _entryIndex; private bool _isRefreshing; @@ -25,8 +26,11 @@ namespace FlaxEditor.CustomEditors.Editors public override void Initialize(LayoutElementsContainer layout) { _updateName = true; - var group = layout.Group("Entry"); - _group = group; + if (layout.ContainerControl.Parent is DropPanel panel) + { + _mainPanel = panel; + _mainPanel.HeaderText = "Entry"; + } if (ParentEditor == null) return; @@ -56,14 +60,14 @@ namespace FlaxEditor.CustomEditors.Editors // Create material picker var materialValue = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase); - var materialEditor = (AssetRefEditor)_group.Property(materialLabel, materialValue); + var materialEditor = (AssetRefEditor)layout.Property(materialLabel, materialValue); materialEditor.Values.SetDefaultValue(defaultValue); materialEditor.RefreshDefaultValue(); materialEditor.Picker.SelectedItemChanged += OnSelectedMaterialChanged; _materialEditor = materialEditor; } - base.Initialize(group); + base.Initialize(layout); } private void OnSelectedMaterialChanged() @@ -116,7 +120,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Update panel title to match material slot name if (_updateName && - _group != null && + _mainPanel != null && ParentEditor?.ParentEditor != null && ParentEditor.ParentEditor.Values.Count > 0) { @@ -127,7 +131,7 @@ namespace FlaxEditor.CustomEditors.Editors if (slots != null && slots.Length > entryIndex) { _updateName = false; - _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; + _mainPanel.HeaderText = "Entry " + slots[entryIndex].Name; } } } diff --git a/Source/Engine/Core/Config/LayersAndTagsSettings.cs b/Source/Engine/Core/Config/LayersAndTagsSettings.cs index 008f70504..d5ebbe19b 100644 --- a/Source/Engine/Core/Config/LayersAndTagsSettings.cs +++ b/Source/Engine/Core/Config/LayersAndTagsSettings.cs @@ -18,7 +18,7 @@ namespace FlaxEditor.Content.Settings /// /// The layers names. /// - [EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(ReadOnly = true)] + [EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(ReadOnly = true, Display = CollectionAttribute.DisplayType.Inline)] public string[] Layers = new string[32]; /// diff --git a/Source/Engine/Localization/LocalizationSettings.h b/Source/Engine/Localization/LocalizationSettings.h index e2c546c8b..22a4f03e4 100644 --- a/Source/Engine/Localization/LocalizationSettings.h +++ b/Source/Engine/Localization/LocalizationSettings.h @@ -16,7 +16,7 @@ public: /// /// The list of the string localization tables used by the game. /// - API_FIELD() + API_FIELD(Attributes="Collection(Display = CollectionAttribute.DisplayType.Inline)") Array> LocalizedStringTables; /// diff --git a/Source/Engine/Scripting/Attributes/CollectionAttribute.cs b/Source/Engine/Scripting/Attributes/CollectionAttribute.cs index ca8f45d39..cd2e962e6 100644 --- a/Source/Engine/Scripting/Attributes/CollectionAttribute.cs +++ b/Source/Engine/Scripting/Attributes/CollectionAttribute.cs @@ -10,6 +10,32 @@ namespace FlaxEngine [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class)] public sealed class CollectionAttribute : Attribute { + /// + /// The display type for collections. + /// + public enum DisplayType + { + /// + /// Displays the default display type. + /// + Default, + + /// + /// Displays a header. + /// + Header, + + /// + /// Displays inline. + /// + Inline, + } + + /// + /// Gets or sets the display type. + /// + public DisplayType Display; + /// /// Gets or sets whether this collection is read-only. If true, applications using this collection should not allow to add or remove items. /// From 9c60da278f7ac374e9e108e78b1d10ee05ac99e7 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 15 Oct 2023 13:31:35 -0500 Subject: [PATCH 02/55] Small change to dictionary. Small changes to collections. --- .../CustomEditors/Editors/CollectionEditor.cs | 35 ++++----- .../CustomEditors/Editors/DictionaryEditor.cs | 73 ++++++++++++------- 2 files changed, 60 insertions(+), 48 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index 281f6b206..eed453054 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -50,30 +50,29 @@ namespace FlaxEditor.CustomEditors.Editors Index = index; var icons = FlaxEditor.Editor.Instance.Icons; - var style = FlaxEngine.GUI.Style.Current; - var imageSize = 18 - Margin.Height; + var imageSize = 18; + var indexAmount = Index == 0 ? 2 : 0; _moveDownImage = new Image { Brush = new SpriteBrush(icons.Down32), TooltipText = "Move down", IsScrollable = false, - AnchorPreset = AnchorPresets.MiddleLeft, - Bounds = new Rectangle(imageSize + 2, -Height * 0.5f, imageSize, imageSize), + AnchorPreset = AnchorPresets.MiddleRight, + Bounds = new Rectangle(-imageSize, -Height * 0.5f, imageSize, imageSize), Color = style.ForegroundGrey, Margin = new Margin(1), Parent = this, }; _moveDownImage.Clicked += MoveDownImageOnClicked; _moveDownImage.Enabled = Index + 1 < Editor.Count; - _moveUpImage = new Image { Brush = new SpriteBrush(icons.Up32), TooltipText = "Move up", IsScrollable = false, - AnchorPreset = AnchorPresets.MiddleLeft, - Bounds = new Rectangle(0, -Height * 0.5f, imageSize, imageSize), + AnchorPreset = AnchorPresets.MiddleRight, + Bounds = new Rectangle(-(imageSize * 2 + 2), -Height * 0.5f, imageSize, imageSize), Color = style.ForegroundGrey, Margin = new Margin(1), Parent = this, @@ -81,7 +80,6 @@ namespace FlaxEditor.CustomEditors.Editors _moveUpImage.Clicked += MoveUpImageOnClicked; _moveUpImage.Enabled = Index > 0; - Margin = new Margin(_moveDownImage.Right + 2, Margin.Right, Margin.Top, Margin.Bottom); SetupContextMenu += OnSetupContextMenu; } @@ -174,7 +172,8 @@ namespace FlaxEditor.CustomEditors.Editors Brush = new SpriteBrush(icons.Down32), TooltipText = "Move down", IsScrollable = false, - Bounds = new Rectangle(imageSize * 2 + ItemsMargin.Left + 2, -HeaderHeight, imageSize, imageSize), + AnchorPreset = AnchorPresets.TopRight, + Bounds = new Rectangle(-(imageSize + ItemsMargin.Right + 2), -HeaderHeight, imageSize, imageSize), Color = style.ForegroundGrey, Margin = new Margin(1), Parent = this, @@ -187,15 +186,14 @@ namespace FlaxEditor.CustomEditors.Editors Brush = new SpriteBrush(icons.Up32), TooltipText = "Move up", IsScrollable = false, - Bounds = new Rectangle(imageSize + ItemsMargin.Left, -HeaderHeight, imageSize, imageSize), + AnchorPreset = AnchorPresets.TopRight, + Bounds = new Rectangle(-(imageSize * 2 + ItemsMargin.Right + 2), -HeaderHeight, imageSize, imageSize), Color = style.ForegroundGrey, Margin = new Margin(1), Parent = this, }; _moveUpImage.Clicked += MoveUpImageOnClicked; _moveUpImage.Enabled = Index > 0; - - HeaderTextMargin = new Margin(_moveDownImage.Right - 12, HeaderTextMargin.Right, HeaderTextMargin.Top, HeaderTextMargin.Bottom); } } @@ -349,6 +347,7 @@ namespace FlaxEditor.CustomEditors.Editors if (size > 0) { var panel = layout.VerticalPanel(); + panel.Panel.Offsets = new Margin(7, 7, 0, 0); panel.Panel.BackgroundColor = _background; var elementType = ElementType; bool single = elementType.IsPrimitive || @@ -359,15 +358,12 @@ namespace FlaxEditor.CustomEditors.Editors elementType.Equals(new ScriptType(typeof(JsonAsset))) || elementType.Equals(new ScriptType(typeof(SettingsBase))); - // Use separate layout cells for each collection items to improve layout updates for them in separation - var useSharedLayout = elementType.IsPrimitive || elementType.IsEnum; - for (int i = 0; i < size; i++) { + // Apply spacing if (i > 0 && i < size && spacing > 0) - { panel.Space(spacing); - } + var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null; if (_displayType == CollectionAttribute.DisplayType.Inline || (collection == null && single) || (_displayType == CollectionAttribute.DisplayType.Default && single)) { @@ -377,15 +373,14 @@ namespace FlaxEditor.CustomEditors.Editors else itemLabel = new PropertyNameLabel("Element " + i); var property = panel.AddPropertyItem(itemLabel); - var itemLayout = useSharedLayout ? (LayoutElementsContainer)property : property.VerticalPanel(); + var itemLayout = property.VerticalPanel(); itemLabel.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor); - itemLabel.Parent.Offsets = new Margin(7, 7, 0, 0); } else if (_displayType == CollectionAttribute.DisplayType.Header || (_displayType == CollectionAttribute.DisplayType.Default && !single)) { var cdp = panel.CustomContainer(); cdp.CustomControl.Setup(this, i, _canReorderItems); - var itemLayout = useSharedLayout ? (LayoutElementsContainer)cdp : cdp.VerticalPanel(); + var itemLayout = cdp.VerticalPanel(); cdp.CustomControl.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor); } } diff --git a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs index 73089ff04..774f9b6b3 100644 --- a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs @@ -7,6 +7,7 @@ using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.GUI; using FlaxEditor.GUI; using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.GUI.Input; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; @@ -149,13 +150,14 @@ namespace FlaxEditor.CustomEditors.Editors } } - private IntegerValueElement _size; + private IntValueBox _sizeBox; private Color _background; private int _elementsCount; private bool _readOnly; private bool _notNullItems; private bool _canEditKeys; private bool _keyEdited; + private CollectionAttribute.DisplayType _displayType; /// /// Gets the length of the collection. @@ -178,6 +180,7 @@ namespace FlaxEditor.CustomEditors.Editors _background = FlaxEngine.GUI.Style.Current.CollectionBackgroundColor; _readOnly = false; _notNullItems = false; + _displayType = CollectionAttribute.DisplayType.Header; // Try get CollectionAttribute for collection editor meta var attributes = Values.GetAttributes(); @@ -192,20 +195,40 @@ namespace FlaxEditor.CustomEditors.Editors _background = collection.BackgroundColor.Value; overrideEditorType = TypeUtils.GetType(collection.OverrideEditorTypeName).Type; spacing = collection.Spacing; + _displayType = collection.Display; } // Size - if (_readOnly || !_canEditKeys) + if (layout.ContainerControl is DropPanel dropPanel) { - layout.Label("Size", size.ToString()); - } - else - { - _size = layout.IntegerValue("Size"); - _size.IntValue.MinValue = 0; - _size.IntValue.MaxValue = _notNullItems ? size : ushort.MaxValue; - _size.IntValue.Value = size; - _size.IntValue.EditEnd += OnSizeChanged; + var height = dropPanel.HeaderHeight - dropPanel.HeaderTextMargin.Height; + var y = -dropPanel.HeaderHeight + dropPanel.HeaderTextMargin.Top; + _sizeBox = new IntValueBox(size) + { + MinValue = 0, + MaxValue = _notNullItems ? size : ushort.MaxValue, + AnchorPreset = AnchorPresets.TopRight, + Bounds = new Rectangle(-40 - dropPanel.ItemsMargin.Right, y, 40, height), + Parent = dropPanel, + }; + + var label = new Label + { + Text = "Size", + AnchorPreset = AnchorPresets.TopRight, + Bounds = new Rectangle(-_sizeBox.Width - 40 - dropPanel.ItemsMargin.Right - 2, y, 40, height), + Parent = dropPanel + }; + + if (_readOnly || !_canEditKeys) + { + _sizeBox.IsReadOnly = true; + _sizeBox.Enabled = false; + } + else + { + _sizeBox.EditEnd += OnSizeChanged; + } } // Elements @@ -216,29 +239,23 @@ namespace FlaxEditor.CustomEditors.Editors var keysEnumerable = ((IDictionary)Values[0]).Keys.OfType(); var keys = keysEnumerable as object[] ?? keysEnumerable.ToArray(); var valuesType = new ScriptType(valueType); + + bool single = valuesType.IsPrimitive || + valuesType.Equals(new ScriptType(typeof(string))) || + valuesType.IsEnum || + (valuesType.GetFields().Length == 1 && valuesType.GetProperties().Length == 0) || + (valuesType.GetProperties().Length == 1 && valuesType.GetFields().Length == 0) || + valuesType.Equals(new ScriptType(typeof(JsonAsset))) || + valuesType.Equals(new ScriptType(typeof(SettingsBase))); // Use separate layout cells for each collection items to improve layout updates for them in separation var useSharedLayout = valueType.IsPrimitive || valueType.IsEnum; for (int i = 0; i < size; i++) { - if (i != 0 && spacing > 0f) + if (i > 0 && i < size && spacing > 0) { - if (panel.Children.Count > 0 && panel.Children[panel.Children.Count - 1] is PropertiesListElement propertiesListElement) - { - if (propertiesListElement.Labels.Count > 0) - { - var label = propertiesListElement.Labels[propertiesListElement.Labels.Count - 1]; - var margin = label.Margin; - margin.Bottom += spacing; - label.Margin = margin; - } - propertiesListElement.Space(spacing); - } - else - { - panel.Space(spacing); - } + panel.Space(spacing); } var key = keys.ElementAt(i); @@ -310,7 +327,7 @@ namespace FlaxEditor.CustomEditors.Editors if (IsSetBlocked) return; - Resize(_size.IntValue.Value); + Resize(_sizeBox.Value); } /// From 86c2406d5200075bb14482895e3fab9dd7c714ef Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 15 Oct 2023 13:34:09 -0500 Subject: [PATCH 03/55] Clean up code. --- Source/Editor/CustomEditors/Editors/CollectionEditor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index eed453054..e48d72003 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -52,7 +52,6 @@ namespace FlaxEditor.CustomEditors.Editors var icons = FlaxEditor.Editor.Instance.Icons; var style = FlaxEngine.GUI.Style.Current; var imageSize = 18; - var indexAmount = Index == 0 ? 2 : 0; _moveDownImage = new Image { Brush = new SpriteBrush(icons.Down32), From a5d0916f93387a7bd62650d07f903f72574b8b3a Mon Sep 17 00:00:00 2001 From: MineBill Date: Tue, 2 Jan 2024 19:36:27 +0200 Subject: [PATCH 04/55] Add custom editor for buttons that allow listening for them inside the editor. --- .../Editors/BindableButtonEditor.cs | 96 +++++++++++++++++++ .../Editors/GamepadButtonEditor.cs | 51 ++++++++++ .../Editors/KeyboardKeysEditor.cs | 36 +++++++ .../Editors/MouseButtonEditor.cs | 37 +++++++ 4 files changed, 220 insertions(+) create mode 100644 Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs create mode 100644 Source/Editor/CustomEditors/Editors/GamepadButtonEditor.cs create mode 100644 Source/Editor/CustomEditors/Editors/KeyboardKeysEditor.cs create mode 100644 Source/Editor/CustomEditors/Editors/MouseButtonEditor.cs diff --git a/Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs b/Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs new file mode 100644 index 000000000..80d873cdf --- /dev/null +++ b/Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs @@ -0,0 +1,96 @@ +using FlaxEditor.GUI; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.CustomEditors.Editors +{ + /// + /// Base class for custom button editors. + /// See , and . + /// + public class BindableButtonEditor : EnumEditor + { + private bool _isListeningForInput; + private Button _button; + + /// + /// Where or not we are currently listening for any input. + /// + protected bool IsListeningForInput + { + get => _isListeningForInput; + set + { + _isListeningForInput = value; + if (_isListeningForInput) + SetupButton(); + else + ResetButton(); + } + } + + /// + /// The window this editor is attached to. + /// Useful to hook into for key pressed, mouse buttons etc. + /// + protected Window Window { get; private set; } + + /// + public override void Initialize(LayoutElementsContainer layout) + { + Window = layout.Control.RootWindow.Window; + + var panel = layout.CustomContainer(); + panel.CustomControl.SlotsHorizontally = 2; + panel.CustomControl.SlotsVertically = 1; + + var button = panel.Button("Listen"); + _button = button.Button; + _button.Clicked += OnButtonClicked; + ResetButton(); + + var padding = panel.CustomControl.SlotPadding; + panel.CustomControl.Height = ComboBox.DefaultHeight + padding.Height; + + base.Initialize(panel); + } + + /// + protected override void Deinitialize() + { + _button.Clicked -= OnButtonClicked; + base.Deinitialize(); + } + + private void ResetButton() + { + _button.Text = "Listen"; + _button.BorderThickness = 1; + + var style = FlaxEngine.GUI.Style.Current; + _button.BorderColor = style.BorderNormal; + _button.BorderColorHighlighted = style.BorderHighlighted; + _button.BorderColorSelected = style.BorderSelected; + } + + private void SetupButton() + { + _button.Text = "Listening..."; + _button.BorderThickness = 2; + + var color = FlaxEngine.GUI.Style.Current.ProgressNormal; + _button.BorderColor = color; + _button.BorderColorHighlighted = color; + _button.BorderColorSelected = color; + } + + private void OnButtonClicked() + { + _isListeningForInput = !_isListeningForInput; + if (_isListeningForInput) + SetupButton(); + else + ResetButton(); + } + } +} diff --git a/Source/Editor/CustomEditors/Editors/GamepadButtonEditor.cs b/Source/Editor/CustomEditors/Editors/GamepadButtonEditor.cs new file mode 100644 index 000000000..7bae24922 --- /dev/null +++ b/Source/Editor/CustomEditors/Editors/GamepadButtonEditor.cs @@ -0,0 +1,51 @@ +using System; +using FlaxEngine; + +namespace FlaxEditor.CustomEditors.Editors +{ + /// + /// Custom editor for . + /// Allows capturing gamepad buttons and assigning them + /// to the edited value. + /// + [CustomEditor(typeof(GamepadButton))] + public class GamepadButtonEditor : BindableButtonEditor + { + /// + public override void Initialize(LayoutElementsContainer layout) + { + base.Initialize(layout); + FlaxEngine.Scripting.Update += ScriptingOnUpdate; + } + + /// + protected override void Deinitialize() + { + FlaxEngine.Scripting.Update -= ScriptingOnUpdate; + base.Deinitialize(); + } + + private void ScriptingOnUpdate() + { + if (!IsListeningForInput) + return; + + // Since there is no way to get an event about + // which gamepad pressed what button, we have + // to poll all gamepads and buttons manually. + for (var i = 0; i < Input.GamepadsCount; i++) + { + var pad = Input.Gamepads[i]; + foreach (var btn in Enum.GetValues()) + { + if (pad.GetButtonUp(btn)) + { + IsListeningForInput = false; + SetValue(btn); + return; + } + } + } + } + } +} diff --git a/Source/Editor/CustomEditors/Editors/KeyboardKeysEditor.cs b/Source/Editor/CustomEditors/Editors/KeyboardKeysEditor.cs new file mode 100644 index 000000000..765527b7e --- /dev/null +++ b/Source/Editor/CustomEditors/Editors/KeyboardKeysEditor.cs @@ -0,0 +1,36 @@ +using FlaxEngine; + +namespace FlaxEditor.CustomEditors.Editors +{ + /// + /// Custom editor for . + /// Allows capturing key presses and assigning them + /// to the edited value. + /// + [CustomEditor(typeof(KeyboardKeys))] + public class KeyboardKeysEditor : BindableButtonEditor + { + /// + public override void Initialize(LayoutElementsContainer layout) + { + base.Initialize(layout); + Window.KeyUp += WindowOnKeyUp; + } + + /// + protected override void Deinitialize() + { + Window.KeyUp -= WindowOnKeyUp; + base.Deinitialize(); + } + + private void WindowOnKeyUp(KeyboardKeys key) + { + if (!IsListeningForInput) + return; + IsListeningForInput = false; + + SetValue(key); + } + } +} diff --git a/Source/Editor/CustomEditors/Editors/MouseButtonEditor.cs b/Source/Editor/CustomEditors/Editors/MouseButtonEditor.cs new file mode 100644 index 000000000..7964d3a4d --- /dev/null +++ b/Source/Editor/CustomEditors/Editors/MouseButtonEditor.cs @@ -0,0 +1,37 @@ +using FlaxEngine; + +namespace FlaxEditor.CustomEditors.Editors +{ + /// + /// Custom editor for . + /// Allows capturing mouse button presses and assigning them + /// to the edited value. + /// + [CustomEditor(typeof(MouseButton))] + public class MouseButtonEditor : BindableButtonEditor + { + /// + public override void Initialize(LayoutElementsContainer layout) + { + base.Initialize(layout); + Window.MouseUp += WindowOnMouseUp; + } + + /// + protected override void Deinitialize() + { + Window.MouseUp -= WindowOnMouseUp; + base.Deinitialize(); + } + + private void WindowOnMouseUp(ref Float2 mouse, MouseButton button, ref bool handled) + { + if (!IsListeningForInput) + return; + IsListeningForInput = false; + + SetValue(button); + handled = true; + } + } +} From 5158de4ac683578a61ef59f8edf0ed4de8dca656 Mon Sep 17 00:00:00 2001 From: MineBill Date: Tue, 2 Jan 2024 19:46:08 +0200 Subject: [PATCH 05/55] Added a tooltip for the listen button. --- Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs b/Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs index 80d873cdf..2d4bf0df3 100644 --- a/Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs +++ b/Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs @@ -44,7 +44,7 @@ namespace FlaxEditor.CustomEditors.Editors panel.CustomControl.SlotsHorizontally = 2; panel.CustomControl.SlotsVertically = 1; - var button = panel.Button("Listen"); + var button = panel.Button("Listen", "Press to listen for input events"); _button = button.Button; _button.Clicked += OnButtonClicked; ResetButton(); From 284c971ec0e1242664aa25280cc89cd3e260b4bb Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Fri, 5 Jan 2024 05:07:08 +0100 Subject: [PATCH 06/55] don't rotate a rigidbody around locked axis --- Source/Engine/Level/Actors/AnimatedModel.cpp | 29 +++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index c1b5af398..2ff4def5f 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -15,6 +15,7 @@ #include "Engine/Graphics/Models/MeshDeformation.h" #include "Engine/Level/Scene/Scene.h" #include "Engine/Level/SceneObjectsFactory.h" +#include "Engine/Physics/Actors/RigidBody.h" #include "Engine/Serialization/Serialization.h" AnimatedModel::AnimatedModel(const SpawnParams& params) @@ -548,7 +549,33 @@ void AnimatedModel::ApplyRootMotion(const Transform& rootMotionDelta) // Apply movement Actor* target = RootMotionTarget ? RootMotionTarget.Get() : this; - target->AddMovement(translation, rootMotionDelta.Orientation); + // filter rotation according to constraints if the target is a rigidbody + const auto rigidBody = dynamic_cast(target); + auto rotation = rootMotionDelta.Orientation; + if (rigidBody) + { + if (static_cast(rigidBody->GetConstraints()) & static_cast(RigidbodyConstraints::LockRotation)) + rotation = Quaternion::Identity; + else + { + Float3 euler = rotation.GetEuler(); + const auto constraints = static_cast(rigidBody->GetConstraints()); + if (constraints & static_cast(RigidbodyConstraints::LockRotationX)) + { + euler.X = 0; + } + if (constraints & static_cast(RigidbodyConstraints::LockRotationY)) + { + euler.Y = 0; + } + if (constraints & static_cast(RigidbodyConstraints::LockRotationZ)) + { + euler.Z = 0; + } + rotation = Quaternion::Euler(euler); + } + } + target->AddMovement(translation, rotation); } void AnimatedModel::SyncParameters() From 6d5888345404cd4ed8e80a21075b78e6127b0dc2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Jan 2024 21:42:28 +0100 Subject: [PATCH 07/55] Add events tracing feature to Animated Model for animation playback insights --- Source/Engine/Animations/Graph/AnimGraph.cpp | 2 ++ Source/Engine/Animations/Graph/AnimGraph.h | 21 +++++++++++++++++++ .../Animations/Graph/AnimGroup.Animation.cpp | 10 +++++++++ Source/Engine/Level/Actors/AnimatedModel.cpp | 11 ++++++++++ Source/Engine/Level/Actors/AnimatedModel.h | 21 +++++++++++++++++++ 5 files changed, 65 insertions(+) diff --git a/Source/Engine/Animations/Graph/AnimGraph.cpp b/Source/Engine/Animations/Graph/AnimGraph.cpp index 7c4671a51..fa039c7e9 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.cpp @@ -56,6 +56,7 @@ void AnimGraphInstanceData::ClearState() State.Resize(0); NodesPose.Resize(0); Slots.Clear(); + TraceEvents.Clear(); } void AnimGraphInstanceData::Invalidate() @@ -238,6 +239,7 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt) } for (auto& e : data.ActiveEvents) e.Hit = false; + data.TraceEvents.Clear(); // Init empty nodes data context.EmptyNodes.RootMotion = Transform::Identity; diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index 30ac5a776..2a8a7f7ab 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -200,6 +200,21 @@ struct FLAXENGINE_API AnimGraphSlot bool Reset = false; }; +/// +/// The animation graph state container for a single node playback trace (eg. animation sample info or state transition). Can be used by Anim Graph debugging or custom scripting. +/// +API_STRUCT() struct FLAXENGINE_API AnimGraphTraceEvent +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(AnimGraphTraceEvent); + + // Contextual asset used. For example, sampled animation. + API_FIELD() Asset* Asset = nullptr; + // Generic value contextual to playback type (eg. animation sample position). + API_FIELD() float Value = 0; + // Identifier of the node in the graph. + API_FIELD() uint32 NodeId = 0; +}; + /// /// The animation graph instance data storage. Required to update the animation graph. /// @@ -358,6 +373,12 @@ public: /// void InvokeAnimEvents(); +public: + // Anim Graph logic tracing feature that allows to collect insights of animations sampling and skeleton poses operations. + bool EnableTracing = false; + // Trace events collected when using EnableTracing option. + Array TraceEvents; + private: struct OutgoingEvent { diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index fc7cf6dbc..598e55da1 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -219,6 +219,16 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* const float animPos = GetAnimSamplePos(length, anim, pos, speed); const float animPrevPos = GetAnimSamplePos(length, anim, prevPos, speed); + // Add to trace + auto& context = Context.Get(); + if (context.Data->EnableTracing) + { + auto& trace = context.Data->TraceEvents.AddOne(); + trace.Asset = anim; + trace.Value = animPos; + trace.NodeId = node->ID; + } + // Evaluate nested animations bool hasNested = false; if (anim->NestedAnims.Count() != 0) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 17f117173..319def475 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -225,6 +225,17 @@ void AnimatedModel::SetMasterPoseModel(AnimatedModel* masterPose) _masterPose->AnimationUpdated.Bind(this); } +const Array& AnimatedModel::GetTraceEvents() const +{ +#if !BUILD_RELEASE + if (!GetEnableTracing()) + { + LOG(Warning, "Accessing AnimatedModel.TraceEvents with tracing disabled."); + } +#endif + return GraphInstance.TraceEvents; +} + #define CHECK_ANIM_GRAPH_PARAM_ACCESS() \ if (!AnimationGraph) \ { \ diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 029e17b62..bd03e824e 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -259,6 +259,27 @@ public: /// The master pose actor to use. API_FUNCTION() void SetMasterPoseModel(AnimatedModel* masterPose); + /// + /// Enables extracting animation playback insights for debugging or custom scripting. + /// + API_PROPERTY(Attributes="HideInEditor, NoSerialize") bool GetEnableTracing() const + { + return GraphInstance.EnableTracing; + } + + /// + /// Enables extracting animation playback insights for debugging or custom scripting. + /// + API_PROPERTY() void SetEnableTracing(bool value) + { + GraphInstance.EnableTracing = value; + } + + /// + /// Gets the trace events from the last animation update. Valid only when EnableTracing is active. + /// + API_PROPERTY(Attributes="HideInEditor, NoSerialize") const Array& GetTraceEvents() const; + public: /// /// Gets the anim graph instance parameters collection. From af0439f3ce5903a9f96479dd821bee9244f6d0fd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Jan 2024 22:06:44 +0100 Subject: [PATCH 08/55] Add displaying playback position of animation in Anim Graph window --- Source/Editor/Surface/AnimGraphSurface.cs | 20 ++++++++++++ Source/Editor/Surface/Archetypes/Animation.cs | 31 +++++++++++++++++++ .../Windows/Assets/AnimationGraphWindow.cs | 12 +++++++ 3 files changed, 63 insertions(+) diff --git a/Source/Editor/Surface/AnimGraphSurface.cs b/Source/Editor/Surface/AnimGraphSurface.cs index d7746662f..ccd78412a 100644 --- a/Source/Editor/Surface/AnimGraphSurface.cs +++ b/Source/Editor/Surface/AnimGraphSurface.cs @@ -113,6 +113,25 @@ namespace FlaxEditor.Surface ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; } + internal AnimGraphTraceEvent[] LastTraceEvents; + + internal bool TryGetTraceEvent(SurfaceNode node, out AnimGraphTraceEvent traceEvent) + { + if (LastTraceEvents != null) + { + foreach (var e in LastTraceEvents) + { + if (e.NodeId == node.ID) + { + traceEvent = e; + return true; + } + } + } + traceEvent = default; + return false; + } + private static SurfaceStyle CreateStyle() { var editor = Editor.Instance; @@ -383,6 +402,7 @@ namespace FlaxEditor.Surface } ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; _nodesCache.Wait(); + LastTraceEvents = null; base.OnDestroy(); } diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index 446883406..c659b475a 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -36,6 +36,7 @@ namespace FlaxEditor.Surface.Archetypes { private AssetSelect _assetSelect; private Box _assetBox; + private ProgressBar _playbackPos; /// public Sample(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) @@ -93,6 +94,36 @@ namespace FlaxEditor.Surface.Archetypes _assetSelect.Visible = !box.HasAnyConnection; UpdateTitle(); } + + /// + public override void Update(float deltaTime) + { + // Debug current playback position + if (((AnimGraphSurface)Surface).TryGetTraceEvent(this, out var traceEvent) && traceEvent.Asset is FlaxEngine.Animation anim) + { + if (_playbackPos == null) + { + _playbackPos = new ProgressBar + { + SmoothingScale = 0.0f, + Offsets = Margin.Zero, + AnchorPreset = AnchorPresets.HorizontalStretchBottom, + Parent = this, + Height = 12.0f, + }; + _playbackPos.Y -= 16.0f; + } + _playbackPos.Visible = true; + _playbackPos.Maximum = anim.Duration; + _playbackPos.Value = traceEvent.Value; // AnimGraph reports position in animation frames, not time + } + else if (_playbackPos != null) + { + _playbackPos.Visible = false; + } + + base.Update(deltaTime); + } } /// diff --git a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs index 12eac22b5..e0d8c9ded 100644 --- a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs @@ -396,6 +396,16 @@ namespace FlaxEditor.Windows.Assets /// public override void OnUpdate() { + // Extract animations playback state from the events tracing + var debugActor = _debugPicker.Value as AnimatedModel; + if (debugActor == null) + debugActor = _preview.PreviewActor; + if (debugActor != null) + { + debugActor.EnableTracing = true; + Surface.LastTraceEvents = debugActor.TraceEvents; + } + base.OnUpdate(); // Update graph execution flow debugging visualization @@ -416,6 +426,8 @@ namespace FlaxEditor.Windows.Assets /// public override void OnDestroy() { + if (IsDisposing) + return; Animations.DebugFlow -= OnDebugFlow; _properties = null; From a24a9d209433d3f79a14be6f34a4ab340ec39bf5 Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Sat, 6 Jan 2024 12:40:24 +0100 Subject: [PATCH 09/55] move constraints checks to RigidBody --- Source/Engine/Level/Actors/AnimatedModel.cpp | 28 +------------- Source/Engine/Physics/Actors/RigidBody.cpp | 40 ++++++++++++++++++++ Source/Engine/Physics/Actors/RigidBody.h | 6 +++ 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 2ff4def5f..3a40dbf46 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -549,33 +549,7 @@ void AnimatedModel::ApplyRootMotion(const Transform& rootMotionDelta) // Apply movement Actor* target = RootMotionTarget ? RootMotionTarget.Get() : this; - // filter rotation according to constraints if the target is a rigidbody - const auto rigidBody = dynamic_cast(target); - auto rotation = rootMotionDelta.Orientation; - if (rigidBody) - { - if (static_cast(rigidBody->GetConstraints()) & static_cast(RigidbodyConstraints::LockRotation)) - rotation = Quaternion::Identity; - else - { - Float3 euler = rotation.GetEuler(); - const auto constraints = static_cast(rigidBody->GetConstraints()); - if (constraints & static_cast(RigidbodyConstraints::LockRotationX)) - { - euler.X = 0; - } - if (constraints & static_cast(RigidbodyConstraints::LockRotationY)) - { - euler.Y = 0; - } - if (constraints & static_cast(RigidbodyConstraints::LockRotationZ)) - { - euler.Z = 0; - } - rotation = Quaternion::Euler(euler); - } - } - target->AddMovement(translation, rotation); + target->AddMovement(translation, rootMotionDelta.Orientation); } void AnimatedModel::SyncParameters() diff --git a/Source/Engine/Physics/Actors/RigidBody.cpp b/Source/Engine/Physics/Actors/RigidBody.cpp index afbac6611..15ea95075 100644 --- a/Source/Engine/Physics/Actors/RigidBody.cpp +++ b/Source/Engine/Physics/Actors/RigidBody.cpp @@ -300,6 +300,46 @@ void RigidBody::ClosestPoint(const Vector3& position, Vector3& result) const } } +void RigidBody::AddMovement(const Vector3& translation, const Quaternion& rotation) +{ + // filter rotation according to constraints + Quaternion allowedRotation; + if (static_cast(GetConstraints()) & static_cast(RigidbodyConstraints::LockRotation)) + allowedRotation = Quaternion::Identity; + else + { + Float3 euler = rotation.GetEuler(); + const auto constraints = static_cast(GetConstraints()); + if (constraints & static_cast(RigidbodyConstraints::LockRotationX)) + euler.X = 0; + if (constraints & static_cast(RigidbodyConstraints::LockRotationY)) + euler.Y = 0; + if (constraints & static_cast(RigidbodyConstraints::LockRotationZ)) + euler.Z = 0; + allowedRotation = Quaternion::Euler(euler); + } + + // filter translation according to the constraints + auto allowedTranslation = translation; + if (static_cast(GetConstraints()) & static_cast(RigidbodyConstraints::LockPosition)) + allowedTranslation = Vector3::Zero; + else + { + const auto constraints = static_cast(GetConstraints()); + if (constraints & static_cast(RigidbodyConstraints::LockPositionX)) + allowedTranslation.X = 0; + if (constraints & static_cast(RigidbodyConstraints::LockPositionY)) + allowedTranslation.Y = 0; + if (constraints & static_cast(RigidbodyConstraints::LockPositionZ)) + allowedTranslation.Z = 0; + } + Transform t; + t.Translation = _transform.Translation + allowedTranslation; + t.Orientation = _transform.Orientation * allowedRotation; + t.Scale = _transform.Scale; + SetTransform(t); +} + void RigidBody::OnCollisionEnter(const Collision& c) { CollisionEnter(c); diff --git a/Source/Engine/Physics/Actors/RigidBody.h b/Source/Engine/Physics/Actors/RigidBody.h index 2ef50ca7a..b8c7d0208 100644 --- a/Source/Engine/Physics/Actors/RigidBody.h +++ b/Source/Engine/Physics/Actors/RigidBody.h @@ -430,6 +430,12 @@ public: /// The result point on the rigidbody shape that is closest to the specified location. API_FUNCTION() void ClosestPoint(const Vector3& position, API_PARAM(Out) Vector3& result) const; + /// + /// Moves and rotates the rigidbody in world space within the limits of defined constraints. + /// + /// The translation vector. + /// The rotation quaternion. + API_FUNCTION() void AddMovement(const Vector3& translation, const Quaternion& rotation) override; public: /// /// Occurs when a collision start gets registered for this rigidbody (it collides with something). From 8c2a156e1f124479d0aaed5e26ab22e58577b1e0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Jan 2024 18:34:15 +0100 Subject: [PATCH 10/55] Add SourceState and DestinationState modes to State Machine interruption modes in Anim Graph #1735 --- .../Archetypes/Animation.StateMachine.cs | 24 ++- .../Editor/Surface/Undo/ConnectBoxesAction.cs | 2 + .../Animations/Graph/AnimGraph.Base.cpp | 5 +- Source/Engine/Animations/Graph/AnimGraph.h | 9 +- .../Animations/Graph/AnimGroup.Animation.cpp | 180 ++++++++++++------ 5 files changed, 153 insertions(+), 67 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 9d4367303..9ec1bab19 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -1583,14 +1583,24 @@ namespace FlaxEditor.Surface.Archetypes None = 0, /// - /// Transition rule will be rechecked during active transition with option to interrupt transition. + /// Transition rule will be rechecked during active transition with option to interrupt transition (to go back to the source state). /// RuleRechecking = 1, /// - /// Interrupted transition is immediately stopped without blending out. + /// Interrupted transition is immediately stopped without blending out (back to the source/destination state). /// Instant = 2, + + /// + /// Enables checking other transitions in the source state that might interrupt this one. + /// + SourceState = 4, + + /// + /// Enables checking transitions in the destination state that might interrupt this one. + /// + DestinationState = 8, } /// @@ -1613,6 +1623,8 @@ namespace FlaxEditor.Surface.Archetypes UseDefaultRule = 4, InterruptionRuleRechecking = 8, InterruptionInstant = 16, + InterruptionSourceState = 32, + InterruptionDestinationState = 64, } /// @@ -1773,7 +1785,7 @@ namespace FlaxEditor.Surface.Archetypes } /// - /// Transition interruption options. + /// Transition interruption options (flags, can select multiple values). /// [EditorOrder(70), DefaultValue(InterruptionFlags.None)] public InterruptionFlags Interruption @@ -1785,12 +1797,18 @@ namespace FlaxEditor.Surface.Archetypes flags |= InterruptionFlags.RuleRechecking; if (_data.HasFlag(Data.FlagTypes.InterruptionInstant)) flags |= InterruptionFlags.Instant; + if (_data.HasFlag(Data.FlagTypes.InterruptionSourceState)) + flags |= InterruptionFlags.SourceState; + if (_data.HasFlag(Data.FlagTypes.InterruptionDestinationState)) + flags |= InterruptionFlags.DestinationState; return flags; } set { _data.SetFlag(Data.FlagTypes.InterruptionRuleRechecking, value.HasFlag(InterruptionFlags.RuleRechecking)); _data.SetFlag(Data.FlagTypes.InterruptionInstant, value.HasFlag(InterruptionFlags.Instant)); + _data.SetFlag(Data.FlagTypes.InterruptionSourceState, value.HasFlag(InterruptionFlags.SourceState)); + _data.SetFlag(Data.FlagTypes.InterruptionDestinationState, value.HasFlag(InterruptionFlags.DestinationState)); SourceState.SaveTransitions(true); } } diff --git a/Source/Editor/Surface/Undo/ConnectBoxesAction.cs b/Source/Editor/Surface/Undo/ConnectBoxesAction.cs index 72b9242a4..07118228e 100644 --- a/Source/Editor/Surface/Undo/ConnectBoxesAction.cs +++ b/Source/Editor/Surface/Undo/ConnectBoxesAction.cs @@ -24,6 +24,8 @@ namespace FlaxEditor.Surface.Undo public ConnectBoxesAction(InputBox iB, OutputBox oB, bool connect) { + if (iB == null || oB == null || iB.ParentNode == null || oB.ParentNode == null) + throw new System.ArgumentNullException(); _surface = iB.Surface; _context = new ContextHandle(iB.ParentNode.Context); _connect = connect; diff --git a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp index 48fa7fed1..c05b82b70 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp @@ -99,10 +99,7 @@ void BlendPoseBucketInit(AnimGraphInstanceData::Bucket& bucket) void StateMachineBucketInit(AnimGraphInstanceData::Bucket& bucket) { - bucket.StateMachine.LastUpdateFrame = 0; - bucket.StateMachine.CurrentState = nullptr; - bucket.StateMachine.ActiveTransition = nullptr; - bucket.StateMachine.TransitionPosition = 0.0f; + Platform::MemoryClear(&bucket.StateMachine, sizeof(bucket.StateMachine)); } void SlotBucketInit(AnimGraphInstanceData::Bucket& bucket) diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index 2a8a7f7ab..c160b671c 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -129,6 +129,8 @@ public: UseDefaultRule = 4, InterruptionRuleRechecking = 8, InterruptionInstant = 16, + InterruptionSourceState = 32, + InterruptionDestinationState = 64, }; public: @@ -256,7 +258,10 @@ public: uint64 LastUpdateFrame; AnimGraphNode* CurrentState; AnimGraphStateTransition* ActiveTransition; + AnimGraphStateTransition* BaseTransition; + AnimGraphNode* BaseTransitionState; float TransitionPosition; + float BaseTransitionPosition; }; struct SlotBucket @@ -858,7 +863,7 @@ public: } /// - /// Resets all the state bucket used by the given graph including sub-graphs (total). Can eb used to reset the animation state of the nested graph (including children). + /// Resets all the state bucket used by the given graph including sub-graphs (total). Can be used to reset the animation state of the nested graph (including children). /// void ResetBuckets(AnimGraphContext& context, AnimGraphBase* graph); @@ -887,5 +892,7 @@ private: Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, Animation* animC, float speedA, float speedB, float speedC, float alphaA, float alphaB, float alphaC); Variant Blend(AnimGraphNode* node, const Value& poseA, const Value& poseB, float alpha, AlphaBlendMode alphaMode); Variant SampleState(AnimGraphNode* state); + void InitStateTransition(AnimGraphContext& context, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, AnimGraphStateTransition* transition = nullptr); + AnimGraphStateTransition* UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphNode* state, AnimGraphNode* ignoreState = nullptr); void UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData); }; diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 598e55da1..cdf1ebd84 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -470,10 +470,12 @@ Variant AnimGraphExecutor::Blend(AnimGraphNode* node, const Value& poseA, const { ANIM_GRAPH_PROFILE_EVENT("Blend Pose"); + if (isnan(alpha) || isinf(alpha)) + alpha = 0; + alpha = Math::Saturate(alpha); alpha = AlphaBlend::Process(alpha, alphaMode); const auto nodes = node->GetNodes(this); - auto nodesA = static_cast(poseA.AsPointer); auto nodesB = static_cast(poseB.AsPointer); if (!ANIM_GRAPH_IS_VALID_PTR(poseA)) @@ -494,32 +496,40 @@ Variant AnimGraphExecutor::Blend(AnimGraphNode* node, const Value& poseA, const Variant AnimGraphExecutor::SampleState(AnimGraphNode* state) { - // Prepare auto& data = state->Data.State; if (data.Graph == nullptr || data.Graph->GetRootNode() == nullptr) - { - // Invalid state graph return Value::Null; - } - ANIM_GRAPH_PROFILE_EVENT("Evaluate State"); - - // Evaluate state auto rootNode = data.Graph->GetRootNode(); - auto result = eatBox((Node*)rootNode, &rootNode->Boxes[0]); - - return result; + return eatBox((Node*)rootNode, &rootNode->Boxes[0]); } -void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData) +void AnimGraphExecutor::InitStateTransition(AnimGraphContext& context, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, AnimGraphStateTransition* transition) +{ + // Reset transiton + stateMachineBucket.ActiveTransition = transition; + stateMachineBucket.TransitionPosition = 0.0f; + + // End base transition + if (stateMachineBucket.BaseTransition) + { + ResetBuckets(context, stateMachineBucket.BaseTransitionState->Data.State.Graph); + stateMachineBucket.BaseTransition = nullptr; + stateMachineBucket.BaseTransitionState = nullptr; + stateMachineBucket.BaseTransitionPosition = 0.0f; + } +} + +AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphNode* state, AnimGraphNode* ignoreState) { int32 transitionIndex = 0; + const AnimGraphNode::StateBaseData& stateData = state->Data.State; while (transitionIndex < ANIM_GRAPH_MAX_STATE_TRANSITIONS && stateData.Transitions[transitionIndex] != AnimGraphNode::StateData::InvalidTransitionIndex) { const uint16 idx = stateData.Transitions[transitionIndex]; ASSERT(idx < stateMachineData.Graph->StateTransitions.Count()); auto& transition = stateMachineData.Graph->StateTransitions[idx]; - if (transition.Destination == stateMachineBucket.CurrentState) + if (transition.Destination == state || transition.Destination == ignoreState) { // Ignore transition to the current state transitionIndex++; @@ -527,7 +537,7 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const } // Evaluate source state transition data (position, length, etc.) - const Value sourceStatePtr = SampleState(stateMachineBucket.CurrentState); + const Value sourceStatePtr = SampleState(state); auto& transitionData = context.TransitionData; // Note: this could support nested transitions but who uses state machine inside transition rule? if (ANIM_GRAPH_IS_VALID_PTR(sourceStatePtr)) { @@ -548,6 +558,7 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const if (transition.RuleGraph && !useDefaultRule) { // Execute transition rule + ANIM_GRAPH_PROFILE_EVENT("Rule"); auto rootNode = transition.RuleGraph->GetRootNode(); ASSERT(rootNode); if (!(bool)eatBox((Node*)rootNode, &rootNode->Boxes[0])) @@ -570,10 +581,7 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const canEnter = true; if (canEnter) { - // Start transition - stateMachineBucket.ActiveTransition = &transition; - stateMachineBucket.TransitionPosition = 0.0f; - break; + return &transition; } // Skip after Solo transition @@ -583,6 +591,18 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const transitionIndex++; } + + // No transition + return nullptr; +} + +void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData) +{ + AnimGraphStateTransition* transition = UpdateStateTransitions(context, stateMachineData, stateMachineBucket.CurrentState); + if (transition) + { + InitStateTransition(context, stateMachineBucket, transition); + } } void ComputeMultiBlendLength(float& length, AnimGraphNode* node) @@ -1511,10 +1531,9 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // Blend two animations { - const float alpha = Math::Saturate(bucket.TransitionPosition / blendDuration); + const float alpha = bucket.TransitionPosition / blendDuration; const auto valueA = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + bucket.PreviousBlendPoseIndex), Value::Null); const auto valueB = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + poseIndex), Value::Null); - value = Blend(node, valueA, valueB, alpha, mode); } @@ -1620,22 +1639,21 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // Enter to the first state pointed by the Entry node (without transitions) bucket.CurrentState = data.Graph->GetRootNode(); - bucket.ActiveTransition = nullptr; - bucket.TransitionPosition = 0.0f; + InitStateTransition(context, bucket); - // Reset all state buckets pof the graphs and nodes included inside the state machine + // Reset all state buckets of the graphs and nodes included inside the state machine ResetBuckets(context, data.Graph); } #define END_TRANSITION() \ ResetBuckets(context, bucket.CurrentState->Data.State.Graph); \ bucket.CurrentState = bucket.ActiveTransition->Destination; \ - bucket.ActiveTransition = nullptr; \ - bucket.TransitionPosition = 0.0f + InitStateTransition(context, bucket) // Update the active transition if (bucket.ActiveTransition) { bucket.TransitionPosition += context.DeltaTime; + ASSERT(bucket.CurrentState); // Check for transition end if (bucket.TransitionPosition >= bucket.ActiveTransition->BlendDuration) @@ -1643,38 +1661,70 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu END_TRANSITION(); } // Check for transition interruption - else if (EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionRuleRechecking)) + else if (EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionRuleRechecking) && + EnumHasNoneFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::UseDefaultRule) && + bucket.ActiveTransition->RuleGraph) { - const bool useDefaultRule = EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::UseDefaultRule); - if (bucket.ActiveTransition->RuleGraph && !useDefaultRule) + // Execute transition rule + auto rootNode = bucket.ActiveTransition->RuleGraph->GetRootNode(); + if (!(bool)eatBox((Node*)rootNode, &rootNode->Boxes[0])) { - // Execute transition rule - auto rootNode = bucket.ActiveTransition->RuleGraph->GetRootNode(); - if (!(bool)eatBox((Node*)rootNode, &rootNode->Boxes[0])) + bool cancelTransition = false; + if (EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionInstant)) { - bool cancelTransition = false; - if (EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionInstant)) + cancelTransition = true; + } + else + { + // Blend back to the source state (remove currently applied delta and rewind transition) + bucket.TransitionPosition -= context.DeltaTime; + bucket.TransitionPosition -= context.DeltaTime; + if (bucket.TransitionPosition <= ZeroTolerance) { cancelTransition = true; } - else - { - // Blend back to the source state (remove currently applied delta and rewind transition) - bucket.TransitionPosition -= context.DeltaTime; - bucket.TransitionPosition -= context.DeltaTime; - if (bucket.TransitionPosition <= ZeroTolerance) - { - cancelTransition = true; - } - } - if (cancelTransition) - { - // Go back to the source state - ResetBuckets(context, bucket.CurrentState->Data.State.Graph); - bucket.ActiveTransition = nullptr; - bucket.TransitionPosition = 0.0f; - } } + if (cancelTransition) + { + // Go back to the source state + ResetBuckets(context, bucket.CurrentState->Data.State.Graph); + InitStateTransition(context, bucket); + } + } + } + if (bucket.ActiveTransition && !bucket.BaseTransition && EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionSourceState)) + { + // Try to interrupt with any other transition in the source state (except the current transition) + if (AnimGraphStateTransition* transition = UpdateStateTransitions(context, data, bucket.CurrentState, bucket.ActiveTransition->Destination)) + { + // Change active transition to the interrupted one + if (EnumHasNoneFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionInstant)) + { + // Cache the current blending state to be used as a base when blending towards new destination state (seamless blending after interruption) + bucket.BaseTransition = bucket.ActiveTransition; + bucket.BaseTransitionState = bucket.CurrentState; + bucket.BaseTransitionPosition = bucket.TransitionPosition; + } + bucket.ActiveTransition = transition; + bucket.TransitionPosition = 0.0f; + } + } + if (bucket.ActiveTransition && !bucket.BaseTransition && EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionDestinationState)) + { + // Try to interrupt with any other transition in the destination state (except the transition back to the current state if exists) + if (AnimGraphStateTransition* transition = UpdateStateTransitions(context, data, bucket.ActiveTransition->Destination, bucket.CurrentState)) + { + // Change active transition to the interrupted one + if (EnumHasNoneFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionInstant)) + { + // Cache the current blending state to be used as a base when blending towards new destination state (seamless blending after interruption) + bucket.BaseTransition = bucket.ActiveTransition; + bucket.BaseTransitionState = bucket.CurrentState; + bucket.BaseTransitionPosition = bucket.TransitionPosition; + } + bucket.CurrentState = bucket.ActiveTransition->Destination; + bucket.ActiveTransition = transition; + bucket.TransitionPosition = 0.0f; } } } @@ -1703,9 +1753,23 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu } } - // Sample the current state - const auto currentState = SampleState(bucket.CurrentState); - value = currentState; + if (bucket.BaseTransitionState) + { + // Sample the other state (eg. when blending from interrupted state to the another state from the old destination) + value = SampleState(bucket.BaseTransitionState); + if (bucket.BaseTransition) + { + // Evaluate the base pose from the time when transition was interrupted + const auto destinationState = SampleState(bucket.BaseTransition->Destination); + const float alpha = bucket.BaseTransitionPosition / bucket.BaseTransition->BlendDuration; + value = Blend(node, value, destinationState, alpha, bucket.BaseTransition->BlendMode); + } + } + else + { + // Sample the current state + value = SampleState(bucket.CurrentState); + } // Handle active transition blending if (bucket.ActiveTransition) @@ -1714,14 +1778,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu const auto destinationState = SampleState(bucket.ActiveTransition->Destination); // Perform blending - const float alpha = Math::Saturate(bucket.TransitionPosition / bucket.ActiveTransition->BlendDuration); - value = Blend(node, currentState, destinationState, alpha, bucket.ActiveTransition->BlendMode); + const float alpha = bucket.TransitionPosition / bucket.ActiveTransition->BlendDuration; + value = Blend(node, value, destinationState, alpha, bucket.ActiveTransition->BlendMode); } - // Update bucket bucket.LastUpdateFrame = context.CurrentFrameIndex; #undef END_TRANSITION - break; } // Entry @@ -2142,7 +2204,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // Blend out auto input = tryGetValue(node->GetBox(1), Value::Null); bucket.BlendOutPosition += deltaTime; - const float alpha = Math::Saturate(bucket.BlendOutPosition / slot.BlendOutTime); + const float alpha = bucket.BlendOutPosition / slot.BlendOutTime; value = Blend(node, value, input, alpha, AlphaBlendMode::HermiteCubic); } else if (bucket.LoopsDone == 0 && slot.BlendInTime > 0.0f && bucket.BlendInPosition < slot.BlendInTime) @@ -2150,7 +2212,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // Blend in auto input = tryGetValue(node->GetBox(1), Value::Null); bucket.BlendInPosition += deltaTime; - const float alpha = Math::Saturate(bucket.BlendInPosition / slot.BlendInTime); + const float alpha = bucket.BlendInPosition / slot.BlendInTime; value = Blend(node, input, value, alpha, AlphaBlendMode::HermiteCubic); } break; From 0b7c18763006a7442a84c03ba43b60c96d3b9408 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Jan 2024 22:35:17 +0100 Subject: [PATCH 11/55] Fix Animated Model slot animations clearing on start #1803 --- Source/Engine/Animations/Graph/AnimGraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Animations/Graph/AnimGraph.cpp b/Source/Engine/Animations/Graph/AnimGraph.cpp index fa039c7e9..f7b883bd7 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.cpp @@ -39,6 +39,7 @@ void AnimGraphImpulse::SetNodeModelTransformation(SkeletonData& skeleton, int32 void AnimGraphInstanceData::Clear() { ClearState(); + Slots.Clear(); Parameters.Resize(0); } @@ -55,7 +56,6 @@ void AnimGraphInstanceData::ClearState() RootMotion = Transform::Identity; State.Resize(0); NodesPose.Resize(0); - Slots.Clear(); TraceEvents.Clear(); } From 9fd7a231ca0e4e027a68d1b5aed3245d60772fbb Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Mon, 8 Jan 2024 20:45:10 +0200 Subject: [PATCH 12/55] Fix invalid Visual Studio solution folder nesting Fixes fatal error when loading generated solution files with Rider, also fixes folders with identical names getting mapped to same folder (Plugins folders within plugin projects). --- .../Projects/VisualStudio/VisualStudioProjectGenerator.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index 5cc1b2205..3438f8960 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -296,7 +296,7 @@ namespace Flax.Build.Projects.VisualStudio var folderIdMatches = new Regex("Project\\(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\"\\) = \"(.*?)\", \"(.*?)\", \"{(.*?)}\"").Matches(contents); foreach (Match match in folderIdMatches) { - var folder = match.Groups[1].Value; + var folder = match.Groups[2].Value; var folderId = Guid.ParseExact(match.Groups[3].Value, "D"); folderIds[folder] = folderId; } @@ -385,8 +385,7 @@ namespace Flax.Build.Projects.VisualStudio { if (!folderIds.TryGetValue(folderPath, out project.FolderGuid)) { - if (!folderIds.TryGetValue(folderParents[i], out project.FolderGuid)) - project.FolderGuid = Guid.NewGuid(); + project.FolderGuid = Guid.NewGuid(); folderIds.Add(folderPath, project.FolderGuid); } folderNames.Add(folderPath); @@ -401,7 +400,7 @@ namespace Flax.Build.Projects.VisualStudio var lastSplit = folder.LastIndexOf('\\'); var name = lastSplit != -1 ? folder.Substring(lastSplit + 1) : folder; - vcSolutionFileContent.AppendLine(string.Format("Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\"", typeGuid, name, name, folderGuid)); + vcSolutionFileContent.AppendLine(string.Format("Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\"", typeGuid, name, folder, folderGuid)); vcSolutionFileContent.AppendLine("EndProject"); } } From afb5edb82451fe1525b40b86cc0b8059b2e6a8c5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 8 Jan 2024 19:45:54 -0600 Subject: [PATCH 13/55] Adjust `ContextMenu` ShortKeys as needed to accommodate for scrollbar when visible. --- Source/Editor/GUI/ContextMenu/ContextMenu.cs | 4 ++-- Source/Editor/GUI/ContextMenu/ContextMenuButton.cs | 7 ++++++- Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs | 7 +------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/Editor/GUI/ContextMenu/ContextMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenu.cs index 25f45a1f8..072b01678 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenu.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenu.cs @@ -408,9 +408,9 @@ namespace FlaxEditor.GUI.ContextMenu { foreach (var child in _panel.Children) { - if (child is ContextMenuChildMenu item && item.Visible) + if (child is ContextMenuButton item && item.Visible) { - item.AdjustArrowAmount = -_panel.VScrollBar.Width; + item.ExtraAdjustmentAmount = -_panel.VScrollBar.Width; } } } diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index e371f7c4b..e79d75e46 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -14,6 +14,11 @@ namespace FlaxEditor.GUI.ContextMenu public class ContextMenuButton : ContextMenuItem { private bool _isMouseDown; + + /// + /// The amount to adjust the short keys and arrow image by in x coordinates. + /// + public float ExtraAdjustmentAmount = 0; /// /// Event fired when user clicks on the button. @@ -133,7 +138,7 @@ namespace FlaxEditor.GUI.ContextMenu if (!string.IsNullOrEmpty(ShortKeys)) { // Draw short keys - Render2D.DrawText(style.FontMedium, ShortKeys, textRect, textColor, TextAlignment.Far, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, ShortKeys, new Rectangle(ExtraAdjustmentAmount, textRect.Y, textRect.Width, textRect.Height), textColor, TextAlignment.Far, TextAlignment.Center); } // Draw icon diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs index 49a60a04e..d71166196 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs @@ -17,11 +17,6 @@ namespace FlaxEditor.GUI.ContextMenu /// public readonly ContextMenu ContextMenu = new ContextMenu(); - /// - /// The amount to adjust the arrow image by in x coordinates. - /// - public float AdjustArrowAmount = 0; - /// /// Initializes a new instance of the class. /// @@ -49,7 +44,7 @@ namespace FlaxEditor.GUI.ContextMenu // Draw arrow if (ContextMenu.HasChildren) - Render2D.DrawSprite(style.ArrowRight, new Rectangle(Width - 15 + AdjustArrowAmount, (Height - 12) / 2, 12, 12), Enabled ? isCMopened ? style.BackgroundSelected : style.Foreground : style.ForegroundDisabled); + Render2D.DrawSprite(style.ArrowRight, new Rectangle(Width - 15 + ExtraAdjustmentAmount, (Height - 12) / 2, 12, 12), Enabled ? isCMopened ? style.BackgroundSelected : style.Foreground : style.ForegroundDisabled); } /// From 1e061b77afc18d7dc1c2559ad9e062961c35b8aa Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 8 Jan 2024 19:48:12 -0600 Subject: [PATCH 14/55] Add back in checking for textRect x location. --- Source/Editor/GUI/ContextMenu/ContextMenuButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index e79d75e46..485cde1a7 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -138,7 +138,7 @@ namespace FlaxEditor.GUI.ContextMenu if (!string.IsNullOrEmpty(ShortKeys)) { // Draw short keys - Render2D.DrawText(style.FontMedium, ShortKeys, new Rectangle(ExtraAdjustmentAmount, textRect.Y, textRect.Width, textRect.Height), textColor, TextAlignment.Far, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, ShortKeys, new Rectangle(textRect.X + ExtraAdjustmentAmount, textRect.Y, textRect.Width, textRect.Height), textColor, TextAlignment.Far, TextAlignment.Center); } // Draw icon From 6289b9d15a2c1c7a5e87f6bc69227bcfbf4fe626 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Jan 2024 10:58:39 +0100 Subject: [PATCH 15/55] Fix crash due to incorrect PhysX usage for vehicle setup #1530 #2129 --- Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index af6c6403b..99a5abc56 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -3312,7 +3312,6 @@ void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor) // Create vehicle drive auto drive4W = PxVehicleDrive4W::allocate(wheels.Count()); drive4W->setup(PhysX, actorPhysX, *wheelsSimData, driveSimData, Math::Max(wheels.Count() - 4, 0)); - drive4W->setToRestState(); drive4W->mDriveDynData.forceGearChange(PxVehicleGearsData::eFIRST); drive4W->mDriveDynData.setUseAutoGears(gearbox.AutoGear); vehicle = drive4W; @@ -3355,7 +3354,6 @@ void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor) // Create vehicle drive auto driveNW = PxVehicleDriveNW::allocate(wheels.Count()); driveNW->setup(PhysX, actorPhysX, *wheelsSimData, driveSimData, wheels.Count()); - driveNW->setToRestState(); driveNW->mDriveDynData.forceGearChange(PxVehicleGearsData::eFIRST); driveNW->mDriveDynData.setUseAutoGears(gearbox.AutoGear); vehicle = driveNW; @@ -3366,7 +3364,6 @@ void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor) // Create vehicle drive auto driveNo = PxVehicleNoDrive::allocate(wheels.Count()); driveNo->setup(PhysX, actorPhysX, *wheelsSimData); - driveNo->setToRestState(); vehicle = driveNo; break; } From d807e9bfe066cfb092e1701a82581ab9c288d52d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Jan 2024 11:48:08 +0100 Subject: [PATCH 16/55] Fix crash when setting maanaged structure data via `Variant` #2163 --- Source/Engine/Core/Types/Variant.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 952e648e6..66e17b0a2 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -3985,15 +3985,32 @@ void Variant::CopyStructure(void* src) { if (AsBlob.Data && src) { - const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(StringAnsiView(Type.TypeName)); + const StringAnsiView typeName(Type.TypeName); + const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(typeName); if (typeHandle) { auto& type = typeHandle.GetType(); type.Struct.Copy(AsBlob.Data, src); } +#if USE_CSHARP + else if (const auto mclass = Scripting::FindClass(typeName)) + { + // Fallback to C#-only types + MCore::Thread::Attach(); + if (MANAGED_GC_HANDLE && mclass->IsValueType()) + { + MObject* instance = MCore::GCHandle::GetTarget(MANAGED_GC_HANDLE); + void* data = MCore::Object::Unbox(instance); + Platform::MemoryCopy(data, src, mclass->GetInstanceSize()); + } + } +#endif else { - Platform::MemoryCopy(AsBlob.Data, src, AsBlob.Length); + if (typeName.Length() != 0) + { + LOG(Warning, "Missing scripting type \'{0}\'", String(typeName)); + } } } } From b7cc4c768f4ba186376f0e03d804d3aa052970e4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Jan 2024 12:08:01 +0100 Subject: [PATCH 17/55] Fix unpacking `Variant` structure if input value is a scalar #2163 --- Source/Engine/Core/Types/Variant.cpp | 18 ++++++++++++++++++ Source/Engine/Visject/VisjectGraph.cpp | 8 +++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 66e17b0a2..9e8b69b2c 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -139,6 +139,24 @@ VariantType::VariantType(const StringAnsiView& typeName) return; } } + { + // Aliases + if (typeName == "FlaxEngine.Vector2") + { + new(this) VariantType(Vector2); + return; + } + if (typeName == "FlaxEngine.Vector3") + { + new(this) VariantType(Vector3); + return; + } + if (typeName == "FlaxEngine.Vector4") + { + new(this) VariantType(Vector4); + return; + } + } // Check case for array if (typeName.EndsWith(StringAnsiView("[]"), StringSearchCase::CaseSensitive)) diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index 64164ecec..ea2824e77 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -685,9 +685,9 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value) case 36: { // Get value with structure data - Variant structureValue = eatBox(node, node->GetBox(0)->FirstConnection()); if (!node->GetBox(0)->HasConnection()) return; + Variant structureValue = eatBox(node, node->GetBox(0)->FirstConnection()); // Find type const StringView typeName(node->Values[0]); @@ -741,6 +741,12 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value) return; } const ScriptingType& type = typeHandle.GetType(); + if (structureValue.Type.Type != VariantType::Structure) // If structureValue is eg. Float we can try to cast it to a required structure type + { + VariantType typeVariantType(typeNameAnsiView); + if (Variant::CanCast(structureValue, typeVariantType)) + structureValue = Variant::Cast(structureValue, typeVariantType); + } structureValue.InvertInline(); // Extract any Float3/Int32 into Structure type from inlined format const ScriptingTypeHandle structureValueTypeHandle = Scripting::FindScriptingType(structureValue.Type.GetTypeName()); if (structureValue.Type.Type != VariantType::Structure || typeHandle != structureValueTypeHandle) From 1094abce5a8ffe3f5bb88ab3a3864f820a29bd31 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Jan 2024 12:12:11 +0100 Subject: [PATCH 18/55] Fix crash when finding actor or level with empty name text #2161 --- Source/Engine/Level/Actor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index ff40cc208..7e2329389 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -1359,7 +1359,7 @@ bool Actor::IsPrefabRoot() const Actor* Actor::FindActor(const StringView& name) const { Actor* result = nullptr; - if (StringUtils::Compare(*_name, *name) == 0) + if (_name == name) { result = const_cast(this); } @@ -1393,7 +1393,7 @@ Actor* Actor::FindActor(const MClass* type) const Actor* Actor::FindActor(const MClass* type, const StringView& name) const { CHECK_RETURN(type, nullptr); - if (GetClass()->IsSubClassOf(type) && StringUtils::Compare(*_name, *name) == 0) + if (GetClass()->IsSubClassOf(type) && _name == name) return const_cast(this); for (auto child : Children) { From 2c76785bf0fc8c206d7441c5b527b8da0676e99a Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Wed, 10 Jan 2024 18:31:13 +0100 Subject: [PATCH 19/55] remove unused import --- Source/Engine/Level/Actors/AnimatedModel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 3a40dbf46..c1b5af398 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -15,7 +15,6 @@ #include "Engine/Graphics/Models/MeshDeformation.h" #include "Engine/Level/Scene/Scene.h" #include "Engine/Level/SceneObjectsFactory.h" -#include "Engine/Physics/Actors/RigidBody.h" #include "Engine/Serialization/Serialization.h" AnimatedModel::AnimatedModel(const SpawnParams& params) From d126f5bc55b2a0f8d2b2db6a983d96956401eebe Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Wed, 10 Jan 2024 18:36:05 +0100 Subject: [PATCH 20/55] use enum helper functions --- Source/Engine/Physics/Actors/RigidBody.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Physics/Actors/RigidBody.cpp b/Source/Engine/Physics/Actors/RigidBody.cpp index 15ea95075..71151866a 100644 --- a/Source/Engine/Physics/Actors/RigidBody.cpp +++ b/Source/Engine/Physics/Actors/RigidBody.cpp @@ -304,33 +304,31 @@ void RigidBody::AddMovement(const Vector3& translation, const Quaternion& rotati { // filter rotation according to constraints Quaternion allowedRotation; - if (static_cast(GetConstraints()) & static_cast(RigidbodyConstraints::LockRotation)) + if (EnumHasAllFlags(GetConstraints(), RigidbodyConstraints::LockRotation)) allowedRotation = Quaternion::Identity; else { Float3 euler = rotation.GetEuler(); - const auto constraints = static_cast(GetConstraints()); - if (constraints & static_cast(RigidbodyConstraints::LockRotationX)) + if (EnumHasAnyFlags(GetConstraints(), RigidbodyConstraints::LockRotationX)) euler.X = 0; - if (constraints & static_cast(RigidbodyConstraints::LockRotationY)) + if (EnumHasAnyFlags(GetConstraints(), RigidbodyConstraints::LockRotationY)) euler.Y = 0; - if (constraints & static_cast(RigidbodyConstraints::LockRotationZ)) + if (EnumHasAnyFlags(GetConstraints(), RigidbodyConstraints::LockRotationZ)) euler.Z = 0; allowedRotation = Quaternion::Euler(euler); } // filter translation according to the constraints auto allowedTranslation = translation; - if (static_cast(GetConstraints()) & static_cast(RigidbodyConstraints::LockPosition)) + if (EnumHasAllFlags(GetConstraints(), RigidbodyConstraints::LockPosition)) allowedTranslation = Vector3::Zero; else { - const auto constraints = static_cast(GetConstraints()); - if (constraints & static_cast(RigidbodyConstraints::LockPositionX)) + if (EnumHasAnyFlags(GetConstraints(), RigidbodyConstraints::LockPositionX)) allowedTranslation.X = 0; - if (constraints & static_cast(RigidbodyConstraints::LockPositionY)) + if (EnumHasAnyFlags(GetConstraints(), RigidbodyConstraints::LockPositionY)) allowedTranslation.Y = 0; - if (constraints & static_cast(RigidbodyConstraints::LockPositionZ)) + if (EnumHasAnyFlags(GetConstraints(), RigidbodyConstraints::LockPositionZ)) allowedTranslation.Z = 0; } Transform t; From 8922b5cd7970321a0ee91389a343d6f6e0b8396b Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Wed, 10 Jan 2024 18:38:29 +0100 Subject: [PATCH 21/55] clean up include file --- Source/Engine/Physics/Actors/RigidBody.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Source/Engine/Physics/Actors/RigidBody.h b/Source/Engine/Physics/Actors/RigidBody.h index b8c7d0208..ca1428810 100644 --- a/Source/Engine/Physics/Actors/RigidBody.h +++ b/Source/Engine/Physics/Actors/RigidBody.h @@ -430,12 +430,6 @@ public: /// The result point on the rigidbody shape that is closest to the specified location. API_FUNCTION() void ClosestPoint(const Vector3& position, API_PARAM(Out) Vector3& result) const; - /// - /// Moves and rotates the rigidbody in world space within the limits of defined constraints. - /// - /// The translation vector. - /// The rotation quaternion. - API_FUNCTION() void AddMovement(const Vector3& translation, const Quaternion& rotation) override; public: /// /// Occurs when a collision start gets registered for this rigidbody (it collides with something). @@ -492,6 +486,7 @@ public: // [Actor] void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + void AddMovement(const Vector3& translation, const Quaternion& rotation) override; // [IPhysicsActor] void* GetPhysicsActor() const override; From 76cf935583f1f6ea3c090316155853a02a4757fb Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 10 Jan 2024 22:00:20 -0600 Subject: [PATCH 22/55] Fix `Dropdown` panel scale correctly. --- Source/Engine/UI/GUI/Common/Dropdown.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 31ea87948..90454b586 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -606,14 +606,15 @@ namespace FlaxEngine.GUI _popup.LostFocus += DestroyPopup; // Show dropdown popup - var locationRootSpace = Location + new Float2(0, Height); + var locationRootSpace = Location + new Float2(0, Height - Height * (1 - Scale.Y) / 2); var parent = Parent; while (parent != null && parent != root) { locationRootSpace = parent.PointToParent(ref locationRootSpace); parent = parent.Parent; } - _popup.Location = locationRootSpace; + _popup.Scale = Scale; + _popup.Location = locationRootSpace - new Float2(0, _popup.Height * (1 - _popup.Scale.Y) / 2); _popup.Parent = root; _popup.Focus(); _popup.StartMouseCapture(); From 82c8e39dfd3174e7f97e87fc178443e15fe55d99 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 12 Jan 2024 17:23:10 -0600 Subject: [PATCH 23/55] Fix `AlwaysShowScrollbars` to update enabled state of the scroll bars. #2165 --- Source/Engine/UI/GUI/Panels/Panel.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs index bae165fba..d660d7b6f 100644 --- a/Source/Engine/UI/GUI/Panels/Panel.cs +++ b/Source/Engine/UI/GUI/Panels/Panel.cs @@ -132,6 +132,22 @@ namespace FlaxEngine.GUI if (_alwaysShowScrollbars != value) { _alwaysShowScrollbars = value; + switch (_scrollBars) + { + case ScrollBars.None: + break; + case ScrollBars.Horizontal: + HScrollBar.Enabled = true; + break; + case ScrollBars.Vertical: + VScrollBar.Enabled = true; + break; + case ScrollBars.Both: + HScrollBar.Enabled = true; + VScrollBar.Enabled = true; + break; + default: break; + } PerformLayout(); } } From 503bcdcf2795f535e92dcd640560e3dcb06a744b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 12 Jan 2024 19:57:00 -0600 Subject: [PATCH 24/55] Change visibility instead of enable on scroll bars. --- Source/Engine/UI/GUI/Panels/Panel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs index d660d7b6f..05cb9bd80 100644 --- a/Source/Engine/UI/GUI/Panels/Panel.cs +++ b/Source/Engine/UI/GUI/Panels/Panel.cs @@ -137,14 +137,14 @@ namespace FlaxEngine.GUI case ScrollBars.None: break; case ScrollBars.Horizontal: - HScrollBar.Enabled = true; + HScrollBar.Visible = value; break; case ScrollBars.Vertical: - VScrollBar.Enabled = true; + VScrollBar.Visible = value; break; case ScrollBars.Both: - HScrollBar.Enabled = true; - VScrollBar.Enabled = true; + HScrollBar.Visible = value; + VScrollBar.Visible = value; break; default: break; } From 2c5095e0a1c6e5157d20ade0a288ce6a4c6a6944 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 12 Jan 2024 20:35:59 -0600 Subject: [PATCH 25/55] Fix Plugin Project creation when the user types in symbols. --- Source/Editor/Windows/PluginsWindow.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index eb04f07b8..4145f200c 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -671,11 +671,11 @@ namespace FlaxEditor.Windows Editor.Log($"Using plugin code type name: {pluginCodeName}"); var oldPluginPath = Path.Combine(extractPath, "ExamplePlugin-master"); - var newPluginPath = Path.Combine(extractPath, pluginName); + var newPluginPath = Path.Combine(extractPath, pluginCodeName); Directory.Move(oldPluginPath, newPluginPath); var oldFlaxProjFile = Path.Combine(newPluginPath, "ExamplePlugin.flaxproj"); - var newFlaxProjFile = Path.Combine(newPluginPath, $"{pluginName}.flaxproj"); + var newFlaxProjFile = Path.Combine(newPluginPath, $"{pluginCodeName}.flaxproj"); File.Move(oldFlaxProjFile, newFlaxProjFile); var readme = Path.Combine(newPluginPath, "README.md"); @@ -687,7 +687,7 @@ namespace FlaxEditor.Windows // Flax plugin project file var flaxPluginProjContents = JsonSerializer.Deserialize(await File.ReadAllTextAsync(newFlaxProjFile)); - flaxPluginProjContents.Name = pluginName; + flaxPluginProjContents.Name = pluginCodeName; if (!string.IsNullOrEmpty(pluginVersion)) flaxPluginProjContents.Version = new Version(pluginVersion); if (!string.IsNullOrEmpty(companyName)) @@ -751,7 +751,7 @@ namespace FlaxEditor.Windows } Editor.Log($"Plugin project {pluginName} has successfully been created."); - await AddReferenceToProject(pluginName, pluginName); + await AddReferenceToProject(pluginCodeName, pluginCodeName); MessageBox.Show($"{pluginName} has been successfully created. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); } From 40e75465a328fb14ac9051dbdffe76f9d8563f12 Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Wed, 17 Jan 2024 22:46:49 +0100 Subject: [PATCH 26/55] fix plugin entry layout --- Source/Editor/Windows/PluginsWindow.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index eb04f07b8..f2fa2f197 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -95,6 +95,7 @@ namespace FlaxEditor.Windows Bounds = new Rectangle(nameLabel.X, tmp1, nameLabel.Width, Height - tmp1 - margin), }; + var xOffset = nameLabel.X + nameLabel.Width; string versionString = string.Empty; if (desc.IsAlpha) versionString = "ALPHA "; @@ -109,7 +110,7 @@ namespace FlaxEditor.Windows AnchorPreset = AnchorPresets.TopRight, Text = versionString, Parent = this, - Bounds = new Rectangle(Width - 140 - margin, margin, 140, 14), + Bounds = new Rectangle(Width - 140 - margin - xOffset, margin, 140, 14), }; string url = null; @@ -129,7 +130,7 @@ namespace FlaxEditor.Windows AnchorPreset = AnchorPresets.TopRight, Text = desc.Author, Parent = this, - Bounds = new Rectangle(Width - authorWidth - margin, versionLabel.Bottom + margin, authorWidth, 14), + Bounds = new Rectangle(Width - authorWidth - margin - xOffset, versionLabel.Bottom + margin, authorWidth, 14), }; if (url != null) { From 0d3b81b8ca8e82f3e39202efb3506651ab417680 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 22 Jan 2024 17:37:02 -0600 Subject: [PATCH 27/55] Change drop location of multiple nodes to be vertical. --- Source/Editor/Surface/MaterialSurface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/MaterialSurface.cs b/Source/Editor/Surface/MaterialSurface.cs index ba7f2e01e..a7fa6cb38 100644 --- a/Source/Editor/Surface/MaterialSurface.cs +++ b/Source/Editor/Surface/MaterialSurface.cs @@ -105,7 +105,7 @@ namespace FlaxEditor.Surface if (node != null) { - args.SurfaceLocation.X += node.Width + 10; + args.SurfaceLocation.Y += node.Height + 10; } } From ce658acb22a0ba9b5eb3ef41177b37cd3f9cabed Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 22 Jan 2024 18:03:14 -0600 Subject: [PATCH 28/55] Dont include editor modules in auto generated git plugin pulling. --- Source/Editor/Windows/PluginsWindow.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index eb04f07b8..5bb6ea492 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -775,8 +775,12 @@ namespace FlaxEditor.Windows var pluginModuleScriptPath = Path.Combine(subDir, pluginModuleName + ".Build.cs"); if (File.Exists(pluginModuleScriptPath)) { - gameScriptContents = gameScriptContents.Insert(insertLocation, $"\n options.PublicDependencies.Add(\"{pluginModuleName}\");"); - modifiedAny = true; + var text = await File.ReadAllTextAsync(pluginModuleScriptPath); + if (!text.Contains("GameEditorModule", StringComparison.OrdinalIgnoreCase)) + { + gameScriptContents = gameScriptContents.Insert(insertLocation, $"\n options.PublicDependencies.Add(\"{pluginModuleName}\");"); + modifiedAny = true; + } } } From d5e9ad2147a1b6088b3ba26bff082cbd48077b78 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 22 Jan 2024 21:01:04 -0600 Subject: [PATCH 29/55] Adjust combobox window position based on what direction it opens. --- Source/Editor/GUI/ComboBox.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 0417cc7e3..176e83374 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -428,6 +428,13 @@ namespace FlaxEditor.GUI // Show dropdown list _popupMenu.MinimumWidth = Width; _popupMenu.Show(this, new Float2(1, Height)); + + // Adjust menu position if it is not the down direction + if (_popupMenu.Direction == ContextMenuDirection.RightUp) + { + var position = _popupMenu.RootWindow.Window.Position; + _popupMenu.RootWindow.Window.Position = new Float2(position.X, position.Y - Height); + } } } From 045f49d2d62e36d7967f7031a9e84d2fc06b8294 Mon Sep 17 00:00:00 2001 From: envision3d Date: Mon, 22 Jan 2024 22:25:53 -0600 Subject: [PATCH 30/55] Fixes terrain smoothing separation issue --- .../Tools/Terrain/Sculpt/FlattenMode.cs | 2 +- .../Editor/Tools/Terrain/Sculpt/HolesMode.cs | 2 +- Source/Editor/Tools/Terrain/Sculpt/Mode.cs | 107 +++++++---- .../Editor/Tools/Terrain/Sculpt/NoiseMode.cs | 2 +- .../Editor/Tools/Terrain/Sculpt/SculptMode.cs | 2 +- .../Editor/Tools/Terrain/Sculpt/SmoothMode.cs | 166 ++++++++++++++---- 6 files changed, 214 insertions(+), 67 deletions(-) diff --git a/Source/Editor/Tools/Terrain/Sculpt/FlattenMode.cs b/Source/Editor/Tools/Terrain/Sculpt/FlattenMode.cs index 610a336b7..bf80eb34d 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/FlattenMode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/FlattenMode.cs @@ -18,7 +18,7 @@ namespace FlaxEditor.Tools.Terrain.Sculpt public float TargetHeight = 0.0f; /// - public override unsafe void Apply(ref ApplyParams p) + public override unsafe void ApplyBrushToPatch(ref ApplyParams p) { // If used with invert mode pick the target height level if (p.Options.Invert) diff --git a/Source/Editor/Tools/Terrain/Sculpt/HolesMode.cs b/Source/Editor/Tools/Terrain/Sculpt/HolesMode.cs index d4c10f00e..3d75a56f2 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/HolesMode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/HolesMode.cs @@ -26,7 +26,7 @@ namespace FlaxEditor.Tools.Terrain.Sculpt } /// - public override unsafe void Apply(ref ApplyParams p) + public override unsafe void ApplyBrushToPatch(ref ApplyParams p) { var strength = p.Strength * -10.0f; var brushPosition = p.Gizmo.CursorPosition; diff --git a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs index 9a42d2ae8..a92b899e1 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Collections.Generic; using FlaxEditor.Tools.Terrain.Brushes; using FlaxEngine; @@ -50,18 +51,20 @@ namespace FlaxEditor.Tools.Terrain.Sculpt public virtual bool EditHoles => false; /// - /// Applies the modification to the terrain. + /// Gets all patches that will be affected by the brush /// - /// The brush. - /// The options. - /// The gizmo. - /// The terrain. - public unsafe void Apply(Brush brush, ref Options options, SculptTerrainGizmoMode gizmo, FlaxEngine.Terrain terrain) + /// + /// + /// + /// + public unsafe virtual List GetAffectedPatches(Brush brush, ref Options options, SculptTerrainGizmoMode gizmo, FlaxEngine.Terrain terrain) { + List affectedPatches = new(); + // Combine final apply strength float strength = Strength * options.Strength * options.DeltaTime; if (strength <= 0.0f) - return; + return affectedPatches; if (options.Invert && SupportsNegativeApply) strength *= -1; @@ -72,20 +75,10 @@ namespace FlaxEditor.Tools.Terrain.Sculpt var patchSize = chunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount; var tempBuffer = (float*)gizmo.GetHeightmapTempBuffer(heightmapLength * sizeof(float)).ToPointer(); var unitsPerVertexInv = 1.0f / FlaxEngine.Terrain.UnitsPerVertex; - ApplyParams p = new ApplyParams - { - Terrain = terrain, - Brush = brush, - Gizmo = gizmo, - Options = options, - Strength = strength, - HeightmapSize = heightmapSize, - TempBuffer = tempBuffer, - }; // Get brush bounds in terrain local space var brushBounds = gizmo.CursorBrushBounds; - terrain.GetLocalToWorldMatrix(out p.TerrainWorld); + terrain.GetLocalToWorldMatrix(out var terrainWorld); terrain.GetWorldToLocalMatrix(out var terrainInvWorld); BoundingBox.Transform(ref brushBounds, ref terrainInvWorld, out var brushBoundsLocal); @@ -131,26 +124,78 @@ namespace FlaxEditor.Tools.Terrain.Sculpt if (sourceHeights == null && sourceHoles == null) throw new Exception("Cannot modify terrain. Loading heightmap failed. See log for more info."); - // Record patch data before editing it - if (!gizmo.CurrentEditUndoAction.HashPatch(ref patch.PatchCoord)) + ApplyParams p = new ApplyParams { - gizmo.CurrentEditUndoAction.AddPatch(ref patch.PatchCoord); - } + Terrain = terrain, + TerrainWorld = terrainWorld, + Brush = brush, + Gizmo = gizmo, + Options = options, + Strength = strength, + HeightmapSize = heightmapSize, + TempBuffer = tempBuffer, + ModifiedOffset = modifiedOffset, + ModifiedSize = modifiedSize, + PatchCoord = patch.PatchCoord, + PatchPositionLocal = patchPositionLocal, + SourceHeightMap = sourceHeights, + SourceHolesMask = sourceHoles, + }; - // Apply modification - p.ModifiedOffset = modifiedOffset; - p.ModifiedSize = modifiedSize; - p.PatchCoord = patch.PatchCoord; - p.PatchPositionLocal = patchPositionLocal; - p.SourceHeightMap = sourceHeights; - p.SourceHolesMask = sourceHoles; - Apply(ref p); + affectedPatches.Add(p); } + return affectedPatches; + } + + /// + /// Applies the modification to the terrain. + /// + /// The brush. + /// The options. + /// The gizmo. + /// The terrain. + public unsafe void Apply(Brush brush, ref Options options, SculptTerrainGizmoMode gizmo, FlaxEngine.Terrain terrain) + { + var affectedPatches = GetAffectedPatches(brush, ref options, gizmo, terrain); + + if (affectedPatches.Count == 0) + { + return; + } + + ApplyBrush(gizmo, affectedPatches); + // Auto NavMesh rebuild + var brushBounds = gizmo.CursorBrushBounds; gizmo.CurrentEditUndoAction.AddDirtyBounds(ref brushBounds); } + /// + /// Applies the brush to all affected patches + /// + /// + /// + public unsafe virtual void ApplyBrush(SculptTerrainGizmoMode gizmo, List affectedPatches) + { + for (int i = 0; i < affectedPatches.Count; i++) + { + ApplyParams patchApplyParams = affectedPatches[i]; + + // Record patch data before editing it + if (!gizmo.CurrentEditUndoAction.HashPatch(ref patchApplyParams.PatchCoord)) + { + gizmo.CurrentEditUndoAction.AddPatch(ref patchApplyParams.PatchCoord); + } + + ApplyBrushToPatch(ref patchApplyParams); + + // Auto NavMesh rebuild + var brushBounds = gizmo.CursorBrushBounds; + gizmo.CurrentEditUndoAction.AddDirtyBounds(ref brushBounds); + } + } + /// /// The mode apply parameters. /// @@ -231,6 +276,6 @@ namespace FlaxEditor.Tools.Terrain.Sculpt /// Applies the modification to the terrain. /// /// The parameters to use. - public abstract void Apply(ref ApplyParams p); + public abstract void ApplyBrushToPatch(ref ApplyParams p); } } diff --git a/Source/Editor/Tools/Terrain/Sculpt/NoiseMode.cs b/Source/Editor/Tools/Terrain/Sculpt/NoiseMode.cs index 32d02bb3f..7fcb09288 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/NoiseMode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/NoiseMode.cs @@ -29,7 +29,7 @@ namespace FlaxEditor.Tools.Terrain.Sculpt public override bool SupportsNegativeApply => true; /// - public override unsafe void Apply(ref ApplyParams p) + public override unsafe void ApplyBrushToPatch(ref ApplyParams p) { // Prepare var brushPosition = p.Gizmo.CursorPosition; diff --git a/Source/Editor/Tools/Terrain/Sculpt/SculptMode.cs b/Source/Editor/Tools/Terrain/Sculpt/SculptMode.cs index c3409f30c..381b39ebd 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/SculptMode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/SculptMode.cs @@ -15,7 +15,7 @@ namespace FlaxEditor.Tools.Terrain.Sculpt public override bool SupportsNegativeApply => true; /// - public override unsafe void Apply(ref ApplyParams p) + public override unsafe void ApplyBrushToPatch(ref ApplyParams p) { var strength = p.Strength * 1000.0f; var brushPosition = p.Gizmo.CursorPosition; diff --git a/Source/Editor/Tools/Terrain/Sculpt/SmoothMode.cs b/Source/Editor/Tools/Terrain/Sculpt/SmoothMode.cs index d23eae8dd..49ccfe0de 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/SmoothMode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/SmoothMode.cs @@ -1,7 +1,9 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System; +using System.Collections.Generic; using FlaxEngine; +using FlaxEditor.Tools.Terrain.Brushes; +using System; namespace FlaxEditor.Tools.Terrain.Sculpt { @@ -19,43 +21,135 @@ namespace FlaxEditor.Tools.Terrain.Sculpt public float FilterRadius = 0.4f; /// - public override unsafe void Apply(ref ApplyParams p) + public override unsafe void ApplyBrush(SculptTerrainGizmoMode gizmo, List affectedPatches) { - // Prepare - var brushPosition = p.Gizmo.CursorPosition; - var radius = Mathf.Max(Mathf.CeilToInt(FilterRadius * 0.01f * p.Brush.Size), 2); - var max = p.HeightmapSize - 1; - var strength = Mathf.Saturate(p.Strength); - - // Apply brush modification Profiler.BeginEvent("Apply Brush"); - for (int z = 0; z < p.ModifiedSize.Y; z++) + + // TODO: don't need these on each patch; just need them once + var heightmapSize = affectedPatches[0].HeightmapSize; + var radius = Mathf.Max(Mathf.CeilToInt(FilterRadius * 0.01f * affectedPatches[0].Brush.Size), 2); + + + ///// + /// Calculate bounding coordinates of the total affected area + /// + + + Int2 modifieedAreaMinCoord = Int2.Maximum; + Int2 modifiedAreaMaxCoord = Int2.Minimum; + + for (int i = 0; i < affectedPatches.Count; i++) { - var zz = z + p.ModifiedOffset.Y; - for (int x = 0; x < p.ModifiedSize.X; x++) + var patch = affectedPatches[i]; + + var tl = (patch.PatchCoord * (heightmapSize - 1)) + patch.ModifiedOffset; + var br = tl + patch.ModifiedSize; + + if (tl.X <= modifieedAreaMinCoord.X && tl.Y <= modifieedAreaMinCoord.Y) { - var xx = x + p.ModifiedOffset.X; - var sourceHeight = p.SourceHeightMap[zz * p.HeightmapSize + xx]; + modifieedAreaMinCoord = tl; + } - var samplePositionLocal = p.PatchPositionLocal + new Vector3(xx * FlaxEngine.Terrain.UnitsPerVertex, sourceHeight, zz * FlaxEngine.Terrain.UnitsPerVertex); - Vector3.Transform(ref samplePositionLocal, ref p.TerrainWorld, out Vector3 samplePositionWorld); + if (br.X >= modifiedAreaMaxCoord.X && br.Y >= modifiedAreaMaxCoord.Y) + { + modifiedAreaMaxCoord = br; + } + } - var paintAmount = p.Brush.Sample(ref brushPosition, ref samplePositionWorld) * strength; - if (paintAmount > 0) + var totalModifiedSize = modifiedAreaMaxCoord - modifieedAreaMinCoord; + + + ///// + /// Build map of heights in affected area + /// + + + var modifiedHeights = new float[totalModifiedSize.X * totalModifiedSize.Y]; + + for (int i = 0; i < affectedPatches.Count; i++) + { + var patch = affectedPatches[i]; + + for (int z = 0; z < patch.ModifiedSize.Y; z++) + { + for (int x = 0; x < patch.ModifiedSize.X; x++) { + // read height from current patch + var localCoordX = (x + patch.ModifiedOffset.X); + var localCoordY = (z + patch.ModifiedOffset.Y); + var coordHeight = patch.SourceHeightMap[(localCoordY * heightmapSize) + localCoordX]; + + // calculate the absolute coordinate of the terrain point + var absoluteCurrentPointCoord = patch.PatchCoord * (heightmapSize - 1) + patch.ModifiedOffset + new Int2(x, z); + var currentPointCoordRelativeToModifiedArea = absoluteCurrentPointCoord - modifieedAreaMinCoord; + + // store height + var index = (currentPointCoordRelativeToModifiedArea.Y * totalModifiedSize.X) + currentPointCoordRelativeToModifiedArea.X; + modifiedHeights[index] = coordHeight; + } + } + } + + + ///// + /// Iterate through modified points and smooth now that we have height information for all necessary points + /// + + + for (int i = 0; i < affectedPatches.Count; i++) + { + var patch = affectedPatches[i]; + + var brushPosition = patch.Gizmo.CursorPosition; + var strength = Mathf.Saturate(patch.Strength); + + for (int z = 0; z < patch.ModifiedSize.Y; z++) + { + for (int x = 0; x < patch.ModifiedSize.X; x++) + { + // read height from current patch + var localCoordX = (x + patch.ModifiedOffset.X); + var localCoordY = (z + patch.ModifiedOffset.Y); + var coordHeight = patch.SourceHeightMap[(localCoordY * heightmapSize) + localCoordX]; + + // calculate the absolute coordinate of the terrain point + var absoluteCurrentPointCoord = patch.PatchCoord * (heightmapSize - 1) + patch.ModifiedOffset + new Int2(x, z); + var currentPointCoordRelativeToModifiedArea = absoluteCurrentPointCoord - modifieedAreaMinCoord; + + // calculate brush influence at the current position + var samplePositionLocal = patch.PatchPositionLocal + new Vector3(localCoordX * FlaxEngine.Terrain.UnitsPerVertex, coordHeight, localCoordY * FlaxEngine.Terrain.UnitsPerVertex); + Vector3.Transform(ref samplePositionLocal, ref patch.TerrainWorld, out Vector3 samplePositionWorld); + var paintAmount = patch.Brush.Sample(ref brushPosition, ref samplePositionWorld) * strength; + + if (paintAmount == 0) + { + patch.TempBuffer[z * patch.ModifiedSize.X + x] = coordHeight; + continue; + } + + // Record patch data before editing it + if (!gizmo.CurrentEditUndoAction.HashPatch(ref patch.PatchCoord)) + { + gizmo.CurrentEditUndoAction.AddPatch(ref patch.PatchCoord); + } + // Sum the nearby values float smoothValue = 0; int smoothValueSamples = 0; - int minX = Math.Max(x - radius + p.ModifiedOffset.X, 0); - int minZ = Math.Max(z - radius + p.ModifiedOffset.Y, 0); - int maxX = Math.Min(x + radius + p.ModifiedOffset.X, max); - int maxZ = Math.Min(z + radius + p.ModifiedOffset.Y, max); + + var minX = Math.Max(0, currentPointCoordRelativeToModifiedArea.X - radius); + var maxX = Math.Min(totalModifiedSize.X - 1, currentPointCoordRelativeToModifiedArea.X + radius); + var minZ = Math.Max(0, currentPointCoordRelativeToModifiedArea.Y - radius); + var maxZ = Math.Min(totalModifiedSize.Y - 1, currentPointCoordRelativeToModifiedArea.Y + radius); + for (int dz = minZ; dz <= maxZ; dz++) { for (int dx = minX; dx <= maxX; dx++) { - var height = p.SourceHeightMap[dz * p.HeightmapSize + dx]; + var coordIndex = (dz * totalModifiedSize.X) + dx; + var height = modifiedHeights[coordIndex]; + smoothValue += height; smoothValueSamples++; } @@ -65,18 +159,26 @@ namespace FlaxEditor.Tools.Terrain.Sculpt smoothValue /= smoothValueSamples; // Blend between the height and smooth value - p.TempBuffer[z * p.ModifiedSize.X + x] = Mathf.Lerp(sourceHeight, smoothValue, paintAmount); - } - else - { - p.TempBuffer[z * p.ModifiedSize.X + x] = sourceHeight; + var newHeight = Mathf.Lerp(coordHeight, smoothValue, paintAmount); + patch.TempBuffer[z * patch.ModifiedSize.X + x] = newHeight; } } - } - Profiler.EndEvent(); - // Update terrain patch - TerrainTools.ModifyHeightMap(p.Terrain, ref p.PatchCoord, p.TempBuffer, ref p.ModifiedOffset, ref p.ModifiedSize); + // Update terrain patch + TerrainTools.ModifyHeightMap(patch.Terrain, ref patch.PatchCoord, patch.TempBuffer, ref patch.ModifiedOffset, ref patch.ModifiedSize); + } + + // Auto NavMesh rebuild + var brushBounds = gizmo.CursorBrushBounds; + gizmo.CurrentEditUndoAction.AddDirtyBounds(ref brushBounds); + + Profiler.EndEvent(); + } + + /// + public override unsafe void ApplyBrushToPatch(ref ApplyParams p) + { + // noop; unused } } } From f306d34a6e953e81182617ea9c164036fa98d897 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 23 Jan 2024 08:34:15 -0600 Subject: [PATCH 31/55] Add drag drop for actor script items into scenes and prefabs. --- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 46 +++++++++++++++++++ .../Viewport/MainEditorGizmoViewport.cs | 11 ++++- .../Editor/Viewport/PrefabWindowViewport.cs | 11 ++++- .../Editor/Viewport/ViewportDraggingHelper.cs | 24 +++++++++- .../Windows/Assets/PrefabWindow.Hierarchy.cs | 40 ++++++++++++++++ Source/Editor/Windows/SceneTreeWindow.cs | 42 +++++++++++++++++ 6 files changed, 170 insertions(+), 4 deletions(-) diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 9cfe5b7bd..d31955acc 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -29,6 +29,7 @@ namespace FlaxEditor.SceneGraph.GUI private DragScripts _dragScripts; private DragAssets _dragAssets; private DragActorType _dragActorType; + private DragScriptItems _dragScriptItems; private DragHandlers _dragHandlers; private List _highlights; private bool _hasSearchFilter; @@ -395,6 +396,13 @@ namespace FlaxEditor.SceneGraph.GUI } if (_dragActorType.OnDragEnter(data)) return _dragActorType.Effect; + if (_dragScriptItems == null) + { + _dragScriptItems = new DragScriptItems(ValidateDragScriptItem); + _dragHandlers.Add(_dragScriptItems); + } + if (_dragScriptItems.OnDragEnter(data)) + return _dragScriptItems.Effect; return DragDropEffect.None; } @@ -673,7 +681,37 @@ namespace FlaxEditor.SceneGraph.GUI actor.Transform = Actor.Transform; ActorNode.Root.Spawn(actor, Actor); } + result = DragDropEffect.Move; + } + // Drag script item + else if (_dragScriptItems != null && _dragScriptItems.HasValidDrag) + { + var spawnParent = myActor; + if (DragOverMode == DragItemPositioning.Above || DragOverMode == DragItemPositioning.Below) + spawnParent = newParent; + for (int i = 0; i < _dragScriptItems.Objects.Count; i++) + { + var item = _dragScriptItems.Objects[i]; + // Find actors with the same content item and spawn them. + foreach (var actorType in Editor.Instance.CodeEditing.Actors.Get()) + { + if (actorType.ContentItem != item) + continue; + + var actor = actorType.CreateInstance() as Actor; + if (actor == null) + { + Editor.LogWarning("Failed to spawn actor of type " + actorType.TypeName); + continue; + } + actor.StaticFlags = spawnParent.StaticFlags; + actor.Name = actorType.Name; + actor.Transform = spawnParent.Transform; + ActorNode.Root.Spawn(actor, spawnParent); + actor.OrderInParent = newOrder; + } + } result = DragDropEffect.Move; } @@ -728,6 +766,14 @@ namespace FlaxEditor.SceneGraph.GUI return true; } + private static bool ValidateDragScriptItem(ScriptItem script) + { + var actors = Editor.Instance.CodeEditing.Actors.Get(); + if (actors.Any(x => x.ContentItem == script)) + return true; + return false; + } + /// protected override void DoDragDrop() { diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 24c8f1c4c..c501def33 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; @@ -194,7 +195,7 @@ namespace FlaxEditor.Viewport : base(Object.New(), editor.Undo, editor.Scene.Root) { _editor = editor; - DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType); + DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType, ValidateDragScriptItem); var inputOptions = editor.Options.Options.Input; // Prepare rendering task @@ -940,6 +941,14 @@ namespace FlaxEditor.Viewport return Level.IsAnySceneLoaded; } + private static bool ValidateDragScriptItem(ScriptItem script) + { + var actors = Editor.Instance.CodeEditing.Actors.Get(); + if (actors.Any(x => x.ContentItem == script)) + return true; + return false; + } + /// public override DragDropEffect OnDragMove(ref Float2 location, DragData data) { diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 6e111f588..a46002028 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; @@ -81,7 +82,7 @@ namespace FlaxEditor.Viewport _window.SelectionChanged += OnSelectionChanged; Undo = window.Undo; ViewportCamera = new FPSCamera(); - DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType); + DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType, ValidateDragScriptItem); ShowDebugDraw = true; ShowEditorPrimitives = true; Gizmos = new GizmosCollection(this); @@ -701,6 +702,14 @@ namespace FlaxEditor.Viewport { return true; } + + private static bool ValidateDragScriptItem(ScriptItem script) + { + var actors = Editor.Instance.CodeEditing.Actors.Get(); + if (actors.Any(x => x.ContentItem == script)) + return true; + return false; + } /// public override DragDropEffect OnDragMove(ref Float2 location, DragData data) diff --git a/Source/Editor/Viewport/ViewportDraggingHelper.cs b/Source/Editor/Viewport/ViewportDraggingHelper.cs index 8a1b4f183..83c9cfab0 100644 --- a/Source/Editor/Viewport/ViewportDraggingHelper.cs +++ b/Source/Editor/Viewport/ViewportDraggingHelper.cs @@ -39,17 +39,19 @@ namespace FlaxEditor.Viewport private readonly EditorViewport _viewport; private readonly DragAssets _dragAssets; private readonly DragActorType _dragActorType; + private readonly DragScriptItems _dragScriptItem; private StaticModel _previewStaticModel; private int _previewModelEntryIndex; private BrushSurface _previewBrushSurface; - internal ViewportDragHandlers(IGizmoOwner owner, EditorViewport viewport, Func validateAsset, Func validateDragActorType) + internal ViewportDragHandlers(IGizmoOwner owner, EditorViewport viewport, Func validateAsset, Func validateDragActorType, Func validateDragScriptItem) { _owner = owner; _viewport = viewport; Add(_dragAssets = new DragAssets(validateAsset)); Add(_dragActorType = new DragActorType(validateDragActorType)); + Add(_dragScriptItem = new DragScriptItems(validateDragScriptItem)); } internal void ClearDragEffects() @@ -102,7 +104,13 @@ namespace FlaxEditor.Viewport foreach (var actorType in _dragActorType.Objects) Spawn(actorType, hit, ref location, ref hitLocation, ref hitNormal); } - + else if (_dragScriptItem.HasValidDrag) + { + result = _dragScriptItem.Effect; + foreach (var scripItem in _dragScriptItem.Objects) + Spawn(scripItem, hit, ref location, ref hitLocation, ref hitNormal); + } + Debug.Log("Hit"); OnDragDrop(new DragDropEventArgs { Hit = hit, HitLocation = hitLocation }); return result; @@ -193,6 +201,18 @@ namespace FlaxEditor.Viewport _viewport.Focus(); } + private void Spawn(ScriptItem item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal) + { + // Find actors with the same content item and spawn them. + foreach (var actorType in Editor.Instance.CodeEditing.Actors.Get()) + { + if (actorType.ContentItem != item) + continue; + + Spawn(actorType, hit, ref location, ref hitLocation, ref hitNormal); + } + } + private void Spawn(ScriptType item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal) { var actor = item.CreateInstance() as Actor; diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index f263d4734..1cf5fb40b 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Content; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Drag; @@ -64,6 +65,7 @@ namespace FlaxEditor.Windows.Assets private PrefabWindow _window; private DragAssets _dragAssets; private DragActorType _dragActorType; + private DragScriptItems _dragScriptItems; private DragHandlers _dragHandlers; public SceneTreePanel(PrefabWindow window) @@ -83,6 +85,14 @@ namespace FlaxEditor.Windows.Assets { return true; } + + private static bool ValidateDragScriptItem(ScriptItem script) + { + var actors = Editor.Instance.CodeEditing.Actors.Get(); + if (actors.Any(x => x.ContentItem == script)) + return true; + return false; + } /// public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) @@ -106,6 +116,13 @@ namespace FlaxEditor.Windows.Assets } if (_dragActorType.OnDragEnter(data)) return _dragActorType.Effect; + if (_dragScriptItems == null) + { + _dragScriptItems = new DragScriptItems(ValidateDragScriptItem); + _dragHandlers.Add(_dragScriptItems); + } + if (_dragScriptItems.OnDragEnter(data)) + return _dragScriptItems.Effect; } return result; } @@ -162,7 +179,30 @@ namespace FlaxEditor.Windows.Assets } result = DragDropEffect.Move; } + // Drag script item + else if (_dragScriptItems != null && _dragScriptItems.HasValidDrag) + { + for (int i = 0; i < _dragScriptItems.Objects.Count; i++) + { + var item = _dragScriptItems.Objects[i]; + // Find actors with the same content item and spawn them. + foreach (var actorType in Editor.Instance.CodeEditing.Actors.Get()) + { + if (actorType.ContentItem != item) + continue; + var actor = actorType.CreateInstance() as Actor; + if (actor == null) + { + Editor.LogWarning("Failed to spawn actor of type " + actorType.TypeName); + continue; + } + actor.Name = actorType.Name; + _window.Spawn(actor); + } + } + result = DragDropEffect.Move; + } _dragHandlers.OnDragDrop(null); } return result; diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 63ba7b960..c1390a1fe 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Gizmo; using FlaxEditor.Content; using FlaxEditor.GUI.Tree; @@ -31,6 +32,7 @@ namespace FlaxEditor.Windows private DragAssets _dragAssets; private DragActorType _dragActorType; + private DragScriptItems _dragScriptItems; private DragHandlers _dragHandlers; /// @@ -272,6 +274,14 @@ namespace FlaxEditor.Windows { return true; } + + private static bool ValidateDragScriptItem(ScriptItem script) + { + var actors = Editor.Instance.CodeEditing.Actors.Get(); + if (actors.Any(x => x.ContentItem == script)) + return true; + return false; + } /// public override void Draw() @@ -380,6 +390,13 @@ namespace FlaxEditor.Windows } if (_dragActorType.OnDragEnter(data) && result == DragDropEffect.None) return _dragActorType.Effect; + if (_dragScriptItems == null) + { + _dragScriptItems = new DragScriptItems(ValidateDragScriptItem); + _dragHandlers.Add(_dragScriptItems); + } + if (_dragScriptItems.OnDragEnter(data) && result == DragDropEffect.None) + return _dragScriptItems.Effect; } return result; } @@ -445,6 +462,31 @@ namespace FlaxEditor.Windows } result = DragDropEffect.Move; } + // Drag script item + else if (_dragScriptItems != null && _dragScriptItems.HasValidDrag) + { + for (int i = 0; i < _dragScriptItems.Objects.Count; i++) + { + var item = _dragScriptItems.Objects[i]; + // Find actors with the same content item and spawn them. + foreach (var actorType in Editor.Instance.CodeEditing.Actors.Get()) + { + if (actorType.ContentItem != item) + continue; + + var actor = actorType.CreateInstance() as Actor; + if (actor == null) + { + Editor.LogWarning("Failed to spawn actor of type " + actorType.TypeName); + continue; + } + actor.Name = actorType.Name; + Level.SpawnActor(actor); + Editor.Scene.MarkSceneEdited(actor.Scene); + } + } + result = DragDropEffect.Move; + } _dragHandlers.OnDragDrop(null); } From b85f471fcb0b7cad2b6eb8d399989a1ff460251f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 23 Jan 2024 08:59:27 -0600 Subject: [PATCH 32/55] Add drag cleanup on destroy. --- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 1 + Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs | 1 + Source/Editor/Windows/SceneTreeWindow.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index d31955acc..64def3eae 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -814,6 +814,7 @@ namespace FlaxEditor.SceneGraph.GUI _dragScripts = null; _dragAssets = null; _dragActorType = null; + _dragScriptItems = null; _dragHandlers?.Clear(); _dragHandlers = null; _highlights = null; diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 1cf5fb40b..351dd3f62 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -213,6 +213,7 @@ namespace FlaxEditor.Windows.Assets _window = null; _dragAssets = null; _dragActorType = null; + _dragScriptItems = null; _dragHandlers?.Clear(); _dragHandlers = null; diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index c1390a1fe..9385fb781 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -498,6 +498,7 @@ namespace FlaxEditor.Windows { _dragAssets = null; _dragActorType = null; + _dragScriptItems = null; _dragHandlers?.Clear(); _dragHandlers = null; _tree = null; From 39e7be6322a21353bf26f9799e083e1c12604f36 Mon Sep 17 00:00:00 2001 From: whocares77 <97740209+whocares77@users.noreply.github.com> Date: Wed, 24 Jan 2024 19:58:29 +0300 Subject: [PATCH 33/55] Added "Start Time" option for Audio Source Actor Added ability to set the start time of playback if "Play On Start" is enabled. --- Source/Engine/Audio/AudioSource.cpp | 11 ++++++++++- Source/Engine/Audio/AudioSource.h | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index cb32e0967..a474ec0c7 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -19,6 +19,7 @@ AudioSource::AudioSource(const SpawnParams& params) , _minDistance(1000.0f) , _loop(false) , _playOnStart(false) + , _startTime(0.0f) , _allowSpatialization(true) { Clip.Changed.Bind(this); @@ -71,6 +72,11 @@ void AudioSource::SetPlayOnStart(bool value) _playOnStart = value; } +void AudioSource::SetStartTime(float value) +{ + _startTime = value; +} + void AudioSource::SetMinDistance(float value) { value = Math::Max(0.0f, value); @@ -361,6 +367,7 @@ void AudioSource::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_MEMBER(DopplerFactor, _dopplerFactor); SERIALIZE_MEMBER(Loop, _loop); SERIALIZE_MEMBER(PlayOnStart, _playOnStart); + SERIALIZE_MEMBER(StartTime, _startTime); SERIALIZE_MEMBER(AllowSpatialization, _allowSpatialization); } @@ -377,6 +384,7 @@ void AudioSource::Deserialize(DeserializeStream& stream, ISerializeModifier* mod DESERIALIZE_MEMBER(DopplerFactor, _dopplerFactor); DESERIALIZE_MEMBER(Loop, _loop); DESERIALIZE_MEMBER(PlayOnStart, _playOnStart); + DESERIALIZE_MEMBER(StartTime, _startTime); DESERIALIZE_MEMBER(AllowSpatialization, _allowSpatialization); DESERIALIZE(Clip); } @@ -538,7 +546,8 @@ void AudioSource::BeginPlay(SceneBeginData* data) #if USE_EDITOR if (Time::GetGamePaused()) return; -#endif +#endif Play(); + SetTime(GetStartTime()); } } diff --git a/Source/Engine/Audio/AudioSource.h b/Source/Engine/Audio/AudioSource.h index 70cdb4180..5c0c23c03 100644 --- a/Source/Engine/Audio/AudioSource.h +++ b/Source/Engine/Audio/AudioSource.h @@ -52,6 +52,7 @@ private: float _dopplerFactor = 1.0f; bool _loop; bool _playOnStart; + float _startTime; bool _allowSpatialization; bool _clipChanged = false; @@ -148,11 +149,25 @@ public: return _playOnStart; } + /// + /// Determines the time (in seconds) at which the audio clip starts playing if Play On Start is enabled. + /// + API_PROPERTY(Attributes = "EditorOrder(51), DefaultValue(0.0f), Limit(0, float.MaxValue, 0.01f), EditorDisplay(\"Audio Source\", \"Start Time\"), VisibleIf(nameof(PlayOnStart))") + FORCE_INLINE float GetStartTime() const + { + return _startTime; + } + /// /// Determines whether the audio clip should auto play on game start. /// API_PROPERTY() void SetPlayOnStart(bool value); + /// + /// Determines the time (in seconds) at which the audio clip starts playing if Play On Start is enabled. + /// + API_PROPERTY() void SetStartTime(float value); + /// /// Gets the minimum distance at which audio attenuation starts. When the listener is closer to the source than this value, audio is heard at full volume. Once farther away the audio starts attenuating. /// From ee19bca7e4be0e49ead09990165b6d00ed499b98 Mon Sep 17 00:00:00 2001 From: z1dev Date: Fri, 26 Jan 2024 09:07:02 +0100 Subject: [PATCH 34/55] Fixing skipped static and other fields generating "else if" without "if" first. --- .../Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 5e63f489d..1e8bb749f 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -2423,28 +2423,29 @@ namespace Flax.Build.Bindings // Getter for structure field contents.AppendLine(" static void GetField(void* ptr, const String& name, Variant& value)"); contents.AppendLine(" {"); - for (var i = 0; i < structureInfo.Fields.Count; i++) + for (int i = 0, count = 0; i < structureInfo.Fields.Count; i++) { var fieldInfo = structureInfo.Fields[i]; if (fieldInfo.IsReadOnly || fieldInfo.IsStatic || fieldInfo.IsConstexpr || fieldInfo.Access == AccessLevel.Private) continue; - if (i == 0) + if (count == 0) contents.AppendLine($" if (name == TEXT(\"{fieldInfo.Name}\"))"); else contents.AppendLine($" else if (name == TEXT(\"{fieldInfo.Name}\"))"); contents.AppendLine($" value = {GenerateCppWrapperNativeToVariant(buildData, fieldInfo.Type, structureInfo, $"(({structureTypeNameNative}*)ptr)->{fieldInfo.Name}")};"); + count++; } contents.AppendLine(" }").AppendLine(); // Setter for structure field contents.AppendLine(" static void SetField(void* ptr, const String& name, const Variant& value)"); contents.AppendLine(" {"); - for (var i = 0; i < structureInfo.Fields.Count; i++) + for (int i = 0, count = 0; i < structureInfo.Fields.Count; i++) { var fieldInfo = structureInfo.Fields[i]; if (fieldInfo.IsReadOnly || fieldInfo.IsStatic || fieldInfo.IsConstexpr || fieldInfo.Access == AccessLevel.Private) continue; - if (i == 0) + if (count == 0) contents.AppendLine($" if (name == TEXT(\"{fieldInfo.Name}\"))"); else contents.AppendLine($" else if (name == TEXT(\"{fieldInfo.Name}\"))"); @@ -2460,6 +2461,7 @@ namespace Flax.Build.Bindings } else contents.AppendLine($" (({structureTypeNameNative}*)ptr)->{fieldInfo.Name} = {GenerateCppWrapperVariantToNative(buildData, fieldInfo.Type, structureInfo, "value")};"); + count++; } contents.AppendLine(" }"); From 18fc6ebd3971e2f74c87f32c73a23710604a74e0 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 26 Jan 2024 10:38:36 -0600 Subject: [PATCH 35/55] Half windowed size and center on screen if going to windowed mode. --- Source/Engine/Platform/Windows/WindowsWindow.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Windows/WindowsWindow.cpp b/Source/Engine/Platform/Windows/WindowsWindow.cpp index 181077125..fec322bc3 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.cpp +++ b/Source/Engine/Platform/Windows/WindowsWindow.cpp @@ -324,7 +324,19 @@ void WindowsWindow::SetBorderless(bool isBorderless, bool maximized) lStyle |= WS_OVERLAPPED | WS_SYSMENU | WS_BORDER | WS_CAPTION; SetWindowLong(_handle, GWL_STYLE, lStyle); - SetWindowPos(_handle, nullptr, 0, 0, (int)_settings.Size.X, (int)_settings.Size.Y, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + const Float2 clientSize = GetClientSize(); + const Float2 desktopSize = Platform::GetDesktopSize(); + // Move window and half size if it is larger than desktop size + if (clientSize.X >= desktopSize.X && clientSize.Y >= desktopSize.Y) + { + const Float2 halfSize = desktopSize * 0.5f; + const Float2 middlePos = halfSize * 0.5f; + SetWindowPos(_handle, nullptr, (int)middlePos.X, (int)middlePos.Y, (int)halfSize.X, (int)halfSize.Y, SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE); + } + else + { + SetWindowPos(_handle, nullptr, 0, 0, (int)clientSize.X, (int)clientSize.Y, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + } if (maximized) { From 74d348706c41774d4b6cb73a3d81a7eae969425e Mon Sep 17 00:00:00 2001 From: ruan Date: Sat, 27 Jan 2024 14:23:41 -0400 Subject: [PATCH 36/55] Add skeleton mask asset parameter to Blend With Mask anim node. --- Source/Editor/Surface/Archetypes/Animation.cs | 50 ++++++++++++++++++- .../Animations/Graph/AnimGroup.Animation.cpp | 11 ++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index c659b475a..b2494081a 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -28,6 +28,52 @@ namespace FlaxEditor.Surface.Archetypes } } + /// + /// Customized for Blend with Mask node. + /// + public class SkeletonMaskSample : SurfaceNode + { + private AssetSelect _assetSelect; + private Box _assetBox; + + /// + public SkeletonMaskSample(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) + : base(id, context, nodeArch, groupArch) + { + } + + /// + public override void OnSurfaceLoaded(SurfaceNodeActions action) + { + base.OnSurfaceLoaded(action); + + if (Surface != null) + { + _assetSelect = GetChild(); + + // 4 is the id of skeleton mask parameter node. + if (TryGetBox(4, out var box)) + { + _assetBox = box; + _assetSelect.Visible = !_assetBox.HasAnyConnection; + } + } + } + + /// + public override void ConnectionTick(Box box) + { + base.ConnectionTick(box); + + if (_assetBox == null) + return; + if (box.ID != _assetBox.ID) + return; + + _assetSelect.Visible = !box.HasAnyConnection; + } + } + /// /// Customized for the animation sampling nodes /// @@ -552,6 +598,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 11, Title = "Blend with Mask", Description = "Blend animation poses using skeleton mask", + Create = (id, context, arch, groupArch) => new SkeletonMaskSample(id, context, arch, groupArch), Flags = NodeFlags.AnimGraph, Size = new Float2(180, 140), DefaultValues = new object[] @@ -565,7 +612,8 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, "Pose A", true, typeof(void), 1), NodeElementArchetype.Factory.Input(1, "Pose B", true, typeof(void), 2), NodeElementArchetype.Factory.Input(2, "Alpha", true, typeof(float), 3, 0), - NodeElementArchetype.Factory.Asset(0, 70, 1, typeof(SkeletonMask)), + NodeElementArchetype.Factory.Input(3, "Skeleton Mask Asset", true, typeof(SkeletonMask), 4), + NodeElementArchetype.Factory.Asset(0, Surface.Constants.LayoutOffsetY * 4, 1, typeof(SkeletonMask)), } }, new NodeArchetype diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index cdf1ebd84..ab3bb0250 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -1131,6 +1131,17 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu { const float alpha = Math::Saturate((float)tryGetValue(node->GetBox(3), node->Values[0])); auto mask = node->Assets[0].As(); + auto maskAssetBox = node->GetBox(4); // 4 is the id of skeleton mask parameter node. + + // Check if have some mask asset conected with the mask node + if (maskAssetBox->HasConnection()) + { + const Value assetBoxValue = tryGetValue(maskAssetBox, Value::Null); + + // Use the mask conected with this node instead of default mask asset + if (assetBoxValue != Value::Null) + mask = (SkeletonMask *)assetBoxValue.AsAsset; + } // Only A or missing/invalid mask if (Math::NearEqual(alpha, 0.0f, ANIM_GRAPH_BLEND_THRESHOLD) || mask == nullptr || mask->WaitForLoaded()) From af8e1e527f0755401013d7d67fef559671c0d4ef Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Mon, 29 Jan 2024 15:31:12 +0100 Subject: [PATCH 37/55] Update BindingsGenerator.Cpp.cs --- .../Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 5e63f489d..fc053eda7 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1937,7 +1937,7 @@ namespace Flax.Build.Bindings var useCSharp = EngineConfiguration.WithCSharp(buildData.TargetOptions); var hasInterface = classInfo.Interfaces != null && classInfo.Interfaces.Any(x => x.Access == AccessLevel.Public); CppInternalCalls.Clear(); - + if (classInfo.IsAutoSerialization) GenerateCppAutoSerialization(buildData, contents, moduleInfo, classInfo, classTypeNameNative); GenerateCppTypeInternalsStatics?.Invoke(buildData, classInfo, contents); @@ -1955,7 +1955,7 @@ namespace Flax.Build.Bindings var paramsCount = eventInfo.Type.GenericArgs?.Count ?? 0; CppIncludeFiles.Add("Engine/Profiler/ProfilerCPU.h"); var bindPrefix = eventInfo.IsStatic ? classTypeNameNative + "::" : "__obj->"; - + //eventInfo. if (useCSharp) { // C# event invoking wrapper (calls C# event from C++ delegate) @@ -1965,12 +1965,12 @@ namespace Flax.Build.Bindings contents.Append(" "); if (eventInfo.IsStatic) contents.Append("static "); - contents.AppendFormat("void {0}_ManagedWrapper(", eventInfo.Name); + contents.AppendFormat("void {0}_ManagedWrapper(", eventInfo.Name); for (var i = 0; i < paramsCount; i++) { if (i != 0) contents.Append(", "); - contents.Append(eventInfo.Type.GenericArgs[i]).Append(" arg" + i); + contents.Append(eventInfo.Type.GenericArgs[i].GetFullNameNative(buildData,classInfo)).Append(" arg" + i); } contents.Append(')').AppendLine(); contents.Append(" {").AppendLine(); @@ -2058,7 +2058,7 @@ namespace Flax.Build.Bindings { if (i != 0) contents.Append(", "); - contents.Append(eventInfo.Type.GenericArgs[i]); + contents.Append(eventInfo.Type.GenericArgs[i].GetFullNameNative(buildData, classInfo)); } contents.Append(")> f;").AppendLine(); if (eventInfo.IsStatic) @@ -2084,7 +2084,7 @@ namespace Flax.Build.Bindings { if (i != 0) contents.Append(", "); - contents.Append(eventInfo.Type.GenericArgs[i]).Append(" arg" + i); + contents.Append(eventInfo.Type.GenericArgs[i].GetFullNameNative(buildData, classInfo)).Append(" arg" + i); } contents.Append(')').AppendLine(); contents.Append(" {").AppendLine(); @@ -2111,7 +2111,7 @@ namespace Flax.Build.Bindings { if (i != 0) contents.Append(", "); - contents.Append(eventInfo.Type.GenericArgs[i]); + contents.Append(eventInfo.Type.GenericArgs[i].GetFullNameNative(buildData, classInfo)); } contents.Append(")> f;").AppendLine(); if (eventInfo.IsStatic) From dec9fbd74b7418778c36232fbc2207c1949f528d Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 2 Feb 2024 09:18:48 -0600 Subject: [PATCH 38/55] Add hold arrow key in tree to continuously scroll actors --- Source/Editor/GUI/Tree/Tree.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/GUI/Tree/Tree.cs b/Source/Editor/GUI/Tree/Tree.cs index 3a9780ed9..ab0d4adc4 100644 --- a/Source/Editor/GUI/Tree/Tree.cs +++ b/Source/Editor/GUI/Tree/Tree.cs @@ -19,7 +19,7 @@ namespace FlaxEditor.GUI.Tree /// /// The key updates timeout in seconds. /// - public static float KeyUpdateTimeout = 0.12f; + public static float KeyUpdateTimeout = 0.25f; /// /// Delegate for selected tree nodes collection change. @@ -349,8 +349,8 @@ namespace FlaxEditor.GUI.Tree var window = Root; if (_keyUpdateTime >= KeyUpdateTimeout && window is WindowRootControl windowRoot && windowRoot.Window.IsFocused) { - bool keyUpArrow = window.GetKeyDown(KeyboardKeys.ArrowUp); - bool keyDownArrow = window.GetKeyDown(KeyboardKeys.ArrowDown); + bool keyUpArrow = window.GetKey(KeyboardKeys.ArrowUp); + bool keyDownArrow = window.GetKey(KeyboardKeys.ArrowDown); // Check if arrow flags are different if (keyDownArrow != keyUpArrow) From 0793ebc132734b1d8703a1d37d28523b25cabafa Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 10:58:14 +0100 Subject: [PATCH 39/55] Improve #2221 to be more dynamic when clicking arrow --- Source/Editor/GUI/Tree/Tree.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Editor/GUI/Tree/Tree.cs b/Source/Editor/GUI/Tree/Tree.cs index ab0d4adc4..e86e1382a 100644 --- a/Source/Editor/GUI/Tree/Tree.cs +++ b/Source/Editor/GUI/Tree/Tree.cs @@ -113,7 +113,7 @@ namespace FlaxEditor.GUI.Tree AutoFocus = false; _supportMultiSelect = supportMultiSelect; - _keyUpdateTime = KeyUpdateTimeout * 10; + _keyUpdateTime = KeyUpdateTimeout; } internal void OnRightClickInternal(TreeNode node, ref Float2 location) @@ -347,6 +347,8 @@ namespace FlaxEditor.GUI.Tree if (ContainsFocus && node != null && node.AutoFocus) { var window = Root; + if (window.GetKeyDown(KeyboardKeys.ArrowUp) || window.GetKeyDown(KeyboardKeys.ArrowDown)) + _keyUpdateTime = KeyUpdateTimeout; if (_keyUpdateTime >= KeyUpdateTimeout && window is WindowRootControl windowRoot && windowRoot.Window.IsFocused) { bool keyUpArrow = window.GetKey(KeyboardKeys.ArrowUp); From f9448c3b42116cdfec08efa2daf97e85e91d79af Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 10:59:55 +0100 Subject: [PATCH 40/55] Skip calling `SetTime` api if start time is unused #2203 --- Source/Engine/Audio/AudioSource.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index a474ec0c7..89d1d0a5b 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -546,8 +546,9 @@ void AudioSource::BeginPlay(SceneBeginData* data) #if USE_EDITOR if (Time::GetGamePaused()) return; -#endif +#endif Play(); - SetTime(GetStartTime()); + if (GetStartTime() > 0) + SetTime(GetStartTime()); } } From b2d0afd4eff03565ed4f2b20535165522762668b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 11:09:08 +0100 Subject: [PATCH 41/55] Simplify code in #2202 for script type searching --- .../SourceCodeEditing/CachedTypesCollection.cs | 15 +++++++++++++++ Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 12 +++--------- Source/Editor/Viewport/MainEditorGizmoViewport.cs | 5 +---- Source/Editor/Viewport/PrefabWindowViewport.cs | 7 ++----- Source/Editor/Viewport/ViewportDraggingHelper.cs | 7 ++----- .../Windows/Assets/PrefabWindow.Hierarchy.cs | 14 ++++---------- Source/Editor/Windows/SceneTreeWindow.cs | 14 ++++---------- 7 files changed, 31 insertions(+), 43 deletions(-) diff --git a/Source/Editor/Modules/SourceCodeEditing/CachedTypesCollection.cs b/Source/Editor/Modules/SourceCodeEditing/CachedTypesCollection.cs index a73c3d2ee..648ec74e3 100644 --- a/Source/Editor/Modules/SourceCodeEditing/CachedTypesCollection.cs +++ b/Source/Editor/Modules/SourceCodeEditing/CachedTypesCollection.cs @@ -45,6 +45,21 @@ namespace FlaxEditor.Modules.SourceCodeEditing _checkAssembly = checkAssembly; } + /// + /// Gets the type matching the certain Script. + /// + /// The content item. + /// The type matching that item, or null if not found. + public ScriptType Get(Content.ScriptItem script) + { + foreach (var type in Get()) + { + if (type.ContentItem == script) + return type; + } + return ScriptType.Null; + } + /// /// Gets all the types from the all loaded assemblies (including project scripts and scripts from the plugins). /// diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 64def3eae..bf39a501b 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -693,12 +693,9 @@ namespace FlaxEditor.SceneGraph.GUI for (int i = 0; i < _dragScriptItems.Objects.Count; i++) { var item = _dragScriptItems.Objects[i]; - // Find actors with the same content item and spawn them. - foreach (var actorType in Editor.Instance.CodeEditing.Actors.Get()) + var actorType = Editor.Instance.CodeEditing.Actors.Get(item); + if (actorType != ScriptType.Null) { - if (actorType.ContentItem != item) - continue; - var actor = actorType.CreateInstance() as Actor; if (actor == null) { @@ -768,10 +765,7 @@ namespace FlaxEditor.SceneGraph.GUI private static bool ValidateDragScriptItem(ScriptItem script) { - var actors = Editor.Instance.CodeEditing.Actors.Get(); - if (actors.Any(x => x.ContentItem == script)) - return true; - return false; + return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null; } /// diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index c501def33..ed6bd4915 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -943,10 +943,7 @@ namespace FlaxEditor.Viewport private static bool ValidateDragScriptItem(ScriptItem script) { - var actors = Editor.Instance.CodeEditing.Actors.Get(); - if (actors.Any(x => x.ContentItem == script)) - return true; - return false; + return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null; } /// diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index a46002028..6c1509de8 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -702,13 +702,10 @@ namespace FlaxEditor.Viewport { return true; } - + private static bool ValidateDragScriptItem(ScriptItem script) { - var actors = Editor.Instance.CodeEditing.Actors.Get(); - if (actors.Any(x => x.ContentItem == script)) - return true; - return false; + return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null; } /// diff --git a/Source/Editor/Viewport/ViewportDraggingHelper.cs b/Source/Editor/Viewport/ViewportDraggingHelper.cs index 83c9cfab0..52e52b37f 100644 --- a/Source/Editor/Viewport/ViewportDraggingHelper.cs +++ b/Source/Editor/Viewport/ViewportDraggingHelper.cs @@ -203,12 +203,9 @@ namespace FlaxEditor.Viewport private void Spawn(ScriptItem item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal) { - // Find actors with the same content item and spawn them. - foreach (var actorType in Editor.Instance.CodeEditing.Actors.Get()) + var actorType = Editor.Instance.CodeEditing.Actors.Get(item); + if (actorType != ScriptType.Null) { - if (actorType.ContentItem != item) - continue; - Spawn(actorType, hit, ref location, ref hitLocation, ref hitNormal); } } diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 351dd3f62..9418e7b75 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -85,13 +85,10 @@ namespace FlaxEditor.Windows.Assets { return true; } - + private static bool ValidateDragScriptItem(ScriptItem script) { - var actors = Editor.Instance.CodeEditing.Actors.Get(); - if (actors.Any(x => x.ContentItem == script)) - return true; - return false; + return Editor.Instance.CodeEditing.Actors.Get(script); } /// @@ -185,12 +182,9 @@ namespace FlaxEditor.Windows.Assets for (int i = 0; i < _dragScriptItems.Objects.Count; i++) { var item = _dragScriptItems.Objects[i]; - // Find actors with the same content item and spawn them. - foreach (var actorType in Editor.Instance.CodeEditing.Actors.Get()) + var actorType = Editor.Instance.CodeEditing.Actors.Get(item); + if (actorType != ScriptType.Null) { - if (actorType.ContentItem != item) - continue; - var actor = actorType.CreateInstance() as Actor; if (actor == null) { diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 9385fb781..97bf85b35 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -274,13 +274,10 @@ namespace FlaxEditor.Windows { return true; } - + private static bool ValidateDragScriptItem(ScriptItem script) { - var actors = Editor.Instance.CodeEditing.Actors.Get(); - if (actors.Any(x => x.ContentItem == script)) - return true; - return false; + return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null; } /// @@ -468,12 +465,9 @@ namespace FlaxEditor.Windows for (int i = 0; i < _dragScriptItems.Objects.Count; i++) { var item = _dragScriptItems.Objects[i]; - // Find actors with the same content item and spawn them. - foreach (var actorType in Editor.Instance.CodeEditing.Actors.Get()) + var actorType = Editor.Instance.CodeEditing.Actors.Get(item); + if (actorType != ScriptType.Null) { - if (actorType.ContentItem != item) - continue; - var actor = actorType.CreateInstance() as Actor; if (actor == null) { From b2621ff799bae12c6022ea62136a080d44abbd47 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 11:13:00 +0100 Subject: [PATCH 42/55] Codestyle fix for #2214 --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 17e426053..4e632014a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1937,7 +1937,7 @@ namespace Flax.Build.Bindings var useCSharp = EngineConfiguration.WithCSharp(buildData.TargetOptions); var hasInterface = classInfo.Interfaces != null && classInfo.Interfaces.Any(x => x.Access == AccessLevel.Public); CppInternalCalls.Clear(); - + if (classInfo.IsAutoSerialization) GenerateCppAutoSerialization(buildData, contents, moduleInfo, classInfo, classTypeNameNative); GenerateCppTypeInternalsStatics?.Invoke(buildData, classInfo, contents); @@ -1955,7 +1955,7 @@ namespace Flax.Build.Bindings var paramsCount = eventInfo.Type.GenericArgs?.Count ?? 0; CppIncludeFiles.Add("Engine/Profiler/ProfilerCPU.h"); var bindPrefix = eventInfo.IsStatic ? classTypeNameNative + "::" : "__obj->"; - //eventInfo. + if (useCSharp) { // C# event invoking wrapper (calls C# event from C++ delegate) @@ -1965,12 +1965,12 @@ namespace Flax.Build.Bindings contents.Append(" "); if (eventInfo.IsStatic) contents.Append("static "); - contents.AppendFormat("void {0}_ManagedWrapper(", eventInfo.Name); + contents.AppendFormat("void {0}_ManagedWrapper(", eventInfo.Name); for (var i = 0; i < paramsCount; i++) { if (i != 0) contents.Append(", "); - contents.Append(eventInfo.Type.GenericArgs[i].GetFullNameNative(buildData,classInfo)).Append(" arg" + i); + contents.Append(eventInfo.Type.GenericArgs[i].GetFullNameNative(buildData, classInfo)).Append(" arg" + i); } contents.Append(')').AppendLine(); contents.Append(" {").AppendLine(); From 479a917c5908095474d8700a0da6f02e492ebe38 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 11:42:55 +0100 Subject: [PATCH 43/55] Tweak #2186 --- Source/Editor/Windows/PluginsWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index fcacf47ad..2c96dc9a9 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -95,7 +95,7 @@ namespace FlaxEditor.Windows Bounds = new Rectangle(nameLabel.X, tmp1, nameLabel.Width, Height - tmp1 - margin), }; - var xOffset = nameLabel.X + nameLabel.Width; + var xOffset = nameLabel.Width; string versionString = string.Empty; if (desc.IsAlpha) versionString = "ALPHA "; From 6d757946c5c887649f50fdcf7f7f60b9abe7cdef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 11:49:12 +0100 Subject: [PATCH 44/55] Fix crash when adding physics scene with auto simulation #2180 --- Source/Engine/Physics/Physics.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/Physics.cpp b/Source/Engine/Physics/Physics.cpp index 7087b2e70..5d9b218ec 100644 --- a/Source/Engine/Physics/Physics.cpp +++ b/Source/Engine/Physics/Physics.cpp @@ -203,8 +203,11 @@ void Physics::Simulate(float dt) void Physics::CollectResults() { - if (DefaultScene) - DefaultScene->CollectResults(); + for (PhysicsScene* scene : Scenes) + { + if (scene->GetAutoSimulation()) + scene->CollectResults(); + } } bool Physics::IsDuringSimulation() From 40632a4a881d948a3f5db2f8840eb8a4223c7626 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 12:10:05 +0100 Subject: [PATCH 45/55] Fix missing default value for new Visject method parameter if method uses Vector param #2204 --- Source/Editor/Surface/Archetypes/Function.cs | 17 ++++++++++++++--- Source/Editor/Surface/SurfaceUtils.cs | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index 53950dad2..e8b92d121 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -763,6 +763,17 @@ namespace FlaxEditor.Surface.Archetypes public string Name; public ScriptType Type; public bool IsOut; + + public override string ToString() + { + var sb = new StringBuilder(); + if (IsOut) + sb.Append("out "); + sb.Append(Type.ToString()); + sb.Append(" "); + sb.Append(Name); + return sb.ToString(); + } } private struct SignatureInfo @@ -892,7 +903,7 @@ namespace FlaxEditor.Surface.Archetypes { ref var param = ref signature.Params[i]; ref var paramMember = ref memberParameters[i]; - if (param.Type != paramMember.Type || param.IsOut != paramMember.IsOut) + if (!SurfaceUtils.AreScriptTypesEqual(param.Type, paramMember.Type) || param.IsOut != paramMember.IsOut) { // Special case: param.Type is serialized as just a type while paramMember.Type might be a reference for output parameters (eg. `out Int32` vs `out Int32&`) var paramMemberTypeName = paramMember.Type.TypeName; @@ -1660,7 +1671,7 @@ namespace FlaxEditor.Surface.Archetypes SaveSignature(); // Check if return type has been changed - if (_signature.ReturnType != prevReturnType) + if (!SurfaceUtils.AreScriptTypesEqual(_signature.ReturnType, prevReturnType)) { // Update all return nodes used by this function to match the new type var usedNodes = DepthFirstTraversal(false); @@ -2158,7 +2169,7 @@ namespace FlaxEditor.Surface.Archetypes return false; for (int i = 0; i < _signature.Length; i++) { - if (_signature[i].Type != sig.Parameters[i].Type) + if (!SurfaceUtils.AreScriptTypesEqual(_signature[i].Type, sig.Parameters[i].Type)) return false; } return true; diff --git a/Source/Editor/Surface/SurfaceUtils.cs b/Source/Editor/Surface/SurfaceUtils.cs index 5f4c3ef07..2c8448e92 100644 --- a/Source/Editor/Surface/SurfaceUtils.cs +++ b/Source/Editor/Surface/SurfaceUtils.cs @@ -532,5 +532,24 @@ namespace FlaxEditor.Surface value = new Double4(i); return value; } + + private static bool AreScriptTypesEqualInner(ScriptType left, ScriptType right) + { + // Special case for Vector types that use typedefs and might overlap + if (left.Type == typeof(Vector2) && (right.Type == typeof(Float2) || right.Type == typeof(Double2))) + return true; + if (left.Type == typeof(Vector3) && (right.Type == typeof(Float3) || right.Type == typeof(Double3))) + return true; + if (left.Type == typeof(Vector4) && (right.Type == typeof(Float4) || right.Type == typeof(Double4))) + return true; + return false; + } + + internal static bool AreScriptTypesEqual(ScriptType left, ScriptType right) + { + if (left == right) + return true; + return AreScriptTypesEqualInner(left, right) || AreScriptTypesEqualInner(right, left); + } } } From d33ff4306f2e0baa84a99ce9ecb387ee67796f1b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 14:49:44 +0100 Subject: [PATCH 46/55] Fix sprite atlas limit on `4096` #2218 --- Source/Editor/Windows/Assets/SpriteAtlasWindow.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs b/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs index 7d09620d5..a564144c6 100644 --- a/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs +++ b/Source/Editor/Windows/Assets/SpriteAtlasWindow.cs @@ -83,14 +83,14 @@ namespace FlaxEditor.Windows.Assets set => Sprite.Name = value; } - [EditorOrder(1), Limit(-4096, 4096)] + [EditorOrder(1)] public Float2 Location { get => Sprite.Location; set => Sprite.Location = value; } - [EditorOrder(3), Limit(0, 4096)] + [EditorOrder(3), Limit(0)] public Float2 Size { get => Sprite.Size; From 465c14c04a2e814a4831a9950d803a084cc1dec8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 15:06:29 +0100 Subject: [PATCH 47/55] Remove leftover log --- Source/Editor/Viewport/ViewportDraggingHelper.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Editor/Viewport/ViewportDraggingHelper.cs b/Source/Editor/Viewport/ViewportDraggingHelper.cs index 52e52b37f..bf4ec7979 100644 --- a/Source/Editor/Viewport/ViewportDraggingHelper.cs +++ b/Source/Editor/Viewport/ViewportDraggingHelper.cs @@ -110,7 +110,6 @@ namespace FlaxEditor.Viewport foreach (var scripItem in _dragScriptItem.Objects) Spawn(scripItem, hit, ref location, ref hitLocation, ref hitNormal); } - Debug.Log("Hit"); OnDragDrop(new DragDropEventArgs { Hit = hit, HitLocation = hitLocation }); return result; From 873be6ac178d9ae019a4dbab728f166e154c52f3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 15:41:35 +0100 Subject: [PATCH 48/55] Convert `Matrix3x3` into normal type --- Source/Engine/Core/Math/Matrix3x3.cs | 12 ++---------- Source/Engine/Core/Math/Matrix3x3.h | 3 ++- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Core/Math/Matrix3x3.cs b/Source/Engine/Core/Math/Matrix3x3.cs index f4487bb1e..44af4542b 100644 --- a/Source/Engine/Core/Math/Matrix3x3.cs +++ b/Source/Engine/Core/Math/Matrix3x3.cs @@ -56,13 +56,7 @@ using System.Runtime.InteropServices; namespace FlaxEngine { - /// - /// Represents a 3x3 Matrix ( contains only Scale and Rotation ). - /// - [Serializable] - [StructLayout(LayoutKind.Sequential, Pack = 4)] - // ReSharper disable once InconsistentNaming - public struct Matrix3x3 : IEquatable, IFormattable + partial struct Matrix3x3 : IEquatable, IFormattable { /// /// The size of the type, in bytes. @@ -135,9 +129,7 @@ namespace FlaxEngine /// The value that will be assigned to all components. public Matrix3x3(float value) { - M11 = M12 = M13 = - M21 = M22 = M23 = - M31 = M32 = M33 = value; + M11 = M12 = M13 = M21 = M22 = M23 = M31 = M32 = M33 = value; } /// diff --git a/Source/Engine/Core/Math/Matrix3x3.h b/Source/Engine/Core/Math/Matrix3x3.h index 344f68455..0680e7735 100644 --- a/Source/Engine/Core/Math/Matrix3x3.h +++ b/Source/Engine/Core/Math/Matrix3x3.h @@ -9,8 +9,9 @@ /// /// Represents a 3x3 mathematical matrix. /// -API_STRUCT(InBuild) struct FLAXENGINE_API Matrix3x3 +API_STRUCT() struct FLAXENGINE_API Matrix3x3 { + DECLARE_SCRIPTING_TYPE_MINIMAL(Matrix3x3); public: union { From 5a50ec592f3705deeeec4c5e2a71c4022b10c48f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 16:01:49 +0100 Subject: [PATCH 49/55] Add clickable parsing errors in build tool --- .../Bindings/BindingsGenerator.Parsing.cs | 69 +++++++++++-------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index 843bb60fa..0b71af086 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -47,6 +47,20 @@ namespace Flax.Build.Bindings } } + private class ParseException : Exception + { + public ParseException(ref ParsingContext context, string msg) + : base(GetParseErrorLocation(ref context, msg)) + { + } + } + + private static string GetParseErrorLocation(ref ParsingContext context, string msg) + { + // Make it a link clickable in Visual Studio build output + return $"{context.File.Name}({context.Tokenizer.CurrentLine}): {msg}"; + } + private static string[] ParseComment(ref ParsingContext context) { if (context.StringCache == null) @@ -180,7 +194,7 @@ namespace Flax.Build.Bindings case TokenType.RightParent: parameters.Add(tag); return parameters; - default: throw new Exception($"Expected right parent or next argument, but got {token.Type}."); + default: throw new ParseException(ref context, $"Expected right parent or next argument, but got {token.Type}."); } } } @@ -302,7 +316,7 @@ namespace Flax.Build.Bindings if (context.PreprocessorDefines.TryGetValue(length, out var define)) length = define; if (!int.TryParse(length, out type.ArraySize)) - throw new Exception($"Failed to parse the field {entry} array size '{length}'"); + throw new ParseException(ref context, $"Failed to parse the field {entry} array size '{length}'"); } private static List ParseFunctionParameters(ref ParsingContext context) @@ -354,7 +368,7 @@ namespace Flax.Build.Bindings if (valid) break; var location = "function parameter"; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {location} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{location}'")); break; } } @@ -483,8 +497,7 @@ namespace Flax.Build.Bindings desc.Inheritance = new List(); desc.Inheritance.Add(inheritType); token = context.Tokenizer.NextToken(); - while (token.Type == TokenType.CommentSingleLine - || token.Type == TokenType.CommentMultiLine) + while (token.Type == TokenType.CommentSingleLine || token.Type == TokenType.CommentMultiLine) { token = context.Tokenizer.NextToken(); } @@ -563,7 +576,7 @@ namespace Flax.Build.Bindings // Read 'class' keyword var token = context.Tokenizer.NextToken(); if (token.Value != "class") - throw new Exception($"Invalid {ApiTokens.Class} usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Class} usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); // Read specifiers while (true) @@ -644,7 +657,7 @@ namespace Flax.Build.Bindings ParseTypeTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -672,7 +685,7 @@ namespace Flax.Build.Bindings // Read 'class' keyword var token = context.Tokenizer.NextToken(); if (token.Value != "class") - throw new Exception($"Invalid {ApiTokens.Interface} usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Interface} usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); // Read specifiers while (true) @@ -735,7 +748,7 @@ namespace Flax.Build.Bindings ParseTypeTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -815,13 +828,13 @@ namespace Flax.Build.Bindings { case "const": if (desc.IsConst) - throw new Exception($"Invalid double 'const' specifier in function {desc.Name} at line {context.Tokenizer.CurrentLine}."); + throw new ParseException(ref context, $"Invalid double 'const' specifier in function {desc.Name}"); desc.IsConst = true; break; case "override": desc.IsVirtual = true; break; - default: throw new Exception($"Unknown identifier '{token.Value}' in function {desc.Name} at line {context.Tokenizer.CurrentLine}."); + default: throw new ParseException(ref context, $"Unknown identifier '{token.Value}' in function {desc.Name}"); } } else if (token.Type == TokenType.LeftCurlyBrace) @@ -875,7 +888,7 @@ namespace Flax.Build.Bindings ParseMemberTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -892,11 +905,11 @@ namespace Flax.Build.Bindings var classInfo = context.ScopeInfo as ClassInfo; if (classInfo == null) - throw new Exception($"Found property {propertyName} outside the class at line {context.Tokenizer.CurrentLine}."); + throw new ParseException(ref context, $"Found property {propertyName} outside the class"); var isGetter = !functionInfo.ReturnType.IsVoid; if (!isGetter && functionInfo.Parameters.Count == 0) - throw new Exception($"Property {propertyName} setter method in class {classInfo.Name} has to have value parameter to set (line {context.Tokenizer.CurrentLine})."); + throw new ParseException(ref context, $"Property {propertyName} setter method in class {classInfo.Name} has to have value parameter to set (line {context.Tokenizer.CurrentLine})."); var propertyType = isGetter ? functionInfo.ReturnType : functionInfo.Parameters[0].Type; var propertyInfo = classInfo.Properties.FirstOrDefault(x => x.Name == propertyName); @@ -917,7 +930,7 @@ namespace Flax.Build.Bindings else { if (propertyInfo.IsStatic != functionInfo.IsStatic) - throw new Exception($"Property {propertyName} in class {classInfo.Name} has to have both getter and setter methods static or non-static (line {context.Tokenizer.CurrentLine})."); + throw new ParseException(ref context, $"Property {propertyName} in class {classInfo.Name} has to have both getter and setter methods static or non-static (line {context.Tokenizer.CurrentLine})."); } if (functionInfo.Tags != null) { @@ -934,9 +947,9 @@ namespace Flax.Build.Bindings } if (isGetter && propertyInfo.Getter != null) - throw new Exception($"Property {propertyName} in class {classInfo.Name} cannot have multiple getter method (line {context.Tokenizer.CurrentLine}). Getter methods of properties must return value, while setters take this as a parameter."); + throw new ParseException(ref context, $"Property {propertyName} in class {classInfo.Name} cannot have multiple getter method (line {context.Tokenizer.CurrentLine}). Getter methods of properties must return value, while setters take this as a parameter."); if (!isGetter && propertyInfo.Setter != null) - throw new Exception($"Property {propertyName} in class {classInfo.Name} cannot have multiple setter method (line {context.Tokenizer.CurrentLine}). Getter methods of properties must return value, while setters take this as a parameter."); + throw new ParseException(ref context, $"Property {propertyName} in class {classInfo.Name} cannot have multiple setter method (line {context.Tokenizer.CurrentLine}). Getter methods of properties must return value, while setters take this as a parameter."); if (isGetter) propertyInfo.Getter = functionInfo; @@ -963,7 +976,7 @@ namespace Flax.Build.Bindings return propertyInfo; if (getterType.Type == "Array" && setterType.Type == "Span" && getterType.GenericArgs?.Count == 1 && setterType.GenericArgs?.Count == 1 && getterType.GenericArgs[0].Equals(setterType.GenericArgs[0])) return propertyInfo; - throw new Exception($"Property {propertyName} in class {classInfo.Name} (line {context.Tokenizer.CurrentLine}) has mismatching getter return type ({getterType}) and setter parameter type ({setterType}). Both getter and setter methods must use the same value type used for property."); + throw new ParseException(ref context, $"Property {propertyName} in class {classInfo.Name} (line {context.Tokenizer.CurrentLine}) has mismatching getter return type ({getterType}) and setter parameter type ({setterType}). Both getter and setter methods must use the same value type used for property."); } if (propertyInfo.Comment != null) @@ -996,7 +1009,7 @@ namespace Flax.Build.Bindings // Read 'enum' or `enum class` keywords var token = context.Tokenizer.NextToken(); if (token.Value != "enum") - throw new Exception($"Invalid {ApiTokens.Enum} usage at line {context.Tokenizer.CurrentLine} (expected 'enum' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Enum} usage (expected 'enum' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); token = context.Tokenizer.NextToken(); if (token.Value != "class") context.Tokenizer.PreviousToken(); @@ -1079,7 +1092,7 @@ namespace Flax.Build.Bindings entry.Attributes = tag.Value; break; default: - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name + " enum entry"} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -1153,7 +1166,7 @@ namespace Flax.Build.Bindings ParseTypeTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -1176,7 +1189,7 @@ namespace Flax.Build.Bindings // Read 'struct' keyword var token = context.Tokenizer.NextToken(); if (token.Value != "struct") - throw new Exception($"Invalid {ApiTokens.Struct} usage (expected 'struct' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Struct} usage (expected 'struct' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); // Read name desc.Name = desc.NativeName = ParseName(ref context); @@ -1233,7 +1246,7 @@ namespace Flax.Build.Bindings ParseTypeTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -1366,7 +1379,7 @@ namespace Flax.Build.Bindings ParseMemberTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -1396,7 +1409,7 @@ namespace Flax.Build.Bindings if (desc.Type.Type == "Action") desc.Type = new TypeInfo { Type = "Delegate", GenericArgs = new List() }; else if (desc.Type.Type != "Delegate") - throw new Exception($"Invalid {ApiTokens.Event} type. Only Action and Delegate<> types are supported. '{desc.Type}' used on event at line {context.Tokenizer.CurrentLine}."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Event} type. Only Action and Delegate<> types are supported. '{desc.Type}' used on event."); // Read name desc.Name = ParseName(ref context); @@ -1438,7 +1451,7 @@ namespace Flax.Build.Bindings ParseMemberTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -1474,7 +1487,7 @@ namespace Flax.Build.Bindings // Read 'typedef' keyword var token = context.Tokenizer.NextToken(); if (token.Value != "typedef") - throw new Exception($"Invalid {ApiTokens.Typedef} usage (expected 'typedef' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Typedef} usage (expected 'typedef' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); // Read type definition desc.Type = ParseType(ref context); @@ -1513,7 +1526,7 @@ namespace Flax.Build.Bindings ParseTypeTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } From c0e3b2788029483093893825073a5918ab759f70 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 16:17:43 +0100 Subject: [PATCH 50/55] Codestyle fix #2211 --- Source/Editor/Surface/Archetypes/Animation.cs | 7 +++---- Source/Engine/Animations/Graph/AnimGroup.Animation.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index b2494081a..cbe9c3d85 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -69,7 +69,6 @@ namespace FlaxEditor.Surface.Archetypes return; if (box.ID != _assetBox.ID) return; - _assetSelect.Visible = !box.HasAnyConnection; } } @@ -122,7 +121,7 @@ namespace FlaxEditor.Surface.Archetypes Title = _assetBox.HasAnyConnection || asset == null ? "Animation" : asset.ShortName; else Title = asset?.ShortName ?? "Animation"; - + var style = Style.Current; Resize(Mathf.Max(230, style.FontLarge.MeasureText(Title).X + 30), 160); } @@ -570,7 +569,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 10, Title = "Blend Additive", - Description = + Description = "Blend animation poses (with additive mode)" + "\n" + "\nNote: " + @@ -598,7 +597,7 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 11, Title = "Blend with Mask", Description = "Blend animation poses using skeleton mask", - Create = (id, context, arch, groupArch) => new SkeletonMaskSample(id, context, arch, groupArch), + Create = (id, context, arch, groupArch) => new SkeletonMaskSample(id, context, arch, groupArch), Flags = NodeFlags.AnimGraph, Size = new Float2(180, 140), DefaultValues = new object[] diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index ab3bb0250..d3b61c951 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -228,7 +228,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* trace.Value = animPos; trace.NodeId = node->ID; } - + // Evaluate nested animations bool hasNested = false; if (anim->NestedAnims.Count() != 0) @@ -1133,14 +1133,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu auto mask = node->Assets[0].As(); auto maskAssetBox = node->GetBox(4); // 4 is the id of skeleton mask parameter node. - // Check if have some mask asset conected with the mask node + // Check if have some mask asset connected with the mask node if (maskAssetBox->HasConnection()) { const Value assetBoxValue = tryGetValue(maskAssetBox, Value::Null); - // Use the mask conected with this node instead of default mask asset + // Use the mask connected with this node instead of default mask asset if (assetBoxValue != Value::Null) - mask = (SkeletonMask *)assetBoxValue.AsAsset; + mask = (SkeletonMask*)assetBoxValue.AsAsset; } // Only A or missing/invalid mask From 6954a488eaa35bcb568194a8007462ae6bd57de4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 16:38:44 +0100 Subject: [PATCH 51/55] Codestyle fixes --- .../CustomEditors/Editors/CollectionEditor.cs | 7 +-- Source/Editor/Tools/Terrain/Sculpt/Mode.cs | 14 +++--- .../Editor/Tools/Terrain/Sculpt/SmoothMode.cs | 45 ++++--------------- 3 files changed, 20 insertions(+), 46 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index 526c91d20..00322fc81 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -55,11 +55,11 @@ namespace FlaxEditor.CustomEditors.Editors private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkedEditor) { menu.ItemsContainer.RemoveChildren(); - + menu.AddButton("Copy", linkedEditor.Copy); var paste = menu.AddButton("Paste", linkedEditor.Paste); paste.Enabled = linkedEditor.CanPaste; - + menu.AddSeparator(); var moveUpButton = menu.AddButton("Move up", OnMoveUpClicked); moveUpButton.Enabled = Index > 0; @@ -118,7 +118,7 @@ namespace FlaxEditor.CustomEditors.Editors Editor = editor; Index = index; Offsets = new Margin(7, 7, 0, 0); - + MouseButtonRightClicked += OnMouseButtonRightClicked; if (_canReorder) { @@ -173,6 +173,7 @@ namespace FlaxEditor.CustomEditors.Editors /// Determines if value of collection can be null. /// protected bool NotNullItems; + private IntValueBox _sizeBox; private Color _background; private int _elementsCount; diff --git a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs index a92b899e1..cce2f982f 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs @@ -53,11 +53,11 @@ namespace FlaxEditor.Tools.Terrain.Sculpt /// /// Gets all patches that will be affected by the brush /// - /// - /// - /// - /// - public unsafe virtual List GetAffectedPatches(Brush brush, ref Options options, SculptTerrainGizmoMode gizmo, FlaxEngine.Terrain terrain) + /// The brush. + /// The options. + /// The gizmo. + /// The terrain. + public virtual unsafe List GetAffectedPatches(Brush brush, ref Options options, SculptTerrainGizmoMode gizmo, FlaxEngine.Terrain terrain) { List affectedPatches = new(); @@ -155,7 +155,7 @@ namespace FlaxEditor.Tools.Terrain.Sculpt /// The options. /// The gizmo. /// The terrain. - public unsafe void Apply(Brush brush, ref Options options, SculptTerrainGizmoMode gizmo, FlaxEngine.Terrain terrain) + public void Apply(Brush brush, ref Options options, SculptTerrainGizmoMode gizmo, FlaxEngine.Terrain terrain) { var affectedPatches = GetAffectedPatches(brush, ref options, gizmo, terrain); @@ -176,7 +176,7 @@ namespace FlaxEditor.Tools.Terrain.Sculpt /// /// /// - public unsafe virtual void ApplyBrush(SculptTerrainGizmoMode gizmo, List affectedPatches) + public virtual void ApplyBrush(SculptTerrainGizmoMode gizmo, List affectedPatches) { for (int i = 0; i < affectedPatches.Count; i++) { diff --git a/Source/Editor/Tools/Terrain/Sculpt/SmoothMode.cs b/Source/Editor/Tools/Terrain/Sculpt/SmoothMode.cs index 49ccfe0de..387bb555e 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/SmoothMode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/SmoothMode.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using FlaxEngine; -using FlaxEditor.Tools.Terrain.Brushes; using System; namespace FlaxEditor.Tools.Terrain.Sculpt @@ -29,15 +28,9 @@ namespace FlaxEditor.Tools.Terrain.Sculpt var heightmapSize = affectedPatches[0].HeightmapSize; var radius = Mathf.Max(Mathf.CeilToInt(FilterRadius * 0.01f * affectedPatches[0].Brush.Size), 2); - - ///// - /// Calculate bounding coordinates of the total affected area - /// - - + // Calculate bounding coordinates of the total affected area Int2 modifieedAreaMinCoord = Int2.Maximum; Int2 modifiedAreaMaxCoord = Int2.Minimum; - for (int i = 0; i < affectedPatches.Count; i++) { var patch = affectedPatches[i]; @@ -46,27 +39,14 @@ namespace FlaxEditor.Tools.Terrain.Sculpt var br = tl + patch.ModifiedSize; if (tl.X <= modifieedAreaMinCoord.X && tl.Y <= modifieedAreaMinCoord.Y) - { modifieedAreaMinCoord = tl; - } - if (br.X >= modifiedAreaMaxCoord.X && br.Y >= modifiedAreaMaxCoord.Y) - { modifiedAreaMaxCoord = br; - } } - - var totalModifiedSize = modifiedAreaMaxCoord - modifieedAreaMinCoord; - - ///// - /// Build map of heights in affected area - /// - - + // Build map of heights in affected area var modifiedHeights = new float[totalModifiedSize.X * totalModifiedSize.Y]; - for (int i = 0; i < affectedPatches.Count; i++) { var patch = affectedPatches[i]; @@ -75,32 +55,26 @@ namespace FlaxEditor.Tools.Terrain.Sculpt { for (int x = 0; x < patch.ModifiedSize.X; x++) { - // read height from current patch + // Read height from current patch var localCoordX = (x + patch.ModifiedOffset.X); var localCoordY = (z + patch.ModifiedOffset.Y); var coordHeight = patch.SourceHeightMap[(localCoordY * heightmapSize) + localCoordX]; - // calculate the absolute coordinate of the terrain point + // Calculate the absolute coordinate of the terrain point var absoluteCurrentPointCoord = patch.PatchCoord * (heightmapSize - 1) + patch.ModifiedOffset + new Int2(x, z); var currentPointCoordRelativeToModifiedArea = absoluteCurrentPointCoord - modifieedAreaMinCoord; - // store height + // Store height var index = (currentPointCoordRelativeToModifiedArea.Y * totalModifiedSize.X) + currentPointCoordRelativeToModifiedArea.X; modifiedHeights[index] = coordHeight; } } } - - ///// - /// Iterate through modified points and smooth now that we have height information for all necessary points - /// - - + // Iterate through modified points and smooth now that we have height information for all necessary points for (int i = 0; i < affectedPatches.Count; i++) { var patch = affectedPatches[i]; - var brushPosition = patch.Gizmo.CursorPosition; var strength = Mathf.Saturate(patch.Strength); @@ -108,16 +82,16 @@ namespace FlaxEditor.Tools.Terrain.Sculpt { for (int x = 0; x < patch.ModifiedSize.X; x++) { - // read height from current patch + // Read height from current patch var localCoordX = (x + patch.ModifiedOffset.X); var localCoordY = (z + patch.ModifiedOffset.Y); var coordHeight = patch.SourceHeightMap[(localCoordY * heightmapSize) + localCoordX]; - // calculate the absolute coordinate of the terrain point + // Calculate the absolute coordinate of the terrain point var absoluteCurrentPointCoord = patch.PatchCoord * (heightmapSize - 1) + patch.ModifiedOffset + new Int2(x, z); var currentPointCoordRelativeToModifiedArea = absoluteCurrentPointCoord - modifieedAreaMinCoord; - // calculate brush influence at the current position + // Calculate brush influence at the current position var samplePositionLocal = patch.PatchPositionLocal + new Vector3(localCoordX * FlaxEngine.Terrain.UnitsPerVertex, coordHeight, localCoordY * FlaxEngine.Terrain.UnitsPerVertex); Vector3.Transform(ref samplePositionLocal, ref patch.TerrainWorld, out Vector3 samplePositionWorld); var paintAmount = patch.Brush.Sample(ref brushPosition, ref samplePositionWorld) * strength; @@ -149,7 +123,6 @@ namespace FlaxEditor.Tools.Terrain.Sculpt { var coordIndex = (dz * totalModifiedSize.X) + dx; var height = modifiedHeights[coordIndex]; - smoothValue += height; smoothValueSamples++; } From 9da9a122b8d63f35212ebc76c695d3951370638d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 16:59:17 +0100 Subject: [PATCH 52/55] Fix error in Editor --- .../Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs | 2 +- Source/Editor/GUI/AssetPicker.cs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index 568a57794..f9ccf548a 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -76,7 +76,7 @@ namespace FlaxEditor.CustomEditors.Editors private void OnSelectedMaterialChanged() { - if (_isRefreshing) + if (_isRefreshing || _modelInstance == null) return; _isRefreshing = true; var slots = _modelInstance.MaterialSlots; diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 84f58daf1..9e8e0485e 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -75,6 +75,9 @@ namespace FlaxEditor.GUI /// protected virtual void OnSelectedItemChanged() { + if (IsDisposing) + return; + // Update tooltip string tooltip; if (Validator.SelectedItem is AssetItem assetItem) From 5fc768bbbde5ba0c958f4acdd3e33a7f35f0aadf Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 17:10:55 +0100 Subject: [PATCH 53/55] Fix spatial audio playback in OpenAL with Large Worlds enabled #2015 --- Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp index ace8b6591..3bd709259 100644 --- a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp +++ b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp @@ -116,7 +116,7 @@ namespace ALC { AudioBackend::Listener::TransformChanged(listener); - const Vector3 velocity = listener->GetVelocity(); + const Float3 velocity = listener->GetVelocity(); alListener3f(AL_VELOCITY, FLAX_VEL_TO_OAL(velocity)); alListenerf(AL_GAIN, Audio::GetVolume()); } @@ -319,8 +319,6 @@ void AudioBackendOAL::Listener_OnAdd(AudioListener* listener) ALC::RebuildContexts(false); #else AudioBackend::Listener::TransformChanged(listener); - const Vector3 velocity = listener->GetVelocity(); - alListener3f(AL_VELOCITY, FLAX_VEL_TO_OAL(velocity)); alListenerf(AL_GAIN, Audio::GetVolume()); #endif } @@ -336,7 +334,7 @@ void AudioBackendOAL::Listener_VelocityChanged(AudioListener* listener) { ALC_GET_LISTENER_CONTEXT(listener) - const Vector3 velocity = listener->GetVelocity(); + const Float3 velocity = listener->GetVelocity(); alListener3f(AL_VELOCITY, FLAX_VEL_TO_OAL(velocity)); } @@ -344,15 +342,15 @@ void AudioBackendOAL::Listener_TransformChanged(AudioListener* listener) { ALC_GET_LISTENER_CONTEXT(listener) - const Vector3 position = listener->GetPosition(); + const Float3 position = listener->GetPosition(); const Quaternion orientation = listener->GetOrientation(); - const Vector3 flipX(-1, 1, 1); - const Vector3 alOrientation[2] = + const Float3 flipX(-1, 1, 1); + const Float3 alOrientation[2] = { // Forward - orientation * Vector3::Forward * flipX, + orientation * Float3::Forward * flipX, // Up - orientation * Vector3::Up * flipX + orientation * Float3::Up * flipX }; alListenerfv(AL_ORIENTATION, (float*)alOrientation); From 7c9218840d4509ae4f83aeb47ce0810082a0bf1b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 17:27:27 +0100 Subject: [PATCH 54/55] Format code #2149 --- .../Editor/CustomEditors/Editors/BindableButtonEditor.cs | 2 +- .../Editor/CustomEditors/Editors/GamepadButtonEditor.cs | 8 ++++---- Source/Editor/CustomEditors/Editors/KeyboardKeysEditor.cs | 8 ++++---- Source/Editor/CustomEditors/Editors/MouseButtonEditor.cs | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs b/Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs index 2d4bf0df3..f8323bb49 100644 --- a/Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs +++ b/Source/Editor/CustomEditors/Editors/BindableButtonEditor.cs @@ -1,4 +1,4 @@ -using FlaxEditor.GUI; +using FlaxEditor.GUI; using FlaxEngine; using FlaxEngine.GUI; diff --git a/Source/Editor/CustomEditors/Editors/GamepadButtonEditor.cs b/Source/Editor/CustomEditors/Editors/GamepadButtonEditor.cs index 7bae24922..6b0d190c0 100644 --- a/Source/Editor/CustomEditors/Editors/GamepadButtonEditor.cs +++ b/Source/Editor/CustomEditors/Editors/GamepadButtonEditor.cs @@ -1,4 +1,4 @@ -using System; +using System; using FlaxEngine; namespace FlaxEditor.CustomEditors.Editors @@ -15,17 +15,17 @@ namespace FlaxEditor.CustomEditors.Editors public override void Initialize(LayoutElementsContainer layout) { base.Initialize(layout); - FlaxEngine.Scripting.Update += ScriptingOnUpdate; + FlaxEngine.Scripting.Update += OnUpdate; } /// protected override void Deinitialize() { - FlaxEngine.Scripting.Update -= ScriptingOnUpdate; + FlaxEngine.Scripting.Update -= OnUpdate; base.Deinitialize(); } - private void ScriptingOnUpdate() + private void OnUpdate() { if (!IsListeningForInput) return; diff --git a/Source/Editor/CustomEditors/Editors/KeyboardKeysEditor.cs b/Source/Editor/CustomEditors/Editors/KeyboardKeysEditor.cs index 765527b7e..375ea1d96 100644 --- a/Source/Editor/CustomEditors/Editors/KeyboardKeysEditor.cs +++ b/Source/Editor/CustomEditors/Editors/KeyboardKeysEditor.cs @@ -1,4 +1,4 @@ -using FlaxEngine; +using FlaxEngine; namespace FlaxEditor.CustomEditors.Editors { @@ -14,17 +14,17 @@ namespace FlaxEditor.CustomEditors.Editors public override void Initialize(LayoutElementsContainer layout) { base.Initialize(layout); - Window.KeyUp += WindowOnKeyUp; + Window.KeyUp += OnKeyUp; } /// protected override void Deinitialize() { - Window.KeyUp -= WindowOnKeyUp; + Window.KeyUp -= OnKeyUp; base.Deinitialize(); } - private void WindowOnKeyUp(KeyboardKeys key) + private void OnKeyUp(KeyboardKeys key) { if (!IsListeningForInput) return; diff --git a/Source/Editor/CustomEditors/Editors/MouseButtonEditor.cs b/Source/Editor/CustomEditors/Editors/MouseButtonEditor.cs index 7964d3a4d..4e46f6717 100644 --- a/Source/Editor/CustomEditors/Editors/MouseButtonEditor.cs +++ b/Source/Editor/CustomEditors/Editors/MouseButtonEditor.cs @@ -1,4 +1,4 @@ -using FlaxEngine; +using FlaxEngine; namespace FlaxEditor.CustomEditors.Editors { @@ -14,17 +14,17 @@ namespace FlaxEditor.CustomEditors.Editors public override void Initialize(LayoutElementsContainer layout) { base.Initialize(layout); - Window.MouseUp += WindowOnMouseUp; + Window.MouseUp += OnMouseUp; } /// protected override void Deinitialize() { - Window.MouseUp -= WindowOnMouseUp; + Window.MouseUp -= OnMouseUp; base.Deinitialize(); } - private void WindowOnMouseUp(ref Float2 mouse, MouseButton button, ref bool handled) + private void OnMouseUp(ref Float2 mouse, MouseButton button, ref bool handled) { if (!IsListeningForInput) return; From 169024ae478d3b7e6151163bf004d4fa5829dec2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Feb 2024 17:39:43 +0100 Subject: [PATCH 55/55] Fix new asset naming to always validate filename #2212 --- Source/Editor/Windows/ContentWindow.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index d43174cf3..68aa03678 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -799,7 +799,10 @@ namespace FlaxEditor.Windows if (proxy == null) throw new ArgumentNullException(nameof(proxy)); + // Setup name string name = initialName ?? proxy.NewItemName; + if (!proxy.IsFileNameValid(name) || Utilities.Utils.HasInvalidPathChar(name)) + name = proxy.NewItemName; // If the proxy can not be created in the current folder, then navigate to the content folder if (!proxy.CanCreate(CurrentViewFolder))