From ad28a3fdbfb0cd3590e4cc727501a16f9e5a28b9 Mon Sep 17 00:00:00 2001 From: Luke Schneider Date: Wed, 27 Sep 2023 21:54:34 -0500 Subject: [PATCH 01/57] Better light theme (Style) support, and a Default light theme (as a secondary option) 1) Added ForegroundViewport as a new color. It is used in the main game viewport (ViewportWidgetButton), and the viewport for rendering of particles and materials. It is needed because the default foreground in a Light theme is black, but black does not work well in a viewport. A new color seemed appropriate. 2) Fixed the profiler window to use the Foreground color in multiple text elements, instead of Color.White (or no default TitleColor). This includes the Row class, Asset class, SingleChart class, Timeline Class, and more. 3) Added a second theme/Style (DefaultLight) to include with the engine. It uses RGB float values because those were easier to transfer from the saved values that I had created (and they're easier for me to edit if necessary). I tried to emulate how the Default theme is created/loaded/etc as closely as possible. --- Source/Editor/GUI/Row.cs | 2 +- Source/Editor/Options/OptionsModule.cs | 66 ++++++++++++++++++- Source/Editor/Options/ThemeOptions.cs | 5 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 4 +- Source/Editor/Windows/Profiler/Assets.cs | 5 ++ Source/Editor/Windows/Profiler/CPU.cs | 7 ++ Source/Editor/Windows/Profiler/GPU.cs | 7 ++ Source/Editor/Windows/Profiler/MemoryGPU.cs | 4 ++ Source/Editor/Windows/Profiler/Network.cs | 6 ++ Source/Editor/Windows/Profiler/SingleChart.cs | 6 +- Source/Editor/Windows/Profiler/Timeline.cs | 4 +- Source/Engine/Scripting/Scripting.cs | 1 + Source/Engine/UI/GUI/Style.cs | 6 ++ 13 files changed, 111 insertions(+), 12 deletions(-) diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index 8dad8b20d..4526e60ac 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -95,7 +95,7 @@ namespace FlaxEditor.GUI rect.Width -= leftDepthMargin; Render2D.PushClip(rect); - Render2D.DrawText(style.FontMedium, text, rect, Color.White, column.CellAlignment, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, text, rect, Style.Current.Foreground, column.CellAlignment, TextAlignment.Center); Render2D.PopClip(); x += width; diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 68aa11626..da293ec47 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -208,13 +208,20 @@ namespace FlaxEditor.Options // If a non-default style was chosen, switch to that style string styleName = themeOptions.SelectedStyle; - if (styleName != "Default" && themeOptions.Styles.TryGetValue(styleName, out var style) && style != null) + if (styleName != "Default" && styleName != "LightDefault" && themeOptions.Styles.TryGetValue(styleName, out var style) && style != null) { Style.Current = style; } else { - Style.Current = CreateDefaultStyle(); + if (styleName == "LightDefault") + { + Style.Current = CreateLightStyle(); + } + else + { + Style.Current = CreateDefaultStyle(); + } } } @@ -233,6 +240,7 @@ namespace FlaxEditor.Options Foreground = Color.FromBgra(0xFFFFFFFF), ForegroundGrey = Color.FromBgra(0xFFA9A9B3), ForegroundDisabled = Color.FromBgra(0xFF787883), + ForegroundViewport = Color.FromBgra(0xFFFFFFFF), BackgroundHighlighted = Color.FromBgra(0xFF54545C), BorderHighlighted = Color.FromBgra(0xFF6A6A75), BackgroundSelected = Color.FromBgra(0xFF007ACC), @@ -271,6 +279,60 @@ namespace FlaxEditor.Options return style; } + /// + /// Creates the light style (2nd default). + /// + /// The style object. + public Style CreateLightStyle() + { + // Metro Style colors + var options = Options; + var style = new Style + { + Background = new Color(0.92f, 0.92f, 0.92f, 1f), + LightBackground = new Color(0.84f, 0.84f, 0.88f, 1f), + DragWindow = new Color(0.0f, 0.26f, 0.43f, 0.70f), + Foreground = new Color(0.0f, 0.0f, 0.0f, 1f), + ForegroundGrey = new Color(0.30f, 0.30f, 0.31f, 1f), + ForegroundDisabled = new Color(0.45f, 0.45f, 0.49f, 1f), + ForegroundViewport = new Color(1.0f, 1.0f, 1.0f, 1f), + BackgroundHighlighted = new Color(0.59f, 0.59f, 0.64f, 1f), + BorderHighlighted = new Color(0.50f, 0.50f, 0.55f, 1f), + BackgroundSelected = new Color(0.00f, 0.46f, 0.78f, 0.78f), + BorderSelected = new Color(0.11f, 0.57f, 0.88f, 0.65f), + BackgroundNormal = new Color(0.67f, 0.67f, 0.75f, 1f), + BorderNormal = new Color(0.59f, 0.59f, 0.64f, 1f), + TextBoxBackground = new Color(0.75f, 0.75f, 0.81f, 1f), + TextBoxBackgroundSelected = new Color(0.73f, 0.73f, 0.80f, 1f), + CollectionBackgroundColor = new Color(0.25f, 0.25f, 0.25f, 1f), + ProgressNormal = new Color(0.03f, 0.65f, 0.12f, 1f), + + // Fonts + FontTitle = options.Interface.TitleFont.GetFont(), + FontLarge = options.Interface.LargeFont.GetFont(), + FontMedium = options.Interface.MediumFont.GetFont(), + FontSmall = options.Interface.SmallFont.GetFont(), + + // Icons + ArrowDown = Editor.Icons.ArrowDown12, + ArrowRight = Editor.Icons.ArrowRight12, + Search = Editor.Icons.Search12, + Settings = Editor.Icons.Settings12, + Cross = Editor.Icons.Cross12, + CheckBoxIntermediate = Editor.Icons.CheckBoxIntermediate12, + CheckBoxTick = Editor.Icons.CheckBoxTick12, + StatusBarSizeGrip = Editor.Icons.WindowDrag12, + Translate = Editor.Icons.Translate32, + Rotate = Editor.Icons.Rotate32, + Scale = Editor.Icons.Scale32, + Scalar = Editor.Icons.Scalar32, + + SharedTooltip = new Tooltip() + }; + + return style; + } + /// public override void OnInit() { diff --git a/Source/Editor/Options/ThemeOptions.cs b/Source/Editor/Options/ThemeOptions.cs index 243918939..a033b34da 100644 --- a/Source/Editor/Options/ThemeOptions.cs +++ b/Source/Editor/Options/ThemeOptions.cs @@ -63,13 +63,14 @@ namespace FlaxEditor.Options private void ReloadOptions(ComboBox obj) { var themeOptions = (ThemeOptions)ParentEditor.Values[0]; - var options = new string[themeOptions.Styles.Count + 1]; + var options = new string[themeOptions.Styles.Count + 2]; options[0] = "Default"; + options[1] = "LightDefault"; int i = 0; foreach (var styleName in themeOptions.Styles.Keys) { - options[i + 1] = styleName; + options[i + 2] = styleName; i++; } _combobox.ComboBox.SetItems(options); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index 481bc3f1b..544489208 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -112,7 +112,7 @@ namespace FlaxEditor.Viewport.Widgets if (Icon.IsValid) { // Draw icon - Render2D.DrawSprite(Icon, iconRect, style.Foreground); + Render2D.DrawSprite(Icon, iconRect, style.ForegroundViewport); // Update text rectangle textRect.Location.X += iconSize; @@ -120,7 +120,7 @@ namespace FlaxEditor.Viewport.Widgets } // Draw text - Render2D.DrawText(style.FontMedium, _text, textRect, style.Foreground * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _text, textRect, style.ForegroundViewport * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Editor/Windows/Profiler/Assets.cs b/Source/Editor/Windows/Profiler/Assets.cs index e2a65d496..39c5644c8 100644 --- a/Source/Editor/Windows/Profiler/Assets.cs +++ b/Source/Editor/Windows/Profiler/Assets.cs @@ -63,6 +63,7 @@ namespace FlaxEditor.Windows.Profiler // Table var headerColor = Style.Current.LightBackground; + var textColor = Style.Current.Foreground; _table = new Table { Columns = new[] @@ -73,22 +74,26 @@ namespace FlaxEditor.Windows.Profiler CellAlignment = TextAlignment.Near, Title = "Resource", TitleBackgroundColor = headerColor, + TitleColor = textColor, }, new ColumnDefinition { Title = "Type", CellAlignment = TextAlignment.Center, TitleBackgroundColor = headerColor, + TitleColor = textColor, }, new ColumnDefinition { Title = "References", TitleBackgroundColor = headerColor, + TitleColor = textColor, }, new ColumnDefinition { Title = "Memory Usage", TitleBackgroundColor = headerColor, + TitleColor = textColor, FormatValue = v => Utilities.Utils.FormatBytesCount((ulong)v), }, }, diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs index fd4061276..df4d5b59b 100644 --- a/Source/Editor/Windows/Profiler/CPU.cs +++ b/Source/Editor/Windows/Profiler/CPU.cs @@ -93,6 +93,7 @@ namespace FlaxEditor.Windows.Profiler // Table var headerColor = Style.Current.LightBackground; + var textColor = Style.Current.Foreground; _table = new Table { Columns = new[] @@ -103,36 +104,42 @@ namespace FlaxEditor.Windows.Profiler CellAlignment = TextAlignment.Near, Title = "Event", TitleBackgroundColor = headerColor, + TitleColor = textColor, }, new ColumnDefinition { Title = "Total", TitleBackgroundColor = headerColor, FormatValue = FormatCellPercentage, + TitleColor = textColor, }, new ColumnDefinition { Title = "Self", TitleBackgroundColor = headerColor, FormatValue = FormatCellPercentage, + TitleColor = textColor, }, new ColumnDefinition { Title = "Time ms", TitleBackgroundColor = headerColor, FormatValue = FormatCellMs, + TitleColor = textColor, }, new ColumnDefinition { Title = "Self ms", TitleBackgroundColor = headerColor, FormatValue = FormatCellMs, + TitleColor = textColor, }, new ColumnDefinition { Title = "Memory", TitleBackgroundColor = headerColor, FormatValue = FormatCellBytes, + TitleColor = textColor, }, }, Parent = layout, diff --git a/Source/Editor/Windows/Profiler/GPU.cs b/Source/Editor/Windows/Profiler/GPU.cs index 4ed18691a..e6a93df72 100644 --- a/Source/Editor/Windows/Profiler/GPU.cs +++ b/Source/Editor/Windows/Profiler/GPU.cs @@ -64,6 +64,7 @@ namespace FlaxEditor.Windows.Profiler // Table var headerColor = Style.Current.LightBackground; + var textColor = Style.Current.Foreground; _table = new Table { Columns = new[] @@ -74,35 +75,41 @@ namespace FlaxEditor.Windows.Profiler CellAlignment = TextAlignment.Near, Title = "Event", TitleBackgroundColor = headerColor, + TitleColor = textColor, }, new ColumnDefinition { Title = "Total", TitleBackgroundColor = headerColor, + TitleColor = textColor, FormatValue = (x) => ((float)x).ToString("0.0") + '%', }, new ColumnDefinition { Title = "GPU ms", TitleBackgroundColor = headerColor, + TitleColor = textColor, FormatValue = (x) => ((float)x).ToString("0.000"), }, new ColumnDefinition { Title = "Draw Calls", TitleBackgroundColor = headerColor, + TitleColor = textColor, FormatValue = FormatCountLong, }, new ColumnDefinition { Title = "Triangles", TitleBackgroundColor = headerColor, + TitleColor = textColor, FormatValue = FormatCountLong, }, new ColumnDefinition { Title = "Vertices", TitleBackgroundColor = headerColor, + TitleColor = textColor, FormatValue = FormatCountLong, }, }, diff --git a/Source/Editor/Windows/Profiler/MemoryGPU.cs b/Source/Editor/Windows/Profiler/MemoryGPU.cs index 20a7898e0..e894ef0e2 100644 --- a/Source/Editor/Windows/Profiler/MemoryGPU.cs +++ b/Source/Editor/Windows/Profiler/MemoryGPU.cs @@ -64,6 +64,7 @@ namespace FlaxEditor.Windows.Profiler // Table var headerColor = Style.Current.LightBackground; + var textColor = Style.Current.Foreground; _table = new Table { Columns = new[] @@ -74,18 +75,21 @@ namespace FlaxEditor.Windows.Profiler CellAlignment = TextAlignment.Near, Title = "Resource", TitleBackgroundColor = headerColor, + TitleColor = textColor, }, new ColumnDefinition { Title = "Type", CellAlignment = TextAlignment.Center, TitleBackgroundColor = headerColor, + TitleColor = textColor, }, new ColumnDefinition { Title = "Memory Usage", TitleBackgroundColor = headerColor, FormatValue = v => Utilities.Utils.FormatBytesCount((ulong)v), + TitleColor = textColor, }, }, Parent = layout, diff --git a/Source/Editor/Windows/Profiler/Network.cs b/Source/Editor/Windows/Profiler/Network.cs index dbee0e8e7..b29173e76 100644 --- a/Source/Editor/Windows/Profiler/Network.cs +++ b/Source/Editor/Windows/Profiler/Network.cs @@ -205,6 +205,7 @@ namespace FlaxEditor.Windows.Profiler private static Table InitTable(ContainerControl parent, string name) { var headerColor = Style.Current.LightBackground; + var textColor = Style.Current.Foreground; var table = new Table { Columns = new[] @@ -215,28 +216,33 @@ namespace FlaxEditor.Windows.Profiler CellAlignment = TextAlignment.Near, Title = name, TitleBackgroundColor = headerColor, + TitleColor = textColor, }, new ColumnDefinition { Title = "Count", TitleBackgroundColor = headerColor, + TitleColor = textColor, }, new ColumnDefinition { Title = "Data Size", TitleBackgroundColor = headerColor, + TitleColor = textColor, FormatValue = FormatCellBytes, }, new ColumnDefinition { Title = "Message Size", TitleBackgroundColor = headerColor, + TitleColor = textColor, FormatValue = FormatCellBytes, }, new ColumnDefinition { Title = "Receivers", TitleBackgroundColor = headerColor, + TitleColor = textColor, }, }, Splits = new[] diff --git a/Source/Editor/Windows/Profiler/SingleChart.cs b/Source/Editor/Windows/Profiler/SingleChart.cs index e14480bee..29780150a 100644 --- a/Source/Editor/Windows/Profiler/SingleChart.cs +++ b/Source/Editor/Windows/Profiler/SingleChart.cs @@ -105,7 +105,7 @@ namespace FlaxEditor.Windows.Profiler if (_selectedSampleIndex != -1) { float selectedX = Width - (_samples.Count - _selectedSampleIndex - 1) * PointsOffset; - Render2D.DrawLine(new Float2(selectedX, 0), new Float2(selectedX, chartHeight), Color.White, 1.5f); + Render2D.DrawLine(new Float2(selectedX, 0), new Float2(selectedX, chartHeight), Style.Current.Foreground, 1.5f); } int samplesInViewCount = Math.Min((int)(Width / PointsOffset), _samples.Count) - 1; @@ -138,8 +138,8 @@ namespace FlaxEditor.Windows.Profiler var headerRect = new Rectangle(0, chartHeight, Width, TitleHeight); var headerTextRect = new Rectangle(2, chartHeight, Width - 4, TitleHeight); Render2D.FillRectangle(headerRect, style.BackgroundNormal); - Render2D.DrawText(style.FontMedium, Title, headerTextRect, Color.White * 0.8f, TextAlignment.Near, TextAlignment.Center); - Render2D.DrawText(style.FontMedium, _sample, headerTextRect, Color.White, TextAlignment.Far, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Title, headerTextRect, Style.Current.ForegroundGrey, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _sample, headerTextRect, Style.Current.Foreground, TextAlignment.Far, TextAlignment.Center); } private void OnClick(ref Float2 location) diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index a61ca1d05..59a7a0e26 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -90,7 +90,7 @@ namespace FlaxEditor.Windows.Profiler if (_nameLength < bounds.Width + 4) { Render2D.PushClip(bounds); - Render2D.DrawText(style.FontMedium, _name, bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _name, bounds, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center); Render2D.PopClip(); } } @@ -115,7 +115,7 @@ namespace FlaxEditor.Windows.Profiler var style = Style.Current; var rect = new Rectangle(Float2.Zero, Size); Render2D.PushClip(rect); - Render2D.DrawText(style.FontMedium, Name, rect, Color.White, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars); + Render2D.DrawText(style.FontMedium, Name, rect, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars); Render2D.PopClip(); } } diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index 68da64bf7..e2e6b514a 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -266,6 +266,7 @@ namespace FlaxEngine Foreground = Color.FromBgra(0xFFFFFFFF), ForegroundGrey = Color.FromBgra(0xFFA9A9B3), ForegroundDisabled = Color.FromBgra(0xFF787883), + ForegroundViewport = Color.FromBgra(0xFFFFFFFF), BackgroundHighlighted = Color.FromBgra(0xFF54545C), BorderHighlighted = Color.FromBgra(0xFF6A6A75), BackgroundSelected = Color.FromBgra(0xFF007ACC), diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index fac65e22f..5e8b1c57f 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -104,6 +104,12 @@ namespace FlaxEngine.GUI [EditorOrder(110)] public Color ForegroundDisabled; + /// + /// The foreground color in viewports (usually have a dark background) + /// + [EditorOrder(115)] + public Color ForegroundViewport; + /// /// The background highlighted color. /// From b7b8213179ff05edbea38a3385e1af9efe2ccdd4 Mon Sep 17 00:00:00 2001 From: Luke Schneider Date: Fri, 29 Sep 2023 07:43:59 -0500 Subject: [PATCH 02/57] Some additional fixes to light theme support Fixed some issues with light theme support: 1) Icons in content tree nodes (Folder icons) now use the foreground color. I did not find a case where the content tree is used for other icons. 2) The asset picker now uses the Background Normal color (instead of a very transparent dark gray) for the background, and Orange for the text. Did not seem like it warranted adding a new color, and Orange works in both dark and light styles. 3) The platform selector icons are now hard-coded instead of based on the style. This may sound odd, but the icons are colored, so they should always use White as the fully active color. Previously they worked with a dark theme because the Foreground was set to white. 4) Fixed the CollectionBackgroundColor in the light theme being dark gray instead of light gray like it should be. This fixes certain lists of things having a dark background in the light theme. --- Source/Editor/Content/Tree/ContentTreeNode.cs | 1 + Source/Editor/GUI/AssetPicker.cs | 4 ++-- Source/Editor/GUI/PlatformSelector.cs | 7 ++++--- Source/Editor/Options/OptionsModule.cs | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 2f6651b14..0c0fc6a51 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -86,6 +86,7 @@ namespace FlaxEditor.Content Folder.ParentFolder = parent.Folder; Parent = parent; } + IconColor = Style.Current.Foreground; } /// diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 3e5d22eb0..87346d817 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -395,8 +395,8 @@ namespace FlaxEditor.GUI else { // No element selected - Render2D.FillRectangle(iconRect, new Color(0.2f)); - Render2D.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Wheat, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); + Render2D.FillRectangle(iconRect, Style.Current.BackgroundNormal); + Render2D.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); } // Check if drag is over diff --git a/Source/Editor/GUI/PlatformSelector.cs b/Source/Editor/GUI/PlatformSelector.cs index 1e03a9677..a29f62805 100644 --- a/Source/Editor/GUI/PlatformSelector.cs +++ b/Source/Editor/GUI/PlatformSelector.cs @@ -100,9 +100,10 @@ namespace FlaxEditor.GUI AutoResize = true; Offsets = new Margin(0, 0, 0, IconSize); - _mouseOverColor = style.Foreground; - _selectedColor = style.Foreground; - _defaultColor = style.ForegroundGrey; + // Ignoring style on purpose (style would make sense if the icons were white, but they are colored) + _mouseOverColor = new Color(0.8f, 0.8f, 0.8f, 1f); + _selectedColor = Color.White; + _defaultColor = new Color(0.7f, 0.7f, 0.7f, 0.5f); for (int i = 0; i < platforms.Length; i++) { diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index da293ec47..4ed3b02e1 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -304,7 +304,7 @@ namespace FlaxEditor.Options BorderNormal = new Color(0.59f, 0.59f, 0.64f, 1f), TextBoxBackground = new Color(0.75f, 0.75f, 0.81f, 1f), TextBoxBackgroundSelected = new Color(0.73f, 0.73f, 0.80f, 1f), - CollectionBackgroundColor = new Color(0.25f, 0.25f, 0.25f, 1f), + CollectionBackgroundColor = new Color(0.85f, 0.85f, 0.88f, 1f), ProgressNormal = new Color(0.03f, 0.65f, 0.12f, 1f), // Fonts From 70ca1996c5e4a9fc8204a3133baad7d3826fb3bc Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Wed, 4 Oct 2023 10:00:03 +0200 Subject: [PATCH 03/57] added Ratangle Mask and FWidth --- Source/Editor/Surface/Archetypes/Material.cs | 38 +++++++++++++++++++ .../MaterialGenerator.Material.cpp | 19 ++++++++++ 2 files changed, 57 insertions(+) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 1a797c67d..064f0a771 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -882,6 +882,44 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(1, "Inv Size", typeof(Float2), 1), } }, + new NodeArchetype + { + TypeID = 40, + Title = "Ratangle Mask", + Description = "Creates a Ratangle mask", + Flags = NodeFlags.MaterialGraph, + Size = new Float2(150, 100), + ConnectionsHints = ConnectionsHint.Vector, + DefaultValues = new object[] + { + new Float2(0, 0), + new Float2(0.5f, 0.5f), + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Float2), 0), + NodeElementArchetype.Factory.Input(1, "Ratangle", true, typeof(Float2), 1), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), + } + }, + new NodeArchetype + { + TypeID = 41, + Title = "FWidth", + Description = "Creates a Partial Derivatives (fwidth)", + Flags = NodeFlags.MaterialGraph, + Size = new Float2(150, 100), + ConnectionsHints = ConnectionsHint.Vector, + DefaultValues = new object[] + { + 1 + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "value", true, typeof(float), 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), + } + }, }; } } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 8aa300731..b00477fdc 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -519,6 +519,25 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) } break; } + // Ratangle Mask + case 40: + { + const auto uv = tryGetValue(node->GetBox(0), getUVs).AsFloat2(); + const auto ratangle = tryGetValue(node->GetBox(1), node->Values[1]).AsFloat2(); + + auto d = writeLocal(ValueType::Float2, String::Format(TEXT("abs({0} * 2 - 1) - {1}"),uv.Value, ratangle.Value), node); + auto fwidth = writeLocal(ValueType::Float , String::Format(TEXT("abs(ddx({0})) + abs(ddy({0}))"), d.Value), node); + auto d2 = writeLocal(ValueType::Float , String::Format(TEXT("1 - {0} / {1}"), d.Value, fwidth.Value), node); + value = writeLocal(ValueType::Float , String::Format(TEXT("saturate(min({0}.x, {0}.y))"), d2.Value), node); + break; + } + // FWidth + case 41: + { + const auto d = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat(); + value = writeLocal(ValueType::Float, String::Format(TEXT("abs(ddx({0})) + abs(ddy({0}))"), d.Value), node); + break; + } default: break; } From 367eaf2f8966a04628d3dedb67e366c662abb6f2 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Wed, 4 Oct 2023 10:28:23 +0200 Subject: [PATCH 04/57] adjustment to nodes size --- Source/Editor/Surface/Archetypes/Material.cs | 4 ++-- .../Tools/MaterialGenerator/MaterialGenerator.Material.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 064f0a771..a0aa7d29f 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -888,7 +888,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Ratangle Mask", Description = "Creates a Ratangle mask", Flags = NodeFlags.MaterialGraph, - Size = new Float2(150, 100), + Size = new Float2(150, 40), ConnectionsHints = ConnectionsHint.Vector, DefaultValues = new object[] { @@ -908,7 +908,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "FWidth", Description = "Creates a Partial Derivatives (fwidth)", Flags = NodeFlags.MaterialGraph, - Size = new Float2(150, 100), + Size = new Float2(150, 20), ConnectionsHints = ConnectionsHint.Vector, DefaultValues = new object[] { diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index b00477fdc..c6d05ef48 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -527,7 +527,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) auto d = writeLocal(ValueType::Float2, String::Format(TEXT("abs({0} * 2 - 1) - {1}"),uv.Value, ratangle.Value), node); auto fwidth = writeLocal(ValueType::Float , String::Format(TEXT("abs(ddx({0})) + abs(ddy({0}))"), d.Value), node); - auto d2 = writeLocal(ValueType::Float , String::Format(TEXT("1 - {0} / {1}"), d.Value, fwidth.Value), node); + auto d2 = writeLocal(ValueType::Float2 , String::Format(TEXT("1 - {0} / {1}"), d.Value, fwidth.Value), node); value = writeLocal(ValueType::Float , String::Format(TEXT("saturate(min({0}.x, {0}.y))"), d2.Value), node); break; } From d7b9056d943615767523980e47d4bffb07ab8932 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:13:45 +0200 Subject: [PATCH 05/57] added AAStep and resolved stefnotch reviewe --- Source/Editor/Surface/Archetypes/Material.cs | 27 ++++++++++++++++--- .../MaterialGenerator.Material.cpp | 24 ++++++++++++++--- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index a0aa7d29f..8e5329d22 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -885,8 +885,8 @@ namespace FlaxEditor.Surface.Archetypes new NodeArchetype { TypeID = 40, - Title = "Ratangle Mask", - Description = "Creates a Ratangle mask", + Title = "Rectangle Mask", + Description = "Creates a Rectangle mask", Flags = NodeFlags.MaterialGraph, Size = new Float2(150, 40), ConnectionsHints = ConnectionsHint.Vector, @@ -898,7 +898,7 @@ namespace FlaxEditor.Surface.Archetypes Elements = new[] { NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Float2), 0), - NodeElementArchetype.Factory.Input(1, "Ratangle", true, typeof(Float2), 1), + NodeElementArchetype.Factory.Input(1, "Rectangle", true, typeof(Float2), 1), NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), } }, @@ -906,7 +906,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 41, Title = "FWidth", - Description = "Creates a Partial Derivatives (fwidth)", + Description = "Creates a partial derivative (fwidth)", Flags = NodeFlags.MaterialGraph, Size = new Float2(150, 20), ConnectionsHints = ConnectionsHint.Vector, @@ -920,6 +920,25 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), } }, + new NodeArchetype + { + TypeID = 42, + Title = "AAStep", + Description = "Smooth version of step", + Flags = NodeFlags.MaterialGraph, + Size = new Float2(150, 20), + ConnectionsHints = ConnectionsHint.Vector, + DefaultValues = new object[] + { + 1, + 0 + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "value", true, typeof(float), 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), + } + }, }; } } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index c6d05ef48..0c8d0780e 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -523,11 +523,10 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) case 40: { const auto uv = tryGetValue(node->GetBox(0), getUVs).AsFloat2(); - const auto ratangle = tryGetValue(node->GetBox(1), node->Values[1]).AsFloat2(); + const auto rectangle = tryGetValue(node->GetBox(1), node->Values[1]).AsFloat2(); - auto d = writeLocal(ValueType::Float2, String::Format(TEXT("abs({0} * 2 - 1) - {1}"),uv.Value, ratangle.Value), node); - auto fwidth = writeLocal(ValueType::Float , String::Format(TEXT("abs(ddx({0})) + abs(ddy({0}))"), d.Value), node); - auto d2 = writeLocal(ValueType::Float2 , String::Format(TEXT("1 - {0} / {1}"), d.Value, fwidth.Value), node); + auto d = writeLocal(ValueType::Float2, String::Format(TEXT("abs({0} * 2 - 1) - {1}"),uv.Value, rectangle.Value), node); + auto d2 = writeLocal(ValueType::Float2 , String::Format(TEXT("1 - {0} / fwidth({0})"), d.Value), node); value = writeLocal(ValueType::Float , String::Format(TEXT("saturate(min({0}.x, {0}.y))"), d2.Value), node); break; } @@ -538,6 +537,23 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) value = writeLocal(ValueType::Float, String::Format(TEXT("abs(ddx({0})) + abs(ddy({0}))"), d.Value), node); break; } + //AAStep (smooth version of step) + case 42: + { + //source https://www.ronja-tutorials.com/post/046-fwidth/#a-better-step + + const auto compValue = tryGetValue(node->GetBox(0), getUVs).AsFloat(); + const auto gradient = tryGetValue(node->GetBox(1), node->Values[1]).AsFloat(); + + auto change = writeLocal(ValueType::Float, String::Format(TEXT("fwidth({0})"), gradient.Value), node); + + //base the range of the inverse lerp on the change over two pixels + auto lowerEdge = writeLocal(ValueType::Float, String::Format(TEXT("{0} - {1})"), compValue.Value, change.Value), node); + auto upperEdge = writeLocal(ValueType::Float, String::Format(TEXT("{0} + {1})"), compValue.Value, change.Value), node); + + //do the inverse interpolation and saturate it + value = writeLocal(ValueType::Float, String::Format(TEXT("saturate((({0} - {1}) / ({2} - {1})))"), gradient.Value, lowerEdge.Value, upperEdge.Value), node); + } default: break; } From 329910ae0d022445e34ede5fbe20401c683bd7b6 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:18:42 +0200 Subject: [PATCH 06/57] missing input fix --- Source/Editor/Surface/Archetypes/Material.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 8e5329d22..c725b0662 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -926,7 +926,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "AAStep", Description = "Smooth version of step", Flags = NodeFlags.MaterialGraph, - Size = new Float2(150, 20), + Size = new Float2(150, 40), ConnectionsHints = ConnectionsHint.Vector, DefaultValues = new object[] { @@ -936,6 +936,7 @@ namespace FlaxEditor.Surface.Archetypes Elements = new[] { NodeElementArchetype.Factory.Input(0, "value", true, typeof(float), 0), + NodeElementArchetype.Factory.Input(1, "gradient", true, typeof(float), 0), NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), } }, From 1736aaeb6a791005c978d6d57c1bd9f82c0fd53a Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Fri, 6 Oct 2023 22:54:26 +0200 Subject: [PATCH 07/57] Update Source/Editor/Surface/Archetypes/Material.cs Co-authored-by: stefnotch --- Source/Editor/Surface/Archetypes/Material.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index c725b0662..77bdb25c6 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -910,10 +910,6 @@ namespace FlaxEditor.Surface.Archetypes Flags = NodeFlags.MaterialGraph, Size = new Float2(150, 20), ConnectionsHints = ConnectionsHint.Vector, - DefaultValues = new object[] - { - 1 - }, Elements = new[] { NodeElementArchetype.Factory.Input(0, "value", true, typeof(float), 0), From 4e2870e90cdcc0b3c04713eefaf1f4b469ef997b Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Fri, 6 Oct 2023 22:56:16 +0200 Subject: [PATCH 08/57] Update Source/Editor/Surface/Archetypes/Material.cs Co-authored-by: stefnotch --- Source/Editor/Surface/Archetypes/Material.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 77bdb25c6..6fee3a978 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -909,7 +909,9 @@ namespace FlaxEditor.Surface.Archetypes Description = "Creates a partial derivative (fwidth)", Flags = NodeFlags.MaterialGraph, Size = new Float2(150, 20), - ConnectionsHints = ConnectionsHint.Vector, + ConnectionsHints = ConnectionsHint.Numeric, + IndependentBoxes = new[] { 0 }, + DependentBoxes = new[] { 1 }, Elements = new[] { NodeElementArchetype.Factory.Input(0, "value", true, typeof(float), 0), From d7095957d089972be84376a66bd43d981bfe5908 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:07:00 +0200 Subject: [PATCH 09/57] Update Source/Editor/Surface/Archetypes/Material.cs Co-authored-by: stefnotch --- Source/Editor/Surface/Archetypes/Material.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 6fee3a978..ee3270460 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -914,8 +914,8 @@ namespace FlaxEditor.Surface.Archetypes DependentBoxes = new[] { 1 }, Elements = new[] { - NodeElementArchetype.Factory.Input(0, "value", true, typeof(float), 0), - NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), + NodeElementArchetype.Factory.Input(0, "value", true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, null, 1), } }, new NodeArchetype From 004e2ab5e80f8a91a3cafffdfb94208d7da646da Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:07:19 +0200 Subject: [PATCH 10/57] Update Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp Co-authored-by: stefnotch --- .../Tools/MaterialGenerator/MaterialGenerator.Material.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 0c8d0780e..df594a02b 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -533,8 +533,8 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // FWidth case 41: { - const auto d = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat(); - value = writeLocal(ValueType::Float, String::Format(TEXT("abs(ddx({0})) + abs(ddy({0}))"), d.Value), node); + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + value = writeLocal(inValue.Type, String::Format(TEXT("fwidth({0})"), inValue.Value), node); break; } //AAStep (smooth version of step) From 898296125424820576c5fcc5e7bedeb1b8bbb5f4 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:08:50 -0400 Subject: [PATCH 11/57] Add custom material option to Material Preview. --- .../Viewport/Previews/MaterialPreview.cs | 74 ++++++++++++++++--- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Viewport/Previews/MaterialPreview.cs b/Source/Editor/Viewport/Previews/MaterialPreview.cs index 5520d6c4c..f05c8413b 100644 --- a/Source/Editor/Viewport/Previews/MaterialPreview.cs +++ b/Source/Editor/Viewport/Previews/MaterialPreview.cs @@ -7,6 +7,9 @@ using FlaxEngine.GUI; using FlaxEditor.Viewport.Widgets; using FlaxEditor.GUI.ContextMenu; using Object = FlaxEngine.Object; +using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.GUI; +using FlaxEditor.Scripting; namespace FlaxEditor.Viewport.Previews { @@ -49,6 +52,8 @@ namespace FlaxEditor.Viewport.Previews private Image _guiMaterialControl; private readonly MaterialBase[] _postFxMaterialsCache = new MaterialBase[1]; private ContextMenu _modelWidgetButtonMenu; + private AssetPicker _customModelPicker; + private Model _customModel; /// /// Gets or sets the material asset to preview. It can be or . @@ -74,15 +79,72 @@ namespace FlaxEditor.Viewport.Previews get => _selectedModelIndex; set { + if (value == -1) // Using Custom Model + { + return; + } + if (value < 0 || value > Models.Length) throw new ArgumentOutOfRangeException(); + if (_customModelPicker != null) + { + _customModelPicker.SelectedAsset = null; + } _selectedModelIndex = value; _previewModel.Model = FlaxEngine.Content.LoadAsyncInternal("Editor/Primitives/" + Models[value]); _previewModel.Transform = Transforms[value]; } } + // Used to automatically update which entry is checked. + // TODO: Maybe a better system with predicate bool checks could be used? + private void ResetModelContextMenu() + { + _modelWidgetButtonMenu.ItemsContainer.DisposeChildren(); + + // Fill out all models + for (int i = 0; i < Models.Length; i++) + { + var index = i; + var button = _modelWidgetButtonMenu.AddButton(Models[index]); + button.ButtonClicked += _ => SelectedModelIndex = index; + button.Checked = SelectedModelIndex == index && _customModel == null; + button.Tag = index; + } + + _modelWidgetButtonMenu.AddSeparator(); + _customModelPicker = new AssetPicker(new ScriptType(typeof(Model)), Float2.Zero); + + // Label Button + var customModelPickerLabel = _modelWidgetButtonMenu.AddButton("Custom Model:"); + customModelPickerLabel.CloseMenuOnClick = false; + customModelPickerLabel.Checked = _customModel != null; + + // Container button + var customModelPickerButton = _modelWidgetButtonMenu.AddButton(""); + + customModelPickerButton.Height = _customModelPicker.Height + 4; + customModelPickerButton.CloseMenuOnClick = false; + _customModelPicker.Parent = customModelPickerButton; + _customModelPicker.SelectedAsset = _customModel; + _customModelPicker.SelectedItemChanged += () => + { + _customModel = _customModelPicker.SelectedAsset as Model; + if (_customModelPicker.SelectedAsset == null) + { + SelectedModelIndex = 0; + ResetModelContextMenu(); + return; + } + + _previewModel.Model = _customModel; + _previewModel.Transform = Transforms[0]; + SelectedModelIndex = -1; + ResetModelContextMenu(); + }; + } + /// /// Initializes a new instance of the class. /// @@ -107,17 +169,7 @@ namespace FlaxEditor.Viewport.Previews { if (!control.Visible) return; - _modelWidgetButtonMenu.ItemsContainer.DisposeChildren(); - - // Fill out all models - for (int i = 0; i < Models.Length; i++) - { - var index = i; - var button = _modelWidgetButtonMenu.AddButton(Models[index]); - button.ButtonClicked += _ => SelectedModelIndex = index; - button.Checked = SelectedModelIndex == index; - button.Tag = index; - } + ResetModelContextMenu(); }; new ViewportWidgetButton("Model", SpriteHandle.Invalid, _modelWidgetButtonMenu) { From b2db1330c04c8fb44c70041bd6bc5d597bb8ab57 Mon Sep 17 00:00:00 2001 From: "Mr. Capybara" Date: Tue, 24 Oct 2023 21:45:00 -0400 Subject: [PATCH 12/57] copy old control data to new control when set UIControl type --- .../Dedicated/UIControlEditor.cs | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 5215e23a4..296560507 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -695,7 +695,41 @@ namespace FlaxEditor.CustomEditors.Dedicated private void SetType(ref ScriptType controlType, UIControl uiControl) { string previousName = uiControl.Control?.GetType().Name ?? nameof(UIControl); - uiControl.Control = (Control)controlType.CreateInstance(); + + var oldControlType = (Control)uiControl.Control; + var newControlType = (Control)controlType.CreateInstance(); + + // copy old control data to new control + if (oldControlType != null) + { + newControlType.Visible = oldControlType.Visible; + newControlType.Enabled = oldControlType.Enabled; + newControlType.AutoFocus = oldControlType.AutoFocus; + + newControlType.AnchorMin = oldControlType.AnchorMin; + newControlType.AnchorMax = oldControlType.AnchorMax; + newControlType.Offsets = oldControlType.Offsets; + + newControlType.LocalLocation = oldControlType.LocalLocation; + newControlType.Scale = oldControlType.Scale; + newControlType.Bounds = oldControlType.Bounds; + newControlType.Width = oldControlType.Width; + newControlType.Height = oldControlType.Height; + newControlType.Center = oldControlType.Center; + newControlType.PivotRelative = oldControlType.PivotRelative; + + newControlType.Pivot = oldControlType.Pivot; + newControlType.Shear = oldControlType.Shear; + newControlType.Rotation = oldControlType.Rotation; + } + if (oldControlType is ContainerControl oldContainer && newControlType is ContainerControl newContainer) + { + newContainer.CullChildren = oldContainer.CullChildren; + newContainer.ClipChildren = oldContainer.ClipChildren; + } + + uiControl.Control = newControlType; + if (uiControl.Name.StartsWith(previousName)) { string newName = controlType.Name + uiControl.Name.Substring(previousName.Length); From 137c82a38748666cfc954e91e4acb2ed2ac3e549 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Wed, 25 Oct 2023 06:21:19 +0200 Subject: [PATCH 13/57] Update Material.cs --- Source/Editor/Surface/Archetypes/Material.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index ee3270460..11237481d 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -934,7 +934,7 @@ namespace FlaxEditor.Surface.Archetypes Elements = new[] { NodeElementArchetype.Factory.Input(0, "value", true, typeof(float), 0), - NodeElementArchetype.Factory.Input(1, "gradient", true, typeof(float), 0), + NodeElementArchetype.Factory.Input(1, "gradient", true, typeof(float), 1), NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), } }, From 590f3f7493f8fff78e83256f43fef417540035fa Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 28 Oct 2023 10:29:39 -0500 Subject: [PATCH 14/57] Fix Dropdown scaling with CanvasScalar. Add limiting number of items to show in the dropdown. --- Source/Engine/UI/GUI/Common/Dropdown.cs | 57 ++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index aea4e173a..bf2dbc0ef 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -22,6 +22,11 @@ namespace FlaxEngine.GUI /// Occurs when popup lost focus. /// public Action LostFocus; + + /// + /// The selected control. Used to scroll to the control on popup creation. + /// + public ContainerControl SelectedControl = null; /// public override void OnEndContainsFocus() @@ -233,6 +238,18 @@ namespace FlaxEngine.GUI } } + /// + /// Gets or sets whether to show all of the items. + /// + [EditorOrder(3), Tooltip("Whether to show all of the items in the drop down.")] + public bool ShowAllItems { get; set; } = true; + + /// + /// Gets or sets the number of items to show. Only used if ShowAllItems is false. + /// + [EditorOrder(4), VisibleIf(nameof(ShowAllItems), true), Limit(1), Tooltip("The number of items to show in the drop down.")] + public int NumberOfItemsToShow { get; set; } = 5; + /// /// Event fired when selected index gets changed. /// @@ -411,13 +428,22 @@ namespace FlaxEngine.GUI // TODO: support item templates - var container = new VerticalPanel + var panel = new Panel { AnchorPreset = AnchorPresets.StretchAll, BackgroundColor = BackgroundColor, - AutoSize = false, + ScrollBars = ScrollBars.Vertical, Parent = popup, }; + + var container = new VerticalPanel + { + AnchorPreset = AnchorPresets.StretchAll, + BackgroundColor = Color.Transparent, + IsScrollable = true, + AutoSize = true, + Parent = panel, + }; var border = new Border { BorderColor = BorderColorHighlighted, @@ -482,10 +508,20 @@ namespace FlaxEngine.GUI //AnchorPreset = AnchorPresets.VerticalStretchLeft, Parent = item, }; + popup.SelectedControl = item; } } - popup.Size = new Float2(itemsWidth, height); + if (ShowAllItems || _items.Count < NumberOfItemsToShow) + { + popup.Size = new Float2(itemsWidth, height); + panel.Size = popup.Size; + } + else + { + popup.Size = new Float2(itemsWidth, (itemsHeight + container.Spacing) * NumberOfItemsToShow); + panel.Size = popup.Size; + } return popup; } @@ -527,7 +563,16 @@ namespace FlaxEngine.GUI /// public void ShowPopup() { - var root = Root; + // Find canvas scalar and set as root if it exists. + ContainerControl c = Parent; + while(c.Parent != Root && c.Parent != null) + { + c = c.Parent; + if (c is CanvasScaler scalar) + break; + } + var root = c is CanvasScaler ? c : Root; + if (_items.Count == 0 || root == null) return; @@ -542,7 +587,7 @@ namespace FlaxEngine.GUI // Show dropdown popup var locationRootSpace = Location + new Float2(0, Height); var parent = Parent; - while (parent != null && parent != Root) + while (parent != null && parent != root) { locationRootSpace = parent.PointToParent(ref locationRootSpace); parent = parent.Parent; @@ -551,6 +596,8 @@ namespace FlaxEngine.GUI _popup.Parent = root; _popup.Focus(); _popup.StartMouseCapture(); + if (_popup.SelectedControl != null && _popup.Children[0] is Panel panel) + panel.ScrollViewTo(_popup.SelectedControl, true); OnPopupShow(); } From 1fa03a0de285ee0a3443cc627dfeeaf880de8ef9 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 28 Oct 2023 11:25:14 -0500 Subject: [PATCH 15/57] Fix focus issues to allow panel scroll bar to be clicked and used. --- Source/Engine/UI/GUI/Common/Dropdown.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index bf2dbc0ef..a6c79ea3f 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -32,7 +32,13 @@ namespace FlaxEngine.GUI public override void OnEndContainsFocus() { base.OnEndContainsFocus(); - + + // Dont lose focus when using panel. Does prevent LostFocus even from being called if clicking inside of the panel. + if (Children[0] is Panel panel && panel.IsMouseOver && !panel.ContainsFocus) + { + panel.Focus(); + return; + } // Call event after this 'focus contains flag' propagation ends to prevent focus issues if (LostFocus != null) Scripting.RunOnUpdate(LostFocus); @@ -433,6 +439,7 @@ namespace FlaxEngine.GUI AnchorPreset = AnchorPresets.StretchAll, BackgroundColor = BackgroundColor, ScrollBars = ScrollBars.Vertical, + AutoFocus = true, Parent = popup, }; From 0930671e909e51454b20323d5d78e8e1d2ebc36b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 6 Nov 2023 06:25:17 -0600 Subject: [PATCH 16/57] Cache main panel, cleanup cached variables. --- Source/Engine/UI/GUI/Common/Dropdown.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index a6c79ea3f..92067398d 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -28,15 +28,20 @@ namespace FlaxEngine.GUI /// public ContainerControl SelectedControl = null; + /// + /// The main panel used to hold the items. + /// + public Panel MainPanel = null; + /// public override void OnEndContainsFocus() { base.OnEndContainsFocus(); // Dont lose focus when using panel. Does prevent LostFocus even from being called if clicking inside of the panel. - if (Children[0] is Panel panel && panel.IsMouseOver && !panel.ContainsFocus) + if (MainPanel != null && MainPanel.IsMouseOver && !MainPanel.ContainsFocus) { - panel.Focus(); + MainPanel.Focus(); return; } // Call event after this 'focus contains flag' propagation ends to prevent focus issues @@ -136,6 +141,8 @@ namespace FlaxEngine.GUI public override void OnDestroy() { LostFocus = null; + MainPanel = null; + SelectedControl = null; base.OnDestroy(); } @@ -442,6 +449,7 @@ namespace FlaxEngine.GUI AutoFocus = true, Parent = popup, }; + popup.MainPanel = panel; var container = new VerticalPanel { @@ -603,8 +611,8 @@ namespace FlaxEngine.GUI _popup.Parent = root; _popup.Focus(); _popup.StartMouseCapture(); - if (_popup.SelectedControl != null && _popup.Children[0] is Panel panel) - panel.ScrollViewTo(_popup.SelectedControl, true); + if (_popup.SelectedControl != null && _popup.MainPanel != null) + _popup.MainPanel.ScrollViewTo(_popup.SelectedControl, true); OnPopupShow(); } From d6e93a7fab1544c50b02242e42d37dabfcc09d66 Mon Sep 17 00:00:00 2001 From: SS Date: Mon, 6 Nov 2023 18:49:30 -0700 Subject: [PATCH 17/57] Fixed issue involving stale scripting assemblies in FlaxEngine.Json dynamic type resolution Added new ExtendedSerializationBinder Added callback to clear serializer cache on scripting assembly reload Added low-cost mechanism to invalidate the SerializerCache after domain reload --- .../Engine/Engine/NativeInterop.Unmanaged.cs | 2 + Source/Engine/Engine/NativeInterop.cs | 65 +++-- .../ExtendedSerializationBinder.cs | 238 ++++++++++++++++++ Source/Engine/Serialization/JsonSerializer.cs | 133 +++++++--- 4 files changed, 381 insertions(+), 57 deletions(-) create mode 100644 Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 0391974b6..574b74580 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -1022,6 +1022,8 @@ namespace FlaxEngine.Interop pair.Value.Free(); classAttributesCacheCollectible.Clear(); + FlaxEngine.Json.JsonSerializer.ResetCache(); + // Unload the ALC bool unloading = true; scriptingAssemblyLoadContext.Unloading += (alc) => { unloading = false; }; diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index fea1fadda..28ac5024d 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -278,44 +278,65 @@ namespace FlaxEngine.Interop if (typeCache.TryGetValue(typeName, out Type type)) return type; - type = Type.GetType(typeName, ResolveAssemblyByName, null); + type = Type.GetType(typeName, ResolveAssembly, null); if (type == null) - { - foreach (var assembly in scriptingAssemblyLoadContext.Assemblies) - { - type = assembly.GetType(typeName); - if (type != null) - break; - } - } + type = ResolveSlow(typeName); if (type == null) { - string oldTypeName = typeName; + string fullTypeName = typeName; typeName = typeName.Substring(0, typeName.IndexOf(',')); - type = Type.GetType(typeName, ResolveAssemblyByName, null); + type = Type.GetType(typeName, ResolveAssembly, null); if (type == null) - { - foreach (var assembly in scriptingAssemblyLoadContext.Assemblies) - { - type = assembly.GetType(typeName); - if (type != null) - break; - } - } - typeName = oldTypeName; + type = ResolveSlow(typeName); + + typeName = fullTypeName; } typeCache.Add(typeName, type); return type; + + /// Resolve the type by manually checking every scripting assembly + static Type ResolveSlow(string typeName) { + foreach (var assembly in scriptingAssemblyLoadContext.Assemblies) { + var type = assembly.GetType(typeName); + if (type != null) + return type; + } + return null; + } + + /// Resolve the assembly by name + static Assembly ResolveAssembly(AssemblyName name) => ResolveScriptingAssemblyByName(name, allowPartial: false); } - private static Assembly ResolveAssemblyByName(AssemblyName assemblyName) + /// Find among the scripting assemblies. + /// The name to find + /// If true, partial names should be allowed to be resolved. + /// The resolved assembly, or null if none could be found. + internal static Assembly ResolveScriptingAssemblyByName(AssemblyName assemblyName, bool allowPartial = false) { foreach (Assembly assembly in scriptingAssemblyLoadContext.Assemblies) - if (assembly.GetName() == assemblyName) + { + var curName = assembly.GetName(); + + if (curName == assemblyName || (allowPartial && curName.Name == assemblyName.Name)) return assembly; + } + + if (allowPartial) // Check partial names if full name isn't found + { + string partialName = assemblyName.Name; + + foreach (Assembly assembly in scriptingAssemblyLoadContext.Assemblies) + { + var curName = assembly.GetName(); + + if (curName.Name == partialName) + return assembly; + } + } return null; } diff --git a/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs new file mode 100644 index 000000000..5f976f24f --- /dev/null +++ b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs @@ -0,0 +1,238 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization; +using FlaxEngine.Interop; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +#nullable enable + +namespace FlaxEngine.Json.JsonCustomSerializers +{ + internal class ExtendedSerializationBinder : SerializationBinder, ISerializationBinder + { + private record struct TypeKey(string? assemblyName, string typeName); + + private ConcurrentDictionary _cache; + private Func _resolve; + + /// Clear the cache + /// Should be cleared on scripting domain reload to avoid out of date types participating in dynamic type resolution + public void ResetCache() + { + _cache.Clear(); + } + + public override Type BindToType(string? assemblyName, string typeName) + { + return FindCachedType(new(assemblyName, typeName)); + } + + public override void BindToName(Type serializedType, out string? assemblyName, out string? typeName) + { + assemblyName = serializedType.Assembly.FullName; + typeName = serializedType.FullName; + } + + + public ExtendedSerializationBinder() + { + _resolve = ResolveBind; + _cache = new(); + } + + Type ResolveBind(TypeKey key) + { + Type? type = null; + if (key.assemblyName is null) { // No assembly name, attempt to find globally + type = FindTypeGlobal(key.typeName); + } + + if (type is null && key.assemblyName is not null) { // Type not found yet, but we have assembly name + Assembly? assembly = null; + + assembly = FindScriptingAssembly(key.assemblyName); // Attempt to load from scripting assembly + + if (assembly is null) + assembly = FindLoadedAssembly(key.assemblyName); // Attempt to load from loaded assemblies + + if (assembly is null) + assembly = FindUnloadedAssembly(key.assemblyName); // Attempt to load from unloaded assemblies + + if (assembly is null) + throw MakeAsmResolutionException(key.assemblyName); // Assembly failed to resolve + + type = FindTypeInAssembly(key.typeName, assembly); // We have assembly, attempt to load from assembly + } + + //if (type is null) + // type = _fallBack.BindToType(key.assemblyName, key.typeName); // Use fallback + + if (type is null) + throw MakeTypeResolutionException(key.assemblyName, key.typeName); + + return type; + } + + /// Attempt to find the assembly among loaded scripting assemblies + Assembly? FindScriptingAssembly(string assemblyName) + { + return NativeInterop.ResolveScriptingAssemblyByName(new AssemblyName(assemblyName), allowPartial: true); + } + + /// Attempt to find the assembly by name + Assembly? FindLoadedAssembly(string assemblyName) // TODO + { + return null; + } + + /// Attempt to find the assembly by name + Assembly? FindUnloadedAssembly(string assemblyName) + { + Assembly? assembly = null; + + assembly = Assembly.Load(new AssemblyName(assemblyName)); + + if (assembly is null) + assembly = Assembly.LoadWithPartialName(assemblyName); // Copying behavior of DefaultSerializationBinder + + + return assembly; + } + + + + Type? FindTypeInAssembly(string typeName, Assembly assembly) + { + var type = assembly.GetType(typeName); // Attempt to load directly + + if (type is null && typeName.IndexOf('`') >= 0) // Attempt failed, but name has generic variant tick, try resolving generic manually + type = FindTypeGeneric(typeName, assembly); + + return type; + } + + /// Attempt to find unqualified type by only name + Type? FindTypeGlobal(string typeName) + { + return Type.GetType(typeName); + } + + /// Get type from the cache + private Type FindCachedType(TypeKey key) + { + return _cache.GetOrAdd(key, _resolve); + } + + + + /********************************************* + ** Below code is adapted from Newtonsoft.Json + *********************************************/ + + /// Attempt to recursively resolve a generic type + private Type? FindTypeGeneric(string typeName, Assembly assembly) + { + Type? type = null; + int openBracketIndex = typeName.IndexOf('[', StringComparison.Ordinal); + if (openBracketIndex >= 0) { + string genericTypeDefName = typeName.Substring(0, openBracketIndex); // Find the unspecialized type + Type? genericTypeDef = assembly.GetType(genericTypeDefName); + if (genericTypeDef != null) { + List genericTypeArguments = new List(); // Recursively resolve the arguments + int scope = 0; + int typeArgStartIndex = 0; + int endIndex = typeName.Length - 1; + for (int i = openBracketIndex + 1; i < endIndex; ++i) { + char current = typeName[i]; + switch (current) { + case '[': + if (scope == 0) { + typeArgStartIndex = i + 1; + } + ++scope; + break; + case ']': + --scope; + if (scope == 0) { // All arguments resolved, compose our type + string typeArgAssemblyQualifiedName = typeName.Substring(typeArgStartIndex, i - typeArgStartIndex); + + TypeKey typeNameKey = SplitFullyQualifiedTypeName(typeArgAssemblyQualifiedName); + genericTypeArguments.Add(FindCachedType(typeNameKey)); + } + break; + } + } + + type = genericTypeDef.MakeGenericType(genericTypeArguments.ToArray()); + } + } + + return type; + } + + /// Split a fully qualified type name into assembly name, and type name + private static TypeKey SplitFullyQualifiedTypeName(string fullyQualifiedTypeName) + { + int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName); + + ReadOnlySpan typeName; + ReadOnlySpan assemblyName; + + if (assemblyDelimiterIndex != null) { + typeName = fullyQualifiedTypeName.AsSpan().Slice(0, assemblyDelimiterIndex ?? 0); + assemblyName = fullyQualifiedTypeName.AsSpan().Slice((assemblyDelimiterIndex ?? 0) + 1); + } else { + typeName = fullyQualifiedTypeName; + assemblyName = null; + } + + return new(new(assemblyName), new(typeName)); + } + + /// Find the assembly name inside a fully qualified type name + private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName) + { + // we need to get the first comma following all surrounded in brackets because of generic types + // e.g. System.Collections.Generic.Dictionary`2[[System.String, mscorlib,Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + int scope = 0; + for (int i = 0; i < fullyQualifiedTypeName.Length; i++) { + char current = fullyQualifiedTypeName[i]; + switch (current) { + case '[': + scope++; + break; + case ']': + scope--; + break; + case ',': + if (scope == 0) { + return i; + } + break; + } + } + + return null; + } + + + private static JsonSerializationException MakeAsmResolutionException(string asmName) + { + return new($"Could not load assembly '{asmName}'."); + } + + private static JsonSerializationException MakeTypeResolutionException(string? asmName, string typeName) + { + if (asmName is null) + return new($"Could not find '{typeName}'"); + else + return new($"Could not find '{typeName}' in assembly '{asmName}'."); + } + } +} diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index c36e00b6a..2eb9eec99 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -19,6 +19,7 @@ namespace FlaxEngine.Json { internal class SerializerCache { + public readonly JsonSerializerSettings settings; public Newtonsoft.Json.JsonSerializer JsonSerializer; public StringBuilder StringBuilder; public StringWriter StringWriter; @@ -28,37 +29,35 @@ namespace FlaxEngine.Json public StreamReader Reader; public bool IsWriting; public bool IsReading; + public uint cacheVersion; public unsafe SerializerCache(JsonSerializerSettings settings) { - JsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(settings); - JsonSerializer.Formatting = Formatting.Indented; - JsonSerializer.ReferenceLoopHandling = ReferenceLoopHandling.Serialize; + this.settings = settings; + StringBuilder = new StringBuilder(256); StringWriter = new StringWriter(StringBuilder, CultureInfo.InvariantCulture); - SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer); MemoryStream = new UnmanagedMemoryStream((byte*)0, 0); - Reader = new StreamReader(MemoryStream, Encoding.UTF8, false); - JsonWriter = new JsonTextWriter(StringWriter) + + lock (cacheSyncRoot) { - IndentChar = '\t', - Indentation = 1, - Formatting = JsonSerializer.Formatting, - DateFormatHandling = JsonSerializer.DateFormatHandling, - DateTimeZoneHandling = JsonSerializer.DateTimeZoneHandling, - FloatFormatHandling = JsonSerializer.FloatFormatHandling, - StringEscapeHandling = JsonSerializer.StringEscapeHandling, - Culture = JsonSerializer.Culture, - DateFormatString = JsonSerializer.DateFormatString, - }; + BuildSerializer(); + BuildRead(); + BuildWrite(); + + cacheVersion = currentCacheVersion; + } } public void ReadBegin() { + CheckCacheVersionRebuild(); + + // TODO: Reset reading state (eg if previous deserialization got exception) if (IsReading) - { - // TODO: Reset reading state (eg if previous deserialization got exception) - } + BuildRead(); + + IsWriting = false; IsReading = true; } @@ -70,23 +69,12 @@ namespace FlaxEngine.Json public void WriteBegin() { + CheckCacheVersionRebuild(); + + // Reset writing state (eg if previous serialization got exception) if (IsWriting) - { - // Reset writing state (eg if previous serialization got exception) - SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer); - JsonWriter = new JsonTextWriter(StringWriter) - { - IndentChar = '\t', - Indentation = 1, - Formatting = JsonSerializer.Formatting, - DateFormatHandling = JsonSerializer.DateFormatHandling, - DateTimeZoneHandling = JsonSerializer.DateTimeZoneHandling, - FloatFormatHandling = JsonSerializer.FloatFormatHandling, - StringEscapeHandling = JsonSerializer.StringEscapeHandling, - Culture = JsonSerializer.Culture, - DateFormatString = JsonSerializer.DateFormatString, - }; - } + BuildWrite(); + StringBuilder.Clear(); IsWriting = true; IsReading = false; @@ -96,21 +84,83 @@ namespace FlaxEngine.Json { IsWriting = false; } + + /// Check that the cache is up to date, rebuild it if it isn't + private void CheckCacheVersionRebuild() + { + var cCV = currentCacheVersion; + if (cacheVersion == cCV) + return; + + lock (cacheSyncRoot) + { + cCV = currentCacheVersion; + if (cacheVersion == cCV) + return; + + BuildSerializer(); + + BuildRead(); + BuildWrite(); + + cacheVersion = cCV; + } + } + + /// Builds the serializer + private void BuildSerializer() + { + JsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(settings); + JsonSerializer.Formatting = Formatting.Indented; + JsonSerializer.ReferenceLoopHandling = ReferenceLoopHandling.Serialize; + } + + /// Builds the reader state + private void BuildRead() + { + Reader = new StreamReader(MemoryStream, Encoding.UTF8, false); + } + + /// Builds the writer state + private void BuildWrite() + { + SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer); + JsonWriter = new JsonTextWriter(StringWriter) { + IndentChar = '\t', + Indentation = 1, + Formatting = JsonSerializer.Formatting, + DateFormatHandling = JsonSerializer.DateFormatHandling, + DateTimeZoneHandling = JsonSerializer.DateTimeZoneHandling, + FloatFormatHandling = JsonSerializer.FloatFormatHandling, + StringEscapeHandling = JsonSerializer.StringEscapeHandling, + Culture = JsonSerializer.Culture, + DateFormatString = JsonSerializer.DateFormatString, + }; + } } internal static JsonSerializerSettings Settings = CreateDefaultSettings(false); internal static JsonSerializerSettings SettingsManagedOnly = CreateDefaultSettings(true); + internal static ExtendedSerializationBinder SerializationBinder; internal static FlaxObjectConverter ObjectConverter; + internal static ThreadLocal Current = new ThreadLocal(); internal static ThreadLocal Cache = new ThreadLocal(() => new SerializerCache(Settings)); internal static ThreadLocal CacheManagedOnly = new ThreadLocal(() => new SerializerCache(SettingsManagedOnly)); internal static ThreadLocal CachedGuidBuffer = new ThreadLocal(() => Marshal.AllocHGlobal(32 * sizeof(char)), true); internal static string CachedGuidDigits = "0123456789abcdef"; + /// The version of the cache, used to check that a cache is not out of date + internal static uint currentCacheVersion = 0; + /// Used to synchronize cache operations such as rebuild, and + internal static readonly object cacheSyncRoot = new(); internal static JsonSerializerSettings CreateDefaultSettings(bool isManagedOnly) { //Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals = ValueEquals; + if (SerializationBinder is null) + SerializationBinder = new(); + var settings = new JsonSerializerSettings { ContractResolver = new ExtendedDefaultContractResolver(isManagedOnly), @@ -118,6 +168,7 @@ namespace FlaxEngine.Json TypeNameHandling = TypeNameHandling.Auto, NullValueHandling = NullValueHandling.Include, ObjectCreationHandling = ObjectCreationHandling.Auto, + SerializationBinder = SerializationBinder, }; if (ObjectConverter == null) ObjectConverter = new FlaxObjectConverter(); @@ -134,6 +185,18 @@ namespace FlaxEngine.Json return settings; } + /// Called to reset the serialization cache + internal static void ResetCache() + { + lock (cacheSyncRoot) + { + unchecked { currentCacheVersion++; } + + Newtonsoft.Json.JsonSerializer.ClearCache(); + SerializationBinder.ResetCache(); + } + } + internal static void Dispose() { CachedGuidBuffer.Values.ForEach(Marshal.FreeHGlobal); From 77b6a4a68b3725dee0a50ecce222af88954a99dd Mon Sep 17 00:00:00 2001 From: SS Date: Tue, 7 Nov 2023 16:03:03 -0700 Subject: [PATCH 18/57] Fixed issue in NativeInterop Readded check in current app domain to ExtendedSerializationBinder --- Source/Engine/Engine/NativeInterop.cs | 11 +++++-- .../ExtendedSerializationBinder.cs | 33 ++++++++++++------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 28ac5024d..b4635e0b4 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -317,11 +317,16 @@ namespace FlaxEngine.Interop /// The resolved assembly, or null if none could be found. internal static Assembly ResolveScriptingAssemblyByName(AssemblyName assemblyName, bool allowPartial = false) { - foreach (Assembly assembly in scriptingAssemblyLoadContext.Assemblies) + var lc = scriptingAssemblyLoadContext; + + if (lc is null) + return null; + + foreach (Assembly assembly in lc.Assemblies) { var curName = assembly.GetName(); - if (curName == assemblyName || (allowPartial && curName.Name == assemblyName.Name)) + if (curName == assemblyName) return assembly; } @@ -329,7 +334,7 @@ namespace FlaxEngine.Interop { string partialName = assemblyName.Name; - foreach (Assembly assembly in scriptingAssemblyLoadContext.Assemblies) + foreach (Assembly assembly in lc.Assemblies) { var curName = assembly.GetName(); diff --git a/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs index 5f976f24f..8199a2bd9 100644 --- a/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs +++ b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Reflection; using System.Runtime.Serialization; using FlaxEngine.Interop; @@ -56,13 +57,13 @@ namespace FlaxEngine.Json.JsonCustomSerializers if (type is null && key.assemblyName is not null) { // Type not found yet, but we have assembly name Assembly? assembly = null; - assembly = FindScriptingAssembly(key.assemblyName); // Attempt to load from scripting assembly + assembly = FindScriptingAssembly(new(key.assemblyName)); // Attempt to find in scripting assemblies if (assembly is null) - assembly = FindLoadedAssembly(key.assemblyName); // Attempt to load from loaded assemblies + assembly = FindLoadAssembly(new(key.assemblyName)); // Attempt to load if (assembly is null) - assembly = FindUnloadedAssembly(key.assemblyName); // Attempt to load from unloaded assemblies + assembly = FindDomainAssembly(new(key.assemblyName)); // Attempt to find in the current domain if (assembly is null) throw MakeAsmResolutionException(key.assemblyName); // Assembly failed to resolve @@ -80,27 +81,35 @@ namespace FlaxEngine.Json.JsonCustomSerializers } /// Attempt to find the assembly among loaded scripting assemblies - Assembly? FindScriptingAssembly(string assemblyName) + Assembly? FindScriptingAssembly(AssemblyName assemblyName) { - return NativeInterop.ResolveScriptingAssemblyByName(new AssemblyName(assemblyName), allowPartial: true); + return NativeInterop.ResolveScriptingAssemblyByName(assemblyName, allowPartial: true); } - /// Attempt to find the assembly by name - Assembly? FindLoadedAssembly(string assemblyName) // TODO + /// Attempt to find the assembly in the current domain + Assembly? FindDomainAssembly(AssemblyName assemblyName) { + var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToArray(); + + foreach (Assembly assembly in assemblies) { + var curName = assembly.GetName(); + + if (curName == assemblyName || curName.Name == assemblyName.Name) + return assembly; + } + return null; } - /// Attempt to find the assembly by name - Assembly? FindUnloadedAssembly(string assemblyName) + /// Attempt to load the assembly + Assembly? FindLoadAssembly(AssemblyName assemblyName) { Assembly? assembly = null; - assembly = Assembly.Load(new AssemblyName(assemblyName)); + assembly = Assembly.Load(assemblyName); if (assembly is null) - assembly = Assembly.LoadWithPartialName(assemblyName); // Copying behavior of DefaultSerializationBinder - + assembly = Assembly.LoadWithPartialName(assemblyName.Name); // Copying behavior of DefaultSerializationBinder return assembly; } From 52a1175f967a13a3630e0f36c362d7e98f792756 Mon Sep 17 00:00:00 2001 From: SS Date: Tue, 7 Nov 2023 16:30:50 -0700 Subject: [PATCH 19/57] Seperated out assembly resolution logic to it's own function --- .../ExtendedSerializationBinder.cs | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs index 8199a2bd9..b4f0173ab 100644 --- a/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs +++ b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs @@ -19,14 +19,15 @@ namespace FlaxEngine.Json.JsonCustomSerializers { private record struct TypeKey(string? assemblyName, string typeName); - private ConcurrentDictionary _cache; - private Func _resolve; + private ConcurrentDictionary _typeCache; + private Func _resolveType; + /// Clear the cache /// Should be cleared on scripting domain reload to avoid out of date types participating in dynamic type resolution public void ResetCache() { - _cache.Clear(); + _typeCache.Clear(); } public override Type BindToType(string? assemblyName, string typeName) @@ -43,11 +44,11 @@ namespace FlaxEngine.Json.JsonCustomSerializers public ExtendedSerializationBinder() { - _resolve = ResolveBind; - _cache = new(); + _resolveType = ResolveType; + _typeCache = new(); } - Type ResolveBind(TypeKey key) + Type ResolveType(TypeKey key) { Type? type = null; if (key.assemblyName is null) { // No assembly name, attempt to find globally @@ -55,18 +56,7 @@ namespace FlaxEngine.Json.JsonCustomSerializers } if (type is null && key.assemblyName is not null) { // Type not found yet, but we have assembly name - Assembly? assembly = null; - - assembly = FindScriptingAssembly(new(key.assemblyName)); // Attempt to find in scripting assemblies - - if (assembly is null) - assembly = FindLoadAssembly(new(key.assemblyName)); // Attempt to load - - if (assembly is null) - assembly = FindDomainAssembly(new(key.assemblyName)); // Attempt to find in the current domain - - if (assembly is null) - throw MakeAsmResolutionException(key.assemblyName); // Assembly failed to resolve + var assembly = ResolveAssembly(new(key.assemblyName)); type = FindTypeInAssembly(key.typeName, assembly); // We have assembly, attempt to load from assembly } @@ -80,6 +70,25 @@ namespace FlaxEngine.Json.JsonCustomSerializers return type; } + + Assembly ResolveAssembly(AssemblyName name) + { + Assembly? assembly = null; + + assembly = FindScriptingAssembly(name); // Attempt to find in scripting assemblies + + if (assembly is null) + assembly = FindLoadAssembly(name); // Attempt to load + + if (assembly is null) + assembly = FindDomainAssembly(name); // Attempt to find in the current domain + + if (assembly is null) + throw MakeAsmResolutionException(name.FullName); // Assembly failed to resolve + + return assembly; + } + /// Attempt to find the assembly among loaded scripting assemblies Assembly? FindScriptingAssembly(AssemblyName assemblyName) { @@ -91,7 +100,7 @@ namespace FlaxEngine.Json.JsonCustomSerializers { var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToArray(); - foreach (Assembly assembly in assemblies) { + foreach (Assembly assembly in assemblies) { // Looking in domain may be necessary (in case of anon dynamic assembly for example) var curName = assembly.GetName(); if (curName == assemblyName || curName.Name == assemblyName.Name) @@ -115,7 +124,7 @@ namespace FlaxEngine.Json.JsonCustomSerializers } - + /// Attempt to find a type in a specified assembly Type? FindTypeInAssembly(string typeName, Assembly assembly) { var type = assembly.GetType(typeName); // Attempt to load directly @@ -135,10 +144,9 @@ namespace FlaxEngine.Json.JsonCustomSerializers /// Get type from the cache private Type FindCachedType(TypeKey key) { - return _cache.GetOrAdd(key, _resolve); + return _typeCache.GetOrAdd(key, _resolveType); } - - + /********************************************* ** Below code is adapted from Newtonsoft.Json From d90b723487f4d593c317ad03664a498e1635de57 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 8 Nov 2023 21:30:31 +0100 Subject: [PATCH 20/57] Code cleanup and usability tweaks for #1600 --- Source/Editor/Surface/Archetypes/Material.cs | 22 +++++++-------- .../MaterialGenerator.Material.cpp | 27 +++++++++---------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 8aba82d86..f65804023 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -886,20 +886,19 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 40, Title = "Rectangle Mask", - Description = "Creates a Rectangle mask", + Description = "Creates a rectangle mask", Flags = NodeFlags.MaterialGraph, Size = new Float2(150, 40), ConnectionsHints = ConnectionsHint.Vector, DefaultValues = new object[] { - new Float2(0, 0), new Float2(0.5f, 0.5f), }, Elements = new[] { NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Float2), 0), - NodeElementArchetype.Factory.Input(1, "Rectangle", true, typeof(Float2), 1), - NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), + NodeElementArchetype.Factory.Input(1, "Rectangle", true, typeof(Float2), 1, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 2), } }, new NodeArchetype @@ -914,28 +913,27 @@ namespace FlaxEditor.Surface.Archetypes DependentBoxes = new[] { 1 }, Elements = new[] { - NodeElementArchetype.Factory.Input(0, "value", true, null, 0), + NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), NodeElementArchetype.Factory.Output(0, string.Empty, null, 1), } }, new NodeArchetype { TypeID = 42, - Title = "AAStep", - Description = "Smooth version of step", + Title = "AA Step", + Description = "Smooth version of step function with less aliasing", Flags = NodeFlags.MaterialGraph, Size = new Float2(150, 40), ConnectionsHints = ConnectionsHint.Vector, DefaultValues = new object[] { - 1, - 0 + 0.5f }, Elements = new[] { - NodeElementArchetype.Factory.Input(0, "value", true, typeof(float), 0), - NodeElementArchetype.Factory.Input(1, "gradient", true, typeof(float), 1), - NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), + NodeElementArchetype.Factory.Input(0, "Value", true, typeof(float), 0), + NodeElementArchetype.Factory.Input(1, "Gradient", true, typeof(float), 1, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 2), } }, }; diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 56de05137..f669d36d2 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -519,39 +519,38 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) } break; } - // Ratangle Mask + // Rectangle Mask case 40: { const auto uv = tryGetValue(node->GetBox(0), getUVs).AsFloat2(); - const auto rectangle = tryGetValue(node->GetBox(1), node->Values[1]).AsFloat2(); - - auto d = writeLocal(ValueType::Float2, String::Format(TEXT("abs({0} * 2 - 1) - {1}"),uv.Value, rectangle.Value), node); - auto d2 = writeLocal(ValueType::Float2 , String::Format(TEXT("1 - {0} / fwidth({0})"), d.Value), node); - value = writeLocal(ValueType::Float , String::Format(TEXT("saturate(min({0}.x, {0}.y))"), d2.Value), node); + const auto rectangle = tryGetValue(node->GetBox(1), node->Values[0]).AsFloat2(); + auto d = writeLocal(ValueType::Float2, String::Format(TEXT("abs({0} * 2 - 1) - {1}"), uv.Value, rectangle.Value), node); + auto d2 = writeLocal(ValueType::Float2, String::Format(TEXT("1 - {0} / fwidth({0})"), d.Value), node); + value = writeLocal(ValueType::Float, String::Format(TEXT("saturate(min({0}.x, {0}.y))"), d2.Value), node); break; } // FWidth case 41: { const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); - value = writeLocal(inValue.Type, String::Format(TEXT("fwidth({0})"), inValue.Value), node); + value = writeLocal(inValue.Type, String::Format(TEXT("fwidth({0})"), inValue.Value), node); break; } - //AAStep (smooth version of step) + // AA Step case 42: { - //source https://www.ronja-tutorials.com/post/046-fwidth/#a-better-step + // Reference: https://www.ronja-tutorials.com/post/046-fwidth/#a-better-step const auto compValue = tryGetValue(node->GetBox(0), getUVs).AsFloat(); - const auto gradient = tryGetValue(node->GetBox(1), node->Values[1]).AsFloat(); + const auto gradient = tryGetValue(node->GetBox(1), node->Values[0]).AsFloat(); auto change = writeLocal(ValueType::Float, String::Format(TEXT("fwidth({0})"), gradient.Value), node); - //base the range of the inverse lerp on the change over two pixels - auto lowerEdge = writeLocal(ValueType::Float, String::Format(TEXT("{0} - {1})"), compValue.Value, change.Value), node); - auto upperEdge = writeLocal(ValueType::Float, String::Format(TEXT("{0} + {1})"), compValue.Value, change.Value), node); + // Base the range of the inverse lerp on the change over two pixels + auto lowerEdge = writeLocal(ValueType::Float, String::Format(TEXT("{0} - {1}"), compValue.Value, change.Value), node); + auto upperEdge = writeLocal(ValueType::Float, String::Format(TEXT("{0} + {1}"), compValue.Value, change.Value), node); - //do the inverse interpolation and saturate it + // Do the inverse interpolation and saturate it value = writeLocal(ValueType::Float, String::Format(TEXT("saturate((({0} - {1}) / ({2} - {1})))"), gradient.Value, lowerEdge.Value, upperEdge.Value), node); } default: From d8a54692f044367a5e340eea344c57c66c1c9e51 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 9 Nov 2023 09:10:25 +0100 Subject: [PATCH 21/57] Code cleanup #1785 --- .../Editor/Viewport/Previews/MaterialPreview.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Source/Editor/Viewport/Previews/MaterialPreview.cs b/Source/Editor/Viewport/Previews/MaterialPreview.cs index f05c8413b..46aac1cdf 100644 --- a/Source/Editor/Viewport/Previews/MaterialPreview.cs +++ b/Source/Editor/Viewport/Previews/MaterialPreview.cs @@ -7,7 +7,6 @@ using FlaxEngine.GUI; using FlaxEditor.Viewport.Widgets; using FlaxEditor.GUI.ContextMenu; using Object = FlaxEngine.Object; -using FlaxEditor.CustomEditors.Editors; using FlaxEditor.GUI; using FlaxEditor.Scripting; @@ -80,17 +79,12 @@ namespace FlaxEditor.Viewport.Previews set { if (value == -1) // Using Custom Model - { return; - } - if (value < 0 || value > Models.Length) throw new ArgumentOutOfRangeException(); if (_customModelPicker != null) - { - _customModelPicker.SelectedAsset = null; - } + _customModelPicker.Validator.SelectedAsset = null; _selectedModelIndex = value; _previewModel.Model = FlaxEngine.Content.LoadAsyncInternal("Editor/Primitives/" + Models[value]); _previewModel.Transform = Transforms[value]; @@ -116,22 +110,21 @@ namespace FlaxEditor.Viewport.Previews _modelWidgetButtonMenu.AddSeparator(); _customModelPicker = new AssetPicker(new ScriptType(typeof(Model)), Float2.Zero); - // Label Button + // Label button var customModelPickerLabel = _modelWidgetButtonMenu.AddButton("Custom Model:"); customModelPickerLabel.CloseMenuOnClick = false; customModelPickerLabel.Checked = _customModel != null; // Container button var customModelPickerButton = _modelWidgetButtonMenu.AddButton(""); - customModelPickerButton.Height = _customModelPicker.Height + 4; customModelPickerButton.CloseMenuOnClick = false; _customModelPicker.Parent = customModelPickerButton; - _customModelPicker.SelectedAsset = _customModel; + _customModelPicker.Validator.SelectedAsset = _customModel; _customModelPicker.SelectedItemChanged += () => { - _customModel = _customModelPicker.SelectedAsset as Model; - if (_customModelPicker.SelectedAsset == null) + _customModel = _customModelPicker.Validator.SelectedAsset as Model; + if (_customModelPicker.Validator.SelectedAsset == null) { SelectedModelIndex = 0; ResetModelContextMenu(); From 710b9275fd5a05f7f22b6a2142a1bf28cd65d818 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 9 Nov 2023 09:13:40 +0100 Subject: [PATCH 22/57] Fix minor typos https://github.com/FlaxEngine/FlaxDocs/pull/123 --- Source/Engine/Graphics/Materials/MaterialInfo.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Graphics/Materials/MaterialInfo.h b/Source/Engine/Graphics/Materials/MaterialInfo.h index 86fab4548..8e4933150 100644 --- a/Source/Engine/Graphics/Materials/MaterialInfo.h +++ b/Source/Engine/Graphics/Materials/MaterialInfo.h @@ -86,7 +86,7 @@ API_ENUM() enum class MaterialBlendMode : byte API_ENUM() enum class MaterialShadingModel : byte { /// - /// The unlit material. Emissive channel is used as an output color. Can perform custom lighting operations or just glow. Won't be affected by the lighting pipeline. + /// The unlit material. The emissive channel is used as an output color. Can perform custom lighting operations or just glow. Won't be affected by the lighting pipeline. /// Unlit = 0, @@ -96,7 +96,7 @@ API_ENUM() enum class MaterialShadingModel : byte Lit = 1, /// - /// The subsurface material. Intended for materials like vax or skin that need light scattering to transport simulation through the object. + /// The subsurface material. Intended for materials like wax or skin that need light scattering to transport simulation through the object. /// Subsurface = 2, @@ -366,12 +366,12 @@ API_ENUM() enum class MaterialDecalBlendingMode : byte API_ENUM() enum class MaterialTransparentLightingMode : byte { /// - /// Default directional lighting evaluated per-pixel at the material surface. Use it for semi-transparent surfaces - with both diffuse and specular lighting component active. + /// Default directional lighting evaluated per-pixel at the material surface. Use it for semi-transparent surfaces - with both diffuse and specular lighting components active. /// Surface = 0, /// - /// Non-directional lighting evaluated per-pixel at material surface. Use it for volumetric objects such as smoke, rain or dust - only diffuse lighting term is active (no specular highlights). + /// Non-directional lighting evaluated per-pixel at material surface. Use it for volumetric objects such as smoke, rain or dust - only the diffuse lighting term is active (no specular highlights). /// SurfaceNonDirectional = 1, }; From 22c8ec53426543c1d8192c2bf5744c0bb37b384c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 9 Nov 2023 11:50:48 +0100 Subject: [PATCH 23/57] Fix crash when rigidbody gets deleted during physical collision #1893 --- .../Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp b/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp index 5968bdd78..5781e4641 100644 --- a/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp +++ b/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp @@ -137,7 +137,11 @@ void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, c c.ThisActor = static_cast(pair.shapes[0]->userData); c.OtherActor = static_cast(pair.shapes[1]->userData); - ASSERT_LOW_LAYER(c.ThisActor && c.OtherActor); + if (c.ThisActor == nullptr || c.OtherActor == nullptr) + { + // One of the actors was deleted (eg. via RigidBody destroyed by gameplay) then skip processing this collision + continue; + } // Extract contact points while (i.hasNextPatch()) From 4ae57e7769d03899182a1a81b9c264934b8189a5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 9 Nov 2023 14:52:28 +0100 Subject: [PATCH 24/57] Fix issue with asset loading to be properly canceled when reimporting file #1894 --- Source/Engine/Audio/AudioClip.cpp | 1 + Source/Engine/Content/Asset.cpp | 8 ++++++++ Source/Engine/Content/Assets/Model.cpp | 1 + Source/Engine/Content/Assets/SkinnedModel.cpp | 1 + .../Engine/Content/Loading/Tasks/LoadAssetDataTask.h | 4 ++-- Source/Engine/Content/Storage/FlaxStorage.cpp | 10 +++++++--- Source/Engine/Graphics/Textures/TextureBase.cpp | 1 + 7 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Audio/AudioClip.cpp b/Source/Engine/Audio/AudioClip.cpp index d04da7274..689f38d12 100644 --- a/Source/Engine/Audio/AudioClip.cpp +++ b/Source/Engine/Audio/AudioClip.cpp @@ -225,6 +225,7 @@ bool AudioClip::ExtractDataRaw(Array& resultData, AudioDataInfo& resultDat void AudioClip::CancelStreaming() { + Asset::CancelStreaming(); CancelStreamingTasks(); } diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index afe38f0a7..93d904d5e 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -522,6 +522,14 @@ void Asset::InitAsVirtual() void Asset::CancelStreaming() { + // Cancel loading task but go over asset locker to prevent case if other load threads still loads asset while it's reimported on other thread + Locker.Lock(); + ContentLoadTask* loadTask = _loadingTask; + Locker.Unlock(); + if (loadTask) + { + loadTask->Cancel(); + } } #if USE_EDITOR diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp index 5a008645d..691b00a50 100644 --- a/Source/Engine/Content/Assets/Model.cpp +++ b/Source/Engine/Content/Assets/Model.cpp @@ -783,6 +783,7 @@ void Model::InitAsVirtual() void Model::CancelStreaming() { + Asset::CancelStreaming(); CancelStreamingTasks(); } diff --git a/Source/Engine/Content/Assets/SkinnedModel.cpp b/Source/Engine/Content/Assets/SkinnedModel.cpp index 5871087d9..b823db5a3 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.cpp +++ b/Source/Engine/Content/Assets/SkinnedModel.cpp @@ -969,6 +969,7 @@ void SkinnedModel::InitAsVirtual() void SkinnedModel::CancelStreaming() { + Asset::CancelStreaming(); CancelStreamingTasks(); } diff --git a/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h b/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h index 23cf5f787..4a7bbb2bb 100644 --- a/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h +++ b/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h @@ -48,6 +48,8 @@ protected: // [ContentLoadTask] Result run() override { + if (IsCancelRequested()) + return Result::Ok; PROFILE_CPU(); AssetReference ref = _asset.Get(); @@ -67,8 +69,6 @@ protected: { if (IsCancelRequested()) return Result::Ok; - - // Load it #if TRACY_ENABLE ZoneScoped; ZoneName(*name, name.Length()); diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index d530e5456..a47e0bd0e 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -1302,15 +1302,15 @@ void FlaxStorage::CloseFileHandles() // In those situations all the async tasks using this storage should be cancelled externally // Ensure that no one is using this resource - int32 waitTime = 10; + int32 waitTime = 100; while (Platform::AtomicRead(&_chunksLock) != 0 && waitTime-- > 0) - Platform::Sleep(10); + Platform::Sleep(1); if (Platform::AtomicRead(&_chunksLock) != 0) { // File can be locked by some streaming tasks (eg. AudioClip::StreamingTask or StreamModelLODTask) + Entry e; for (int32 i = 0; i < GetEntriesCount(); i++) { - Entry e; GetEntry(i, e); Asset* asset = Content::GetAsset(e.ID); if (asset) @@ -1320,8 +1320,12 @@ void FlaxStorage::CloseFileHandles() } } } + waitTime = 100; + while (Platform::AtomicRead(&_chunksLock) != 0 && waitTime-- > 0) + Platform::Sleep(1); ASSERT(_chunksLock == 0); + // Close file handles (from all threads) _file.DeleteAll(); } diff --git a/Source/Engine/Graphics/Textures/TextureBase.cpp b/Source/Engine/Graphics/Textures/TextureBase.cpp index 538b15a4a..181955fce 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.cpp +++ b/Source/Engine/Graphics/Textures/TextureBase.cpp @@ -660,6 +660,7 @@ uint64 TextureBase::GetMemoryUsage() const void TextureBase::CancelStreaming() { + Asset::CancelStreaming(); _texture.CancelStreamingTasks(); } From 3c71dc99e0aac96348e4a74cb107ae4525fbc89c Mon Sep 17 00:00:00 2001 From: "Mr. Capybara" Date: Thu, 9 Nov 2023 14:08:01 -0400 Subject: [PATCH 25/57] Rebuild navigation after apply changes in Navigation asset --- Source/Engine/Navigation/Navigation.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index 8bc94eaaa..09e9b65ba 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -220,6 +220,12 @@ void NavigationSettings::Apply() #endif } } + + // Rebuild all navmeshs after apply changes on navigation + for (auto scene : Level::Scenes) + { + Navigation::BuildNavMesh(scene); + } } void NavigationSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) From 2ae290491e4d56e036bab4c38823a29f7369028d Mon Sep 17 00:00:00 2001 From: "Mr. Capybara" Date: Thu, 9 Nov 2023 14:50:56 -0400 Subject: [PATCH 26/57] fix build --- Source/Engine/Navigation/Navigation.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index 09e9b65ba..c224389f8 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -221,11 +221,13 @@ void NavigationSettings::Apply() } } +#if USE_EDITOR // Rebuild all navmeshs after apply changes on navigation for (auto scene : Level::Scenes) { Navigation::BuildNavMesh(scene); } +#endif } void NavigationSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) From 39a5b8e6353d43aad2d3c57991dfbd2f7b72e0e1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 10 Nov 2023 01:32:24 +0100 Subject: [PATCH 27/57] Invert check order --- Source/Editor/Managed/ManagedEditor.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index fd9219d35..bed742885 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -330,14 +330,15 @@ bool ManagedEditor::CanReloadScripts() bool ManagedEditor::CanAutoBuildCSG() { + if (!ManagedEditorOptions.AutoRebuildCSG) + return false; + // Skip calls from non-managed thread (eg. physics worker) if (!MCore::Thread::IsAttached()) return false; if (!HasManagedInstance()) return false; - if (!ManagedEditorOptions.AutoRebuildCSG) - return false; if (Internal_CanAutoBuildCSG == nullptr) { Internal_CanAutoBuildCSG = GetClass()->GetMethod("Internal_CanAutoBuildCSG"); @@ -348,14 +349,15 @@ bool ManagedEditor::CanAutoBuildCSG() bool ManagedEditor::CanAutoBuildNavMesh() { + if (!ManagedEditorOptions.AutoRebuildNavMesh) + return false; + // Skip calls from non-managed thread (eg. physics worker) if (!MCore::Thread::IsAttached()) return false; if (!HasManagedInstance()) return false; - if (!ManagedEditorOptions.AutoRebuildNavMesh) - return false; if (Internal_CanAutoBuildNavMesh == nullptr) { Internal_CanAutoBuildNavMesh = GetClass()->GetMethod("Internal_CanAutoBuildNavMesh"); From 7d70a1503456577b696f0cfe3f2eac1a0da239b3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 10 Nov 2023 10:20:21 +0100 Subject: [PATCH 28/57] Fix color editing control to properly handle mouse event #1782 --- Source/Editor/GUI/Input/ColorValueBox.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/Input/ColorValueBox.cs b/Source/Editor/GUI/Input/ColorValueBox.cs index 167cc65bb..fdd7d073a 100644 --- a/Source/Editor/GUI/Input/ColorValueBox.cs +++ b/Source/Editor/GUI/Input/ColorValueBox.cs @@ -14,6 +14,8 @@ namespace FlaxEditor.GUI.Input [HideInEditor] public class ColorValueBox : Control { + private bool _isMouseDown; + /// /// Delegate function used for the color picker events handling. /// @@ -134,11 +136,22 @@ namespace FlaxEditor.GUI.Input Render2D.DrawRectangle(r, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black); } + /// + public override bool OnMouseDown(Float2 location, MouseButton button) + { + _isMouseDown = true; + return base.OnMouseDown(location, button); + } + /// public override bool OnMouseUp(Float2 location, MouseButton button) { - Focus(); - OnSubmit(); + if (_isMouseDown) + { + _isMouseDown = false; + Focus(); + OnSubmit(); + } return true; } From 9cd8c02911f2d9e8849e505b4b3ce3c043c36e5a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 10 Nov 2023 11:34:52 +0100 Subject: [PATCH 29/57] Fix `NetworkTransform` to properly reject local simulation deltas on incoming authoritative transform data #1907 --- Source/Engine/Networking/Components/NetworkTransform.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Networking/Components/NetworkTransform.cpp b/Source/Engine/Networking/Components/NetworkTransform.cpp index af802649b..6f5943ff1 100644 --- a/Source/Engine/Networking/Components/NetworkTransform.cpp +++ b/Source/Engine/Networking/Components/NetworkTransform.cpp @@ -301,11 +301,8 @@ void NetworkTransform::Deserialize(NetworkStream* stream) _buffer.Clear(); _bufferHasDeltas = true; } - // TODO: items are added in order to do batch removal - for (int32 i = 0; i < _buffer.Count() && _buffer[i].SequenceIndex < sequenceIndex; i++) - { - _buffer.RemoveAtKeepOrder(i); - } + while (_buffer.Count() != 0 && _buffer[0].SequenceIndex < sequenceIndex) + _buffer.RemoveAtKeepOrder(0); // Use received authoritative actor transformation but re-apply all deltas not yet processed by the server due to lag (reconciliation) for (auto& e : _buffer) From ed69f1112118a131b68ebe934ed4d9a6f3ad2e3c Mon Sep 17 00:00:00 2001 From: MineBill Date: Fri, 10 Nov 2023 13:56:19 +0200 Subject: [PATCH 30/57] Don't hardcode appdata path for linux anymore. --- Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp index 7f5ca6f17..de0a30e40 100644 --- a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -207,9 +207,9 @@ void RiderCodeEditor::FindEditors(Array* output) FileSystem::GetChildDirectories(subDirectories, TEXT("/opt/")); // Versions installed via JetBrains Toolbox - SearchDirectory(&installations, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/rider/")); - FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/Rider/ch-0")); - FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT(".local/share/JetBrains/Toolbox/apps/Rider/ch-1")); // Beta versions + SearchDirectory(&installations, localAppDataPath / TEXT("JetBrains/Toolbox/apps/rider/")); + FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("JetBrains/Toolbox/apps/Rider/ch-0")); + FileSystem::GetChildDirectories(subDirectories, localAppDataPath / TEXT("JetBrains/Toolbox/apps/Rider/ch-1")); // Beta versions // Detect Flatpak installations SearchDirectory(&installations, From 36daa38e0fb249c479ccaba767967846fc15e276 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 10 Nov 2023 13:23:32 +0100 Subject: [PATCH 31/57] Fix `CollectionEditor` to properly support editing multiple arrays #1818 --- Source/Editor/CustomEditors/Editors/CollectionEditor.cs | 2 +- .../CustomEditors/Editors/ModelInstanceEntryEditor.cs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index 7d92c71c0..6f623fb23 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -113,7 +113,7 @@ namespace FlaxEditor.CustomEditors.Editors public override void Initialize(LayoutElementsContainer layout) { // No support for different collections for now - if (HasDifferentValues || HasDifferentTypes) + if (HasDifferentTypes) return; var size = Count; diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index c215a5ab7..f901b20d9 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -28,14 +28,16 @@ namespace FlaxEditor.CustomEditors.Editors var group = layout.Group("Entry"); _group = group; - if (ParentEditor == null) + if (ParentEditor == null || HasDifferentTypes) return; var entry = (ModelInstanceEntry)Values[0]; var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this); var materialLabel = new PropertyNameLabel("Material"); materialLabel.TooltipText = "The mesh surface material used for the rendering."; - if (ParentEditor.ParentEditor?.Values[0] is ModelInstanceActor modelInstance) + var parentEditorValues = ParentEditor.ParentEditor?.Values; + if (parentEditorValues?[0] is ModelInstanceActor modelInstance) { + // TODO: store _modelInstance and _material in array for each selected model instance actor _entryIndex = entryIndex; _modelInstance = modelInstance; var slots = modelInstance.MaterialSlots; @@ -56,6 +58,8 @@ 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); + for (var i = 1; i < parentEditorValues.Count; i++) + materialValue.Add(_material); var materialEditor = (AssetRefEditor)_group.Property(materialLabel, materialValue); materialEditor.Values.SetDefaultValue(defaultValue); materialEditor.RefreshDefaultValue(); From 626cde118bfee87d0a675cef27d72601499fe40d Mon Sep 17 00:00:00 2001 From: "Mr. Capybara" <79365912+RuanLucasGD@users.noreply.github.com> Date: Fri, 10 Nov 2023 09:45:46 -0300 Subject: [PATCH 32/57] Add verification to rebuild navigation --- Source/Engine/Navigation/Navigation.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index c224389f8..73e11e630 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -9,6 +9,8 @@ #include "Engine/Content/JsonAsset.h" #include "Engine/Threading/Threading.h" #if USE_EDITOR +#include "Editor/Editor.h" +#include "Editor/Managed/ManagedEditor.h" #include "Engine/Level/Level.h" #include "Engine/Level/Scene/Scene.h" #endif @@ -222,10 +224,13 @@ void NavigationSettings::Apply() } #if USE_EDITOR - // Rebuild all navmeshs after apply changes on navigation - for (auto scene : Level::Scenes) + if (!Editor::IsPlayMode && Editor::Managed && Editor::Managed->CanAutoBuildNavMesh()) { - Navigation::BuildNavMesh(scene); + // Rebuild all navmeshs after apply changes on navigation + for (auto scene : Level::Scenes) + { + Navigation::BuildNavMesh(scene); + } } #endif } From 057d1fbcc681ecf0191006624261eb4221e4aaee Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 10 Nov 2023 15:07:37 +0100 Subject: [PATCH 33/57] Fix unpacking `Float3` and other inbuilt `Variant` types via Unpack node in Visual Script #1903 --- Source/Engine/Core/Types/Variant.cpp | 57 ++++++++++++++++++++++++++ Source/Engine/Core/Types/Variant.h | 3 ++ Source/Engine/Visject/VisjectGraph.cpp | 6 ++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index df044b299..952e648e6 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -2821,7 +2821,10 @@ void Variant::Inline() type = VariantType::Types::Vector4; } if (type != VariantType::Null) + { + ASSERT(sizeof(data) >= AsBlob.Length); Platform::MemoryCopy(data, AsBlob.Data, AsBlob.Length); + } } if (type != VariantType::Null) { @@ -2912,6 +2915,60 @@ void Variant::Inline() } } +void Variant::InvertInline() +{ + byte data[sizeof(Matrix)]; + switch (Type.Type) + { + case VariantType::Bool: + case VariantType::Int: + case VariantType::Uint: + case VariantType::Int64: + case VariantType::Uint64: + case VariantType::Float: + case VariantType::Double: + case VariantType::Pointer: + case VariantType::String: + case VariantType::Float2: + case VariantType::Float3: + case VariantType::Float4: + case VariantType::Color: +#if !USE_LARGE_WORLDS + case VariantType::BoundingSphere: + case VariantType::BoundingBox: + case VariantType::Ray: +#endif + case VariantType::Guid: + case VariantType::Quaternion: + case VariantType::Rectangle: + case VariantType::Int2: + case VariantType::Int3: + case VariantType::Int4: + case VariantType::Int16: + case VariantType::Uint16: + case VariantType::Double2: + case VariantType::Double3: + case VariantType::Double4: + static_assert(sizeof(data) >= sizeof(AsData), "Invalid memory size."); + Platform::MemoryCopy(data, AsData, sizeof(AsData)); + break; +#if USE_LARGE_WORLDS + case VariantType::BoundingSphere: + case VariantType::BoundingBox: + case VariantType::Ray: +#endif + case VariantType::Transform: + case VariantType::Matrix: + ASSERT(sizeof(data) >= AsBlob.Length); + Platform::MemoryCopy(data, AsBlob.Data, AsBlob.Length); + break; + default: + return; // Not used + } + SetType(VariantType(VariantType::Structure, InBuiltTypesTypeNames[Type.Type])); + CopyStructure(data); +} + Variant Variant::NewValue(const StringAnsiView& typeName) { Variant v; diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h index 8cc1e133b..adb49249f 100644 --- a/Source/Engine/Core/Types/Variant.h +++ b/Source/Engine/Core/Types/Variant.h @@ -372,6 +372,9 @@ public: // Inlines potential value type into in-built format (eg. Vector3 stored as Structure, or String stored as ManagedObject). void Inline(); + // Inverts the inlined value from in-built format into generic storage (eg. Float3 from inlined format into Structure). + void InvertInline(); + // Allocates the Variant of the specific type (eg. structure or object or value). static Variant NewValue(const StringAnsiView& typeName); diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index 1cee5d46e..4e94473f6 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -685,7 +685,7 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value) case 36: { // Get value with structure data - const Variant structureValue = eatBox(node, node->GetBox(0)->FirstConnection()); + Variant structureValue = eatBox(node, node->GetBox(0)->FirstConnection()); if (!node->GetBox(0)->HasConnection()) return; @@ -741,7 +741,9 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value) return; } const ScriptingType& type = typeHandle.GetType(); - if (structureValue.Type.Type != VariantType::Structure || StringUtils::Compare(typeNameAnsi.Get(), structureValue.Type.TypeName) != 0) + 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) { OnError(node, box, String::Format(TEXT("Cannot unpack value of type {0} to structure of type {1}"), structureValue.Type, typeName)); return; From dc7170c51e0e8d6d0d6d505197052b47033a2798 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 10 Nov 2023 21:32:18 +0200 Subject: [PATCH 34/57] Expose Freetype font style flags in `FontAsset` --- Source/Engine/Render2D/FontAsset.cpp | 10 ++++++++++ Source/Engine/Render2D/FontAsset.h | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/Source/Engine/Render2D/FontAsset.cpp b/Source/Engine/Render2D/FontAsset.cpp index f000eb3c6..a477e5f4d 100644 --- a/Source/Engine/Render2D/FontAsset.cpp +++ b/Source/Engine/Render2D/FontAsset.cpp @@ -87,6 +87,16 @@ bool FontAsset::Init() return error; } +FontFlags FontAsset::GetStyle() const +{ + FontFlags flags = FontFlags::None; + if ((_face->style_flags & FT_STYLE_FLAG_ITALIC) != 0) + flags |= FontFlags::Italic; + if ((_face->style_flags & FT_STYLE_FLAG_BOLD) != 0) + flags |= FontFlags::Bold; + return flags; +} + void FontAsset::SetOptions(const FontOptions& value) { _options = value; diff --git a/Source/Engine/Render2D/FontAsset.h b/Source/Engine/Render2D/FontAsset.h index 17839d49b..4dea84a5b 100644 --- a/Source/Engine/Render2D/FontAsset.h +++ b/Source/Engine/Render2D/FontAsset.h @@ -128,6 +128,11 @@ public: return _options; } + /// + /// Gets the font style flags. + /// + API_PROPERTY() FontFlags GetStyle() const; + /// /// Sets the font options. /// From ddaa5f9161fceb4c1120a49fa434e8668605c919 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 11 Nov 2023 14:47:20 +0100 Subject: [PATCH 35/57] Fix regression in Custom Editor UI from 74bcf7d9e598be82635086570555b41cd85b9988 #1616 #1911 --- Source/Editor/CustomEditors/CustomEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index 32111e51c..9de330213 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -157,7 +157,7 @@ namespace FlaxEditor.CustomEditors var values = _values; var presenter = _presenter; var layout = _layout; - if (layout.Editors.Count != 1) + if (layout.Editors.Count > 1) { // There are more editors using the same layout so rebuild parent editor to prevent removing others editors _parent?.RebuildLayout(); From be90f47585da048cb243d830c906f89951a99bfd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 11 Nov 2023 15:37:12 +0100 Subject: [PATCH 36/57] Documentation improvements from https://github.com/FlaxEngine/FlaxDocs/pull/125 --- Source/Engine/Streaming/TextureGroup.h | 8 ++--- Source/Engine/Tools/ModelTool/ModelTool.h | 30 +++++++++---------- Source/Engine/Tools/TextureTool/TextureTool.h | 10 +++---- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Source/Engine/Streaming/TextureGroup.h b/Source/Engine/Streaming/TextureGroup.h index 42b308baf..c7db3f14c 100644 --- a/Source/Engine/Streaming/TextureGroup.h +++ b/Source/Engine/Streaming/TextureGroup.h @@ -36,7 +36,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextureGroup); int32 MaxAnisotropy = 16; /// - /// The quality scale factor applied to textures in this group. Can be used to increase or decrease textures resolution. In range 0-1 where 0 means lowest quality, 1 means full quality. + /// The quality scale factor applied to textures in this group. Can be used to increase or decrease textures resolution. In the range 0-1 where 0 means lowest quality, 1 means full quality. /// API_FIELD(Attributes="EditorOrder(20), Limit(0, 1)") float Quality = 1.0f; @@ -60,20 +60,20 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextureGroup); int32 MipLevelsMin = 0; /// - /// The maximum amount of loaded mip levels for textures in this group. Defines the maximum amount of the mips that can be loaded. Overriden per-platform. Lower values reduce textures quality and improve performance. + /// The maximum amount of loaded mip levels for textures in this group. Defines the maximum amount of mips that can be loaded. Overriden per-platform. Lower values reduce texture quality and improve performance. /// API_FIELD(Attributes="EditorOrder(40), Limit(1, 14)") int32 MipLevelsMax = 14; /// - /// The loaded mip levels bias for textures in this group. Can be used to increase or decrease quality of the streaming for textures in this group (eg. bump up the quality during cinematic sequence). + /// The loaded mip levels bias for textures in this group. Can be used to increase or decrease the quality of streaming for textures in this group (eg. bump up the quality during cinematic sequence). /// API_FIELD(Attributes="EditorOrder(50), Limit(-14, 14)") int32 MipLevelsBias = 0; #if USE_EDITOR /// - /// The per-platform maximum amount of mip levels for textures in this group. Can be used to strip textures quality when cooking game for a target platform. + /// The per-platform maximum amount of mip levels for textures in this group. Can be used to strip textures quality when cooking the game for a target platform. /// API_FIELD(Attributes="EditorOrder(50)") Dictionary MipLevelsMaxPerPlatform; diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index c7876e826..ac8cb231f 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -228,25 +228,25 @@ public: public: // Geometry - // Enable model normal vectors recalculating. + // Enable model normal vectors re-calculating. API_FIELD(Attributes="EditorOrder(20), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") bool CalculateNormals = false; - // Specifies the maximum angle (in degrees) that may be between two face normals at the same vertex position that their are smoothed together. The default value is 175. + // Specifies the maximum angle (in degrees) that may be between two face normals at the same vertex position before they are smoothed together. The default value is 175. API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowSmoothingNormalsAngle)), Limit(0, 175, 0.1f)") float SmoothingNormalsAngle = 175.0f; // If checked, the imported normal vectors of the mesh will be flipped (scaled by -1). API_FIELD(Attributes="EditorOrder(35), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") bool FlipNormals = false; - // Enable model tangent vectors recalculating. + // Enable model tangent vectors re-calculating. API_FIELD(Attributes="EditorOrder(40), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") bool CalculateTangents = false; - // Specifies the maximum angle (in degrees) that may be between two vertex tangents that their tangents and bi-tangents are smoothed. The default value is 45. + // Specifies the maximum angle (in degrees) that may be between two vertex tangents before their tangents and bi-tangents are smoothed. The default value is 45. API_FIELD(Attributes="EditorOrder(45), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowSmoothingTangentsAngle)), Limit(0, 45, 0.1f)") float SmoothingTangentsAngle = 45.0f; // Enable/disable meshes geometry optimization. API_FIELD(Attributes="EditorOrder(50), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") bool OptimizeMeshes = true; - // Enable/disable geometry merge for meshes with the same materials. + // Enable/disable geometry merge for meshes with the same materials. Index buffer will be reordered to improve performance and other modifications will be applied. However, importing time will be increased. API_FIELD(Attributes="EditorOrder(60), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") bool MergeMeshes = true; // Enable/disable importing meshes Level of Details. @@ -258,16 +258,16 @@ public: // Enable/disable importing blend shapes (morph targets). API_FIELD(Attributes="EditorOrder(85), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowSkinnedModel))") bool ImportBlendShapes = false; - // Enable skeleton bones offset matrices recalculating. + // Enable skeleton bones offset matrices re-calculating. API_FIELD(Attributes="EditorOrder(86), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowSkinnedModel))") bool CalculateBoneOffsetMatrices = false; // The lightmap UVs source. API_FIELD(Attributes="EditorOrder(90), EditorDisplay(\"Geometry\", \"Lightmap UVs Source\"), VisibleIf(nameof(ShowModel))") ModelLightmapUVsSource LightmapUVsSource = ModelLightmapUVsSource::Disable; - // If specified, all meshes which name starts with this prefix will be imported as a separate collision data (excluded used for rendering). + // If specified, all meshes that name starts with this prefix in the name will be imported as a separate collision data asset (excluded used for rendering). API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") String CollisionMeshesPrefix = TEXT(""); - // The type of collision that should be generated if has collision prefix specified. + // The type of collision that should be generated if the mesh has a collision prefix specified. API_FIELD(Attributes = "EditorOrder(105), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") CollisionDataType CollisionType = CollisionDataType::TriangleMesh; @@ -291,19 +291,19 @@ public: public: // Animation - // Imported animation duration mode. Can use the original value or overriden by settings. + // Imported animation duration mode. Can use the original value or be overriden by settings. API_FIELD(Attributes="EditorOrder(1000), EditorDisplay(\"Animation\"), VisibleIf(nameof(ShowAnimation))") AnimationDuration Duration = AnimationDuration::Imported; // Imported animation first/last frame index. Used only if Duration mode is set to Custom. API_FIELD(Attributes="EditorOrder(1010), EditorDisplay(\"Animation\"), VisibleIf(nameof(ShowFramesRange)), Limit(0)") Float2 FramesRange = Float2::Zero; - // The imported animation default frame rate. Can specify the default frames per second amount for imported animation. If value is 0 then the original animation frame rate will be used. + // The imported animation default frame rate. Can specify the default frames per second amount for imported animations. If the value is 0 then the original animation frame rate will be used. API_FIELD(Attributes="EditorOrder(1020), EditorDisplay(\"Animation\"), VisibleIf(nameof(ShowAnimation)), Limit(0, 1000, 0.01f)") float DefaultFrameRate = 0.0f; // The imported animation sampling rate. If value is 0 then the original animation speed will be used. API_FIELD(Attributes="EditorOrder(1030), EditorDisplay(\"Animation\"), VisibleIf(nameof(ShowAnimation)), Limit(0, 1000, 0.01f)") float SamplingRate = 0.0f; - // The imported animation will have removed tracks with no keyframes or unspecified data. + // The imported animation will have tracks with no keyframes or unspecified data removed. API_FIELD(Attributes="EditorOrder(1040), EditorDisplay(\"Animation\"), VisibleIf(nameof(ShowAnimation))") bool SkipEmptyCurves = true; // The imported animation channels will be optimized to remove redundant keyframes. @@ -348,7 +348,7 @@ public: // If checked, the importer will create the model's materials as instances of a base material. API_FIELD(Attributes = "EditorOrder(401), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterials))") bool ImportMaterialsAsInstances = false; - // The material to import the model's materials as an instance of. + // The material used as the base material that will be instanced as the imported model's material. API_FIELD(Attributes = "EditorOrder(402), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterialsAsInstances))") AssetReference InstanceToImportAs; // If checked, the importer will import texture files used by the model and any embedded texture resources. @@ -369,16 +369,16 @@ public: public: // Splitting - // If checked, the imported mesh/animations are splitted into separate assets. Used if ObjectIndex is set to -1. + // If checked, the imported mesh/animations are split into separate assets. Used if ObjectIndex is set to -1. API_FIELD(Attributes="EditorOrder(2000), EditorDisplay(\"Splitting\")") bool SplitObjects = false; - // The zero-based index for the mesh/animation clip to import. If the source file has more than one mesh/animation it can be used to pick a desire object. Default -1 imports all objects. + // The zero-based index for the mesh/animation clip to import. If the source file has more than one mesh/animation it can be used to pick a desired object. Default -1 imports all objects. API_FIELD(Attributes="EditorOrder(2010), EditorDisplay(\"Splitting\")") int32 ObjectIndex = -1; public: // Other - // If specified, will be used as sub-directory name for automatically imported sub assets such as textures and materials. Set to whitespace (single space) to import to the same directory. + // If specified, the specified folder will be used as sub-directory name for automatically imported sub assets such as textures and materials. Set to whitespace (single space) to import to the same directory. API_FIELD(Attributes="EditorOrder(3030), EditorDisplay(\"Other\")") String SubAssetFolder = TEXT(""); diff --git a/Source/Engine/Tools/TextureTool/TextureTool.h b/Source/Engine/Tools/TextureTool/TextureTool.h index 98ebf1d89..60e9df3af 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.h +++ b/Source/Engine/Tools/TextureTool/TextureTool.h @@ -57,11 +57,11 @@ API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API TextureTool API_FIELD(Attributes="EditorOrder(70)") bool FlipY = false; - // Texture size scale. Default is 1. + // Texture size scale. Allows increasing or decreasing the imported texture resolution. Default is 1. API_FIELD(Attributes="EditorOrder(80), Limit(0.0001f, 1000.0f, 0.01f)") float Scale = 1.0f; - // Maximum size of the texture (for both width and height). Higher resolution textures will be resized during importing process. + // Maximum size of the texture (for both width and height). Higher resolution textures will be resized during importing process. Used to clip textures that are too big. API_FIELD(Attributes="HideInEditor") int32 MaxSize = 8192; @@ -69,11 +69,11 @@ API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API TextureTool API_FIELD(Attributes="EditorOrder(100)") bool Resize = false; - // The width of the imported texture. If Resize property is set to true then texture will be resized during the import to this value. Otherwise it will be ignored. + // The width of the imported texture. If Resize property is set to true then texture will be resized during the import to this value during the import, otherwise it will be ignored. API_FIELD(Attributes="HideInEditor") int32 SizeX = 1024; - // The height of the imported texture. If Resize property is set to true then texture will be resized during the import to this value. Otherwise it will be ignored. + // The height of the imported texture. If Resize property is set to true then texture will be resized during the import to this value during the import, otherwise it will be ignored. API_FIELD(Attributes="HideInEditor") int32 SizeY = 1024; @@ -85,7 +85,7 @@ API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API TextureTool API_FIELD(Attributes="EditorOrder(210), VisibleIf(\"PreserveAlphaCoverage\")") float PreserveAlphaCoverageReference = 0.5f; - // Texture group for streaming (negative if unused). See Streaming Settings. + // The texture group for streaming (negative if unused). See Streaming Settings. API_FIELD(Attributes="EditorOrder(300), CustomEditorAlias(\"FlaxEditor.CustomEditors.Dedicated.TextureGroupEditor\")") int32 TextureGroup = -1; From 31ce41c5a4ee8469d3895a68c0b4b44abf43f8cd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 11 Nov 2023 17:57:54 +0100 Subject: [PATCH 37/57] Fix marking scene as dirty when creating prefab from existing actor #1916 --- Source/Editor/Modules/PrefabsModule.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Modules/PrefabsModule.cs b/Source/Editor/Modules/PrefabsModule.cs index adb5f7685..6abf8e45c 100644 --- a/Source/Editor/Modules/PrefabsModule.cs +++ b/Source/Editor/Modules/PrefabsModule.cs @@ -124,6 +124,7 @@ namespace FlaxEditor.Modules if (!Editor.StateMachine.CurrentState.CanEditScene) return; undo = Editor.Undo; + Editor.Scene.MarkSceneEdited(actor.Scene); } // Record undo for prefab creating (backend links the target instance with the prefab) From cc1e98db3cdd06bb77c8a3fef52024cb3053503c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 12 Nov 2023 01:06:06 +0100 Subject: [PATCH 38/57] Bump up build number --- Flax.flaxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index a3157a032..b3de36d70 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 7, "Revision": 0, - "Build": 6404 + "Build": 6405 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.", From 0f14672e3bd22d82699c434d2e0bf2f24815c8fc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 13 Nov 2023 09:45:33 +0100 Subject: [PATCH 39/57] Codestyle formatting and fixes for #1888 --- .../Handlers/CompileScriptsProgress.cs | 9 -- Source/Editor/States/ReloadingScriptsState.cs | 1 - Source/Engine/Engine/NativeInterop.cs | 8 +- .../ExtendedSerializationBinder.cs | 83 +++++++++---------- Source/Engine/Serialization/JsonSerializer.cs | 60 ++++++++------ 5 files changed, 80 insertions(+), 81 deletions(-) diff --git a/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs b/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs index 310612a73..cbb383b4c 100644 --- a/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs +++ b/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs @@ -25,7 +25,6 @@ namespace FlaxEditor.Progress.Handlers ScriptsBuilder.ScriptsReloadCalled += () => OnUpdate(0.8f, "Reloading scripts..."); ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd; - ScriptsBuilder.ScriptsReload += OnScriptsReload; } private void OnScriptsReloadBegin() @@ -38,14 +37,6 @@ namespace FlaxEditor.Progress.Handlers Editor.Instance.Scene.ClearRefsToSceneObjects(true); } - private void OnScriptsReload() - { -#if !USE_NETCORE - // Clear types cache - Newtonsoft.Json.JsonSerializer.ClearCache(); -#endif - } - private void OnCompilationFailed() { OnFail("Scripts compilation failed"); diff --git a/Source/Editor/States/ReloadingScriptsState.cs b/Source/Editor/States/ReloadingScriptsState.cs index 4b8866202..d9024d5c8 100644 --- a/Source/Editor/States/ReloadingScriptsState.cs +++ b/Source/Editor/States/ReloadingScriptsState.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEngine; -using FlaxEditor.Utilities; using FlaxEngine.Utilities; namespace FlaxEditor.States diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index b4635e0b4..3f19f5951 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -297,9 +297,10 @@ namespace FlaxEngine.Interop return type; - /// Resolve the type by manually checking every scripting assembly - static Type ResolveSlow(string typeName) { - foreach (var assembly in scriptingAssemblyLoadContext.Assemblies) { + static Type ResolveSlow(string typeName) + { + foreach (var assembly in scriptingAssemblyLoadContext.Assemblies) + { var type = assembly.GetType(typeName); if (type != null) return type; @@ -307,7 +308,6 @@ namespace FlaxEngine.Interop return null; } - /// Resolve the assembly by name static Assembly ResolveAssembly(AssemblyName name) => ResolveScriptingAssemblyByName(name, allowPartial: false); } diff --git a/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs index b4f0173ab..f239a3384 100644 --- a/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs +++ b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedSerializationBinder.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Globalization; -using System.Linq; using System.Reflection; using System.Runtime.Serialization; using FlaxEngine.Interop; @@ -22,7 +20,6 @@ namespace FlaxEngine.Json.JsonCustomSerializers private ConcurrentDictionary _typeCache; private Func _resolveType; - /// Clear the cache /// Should be cleared on scripting domain reload to avoid out of date types participating in dynamic type resolution public void ResetCache() @@ -51,14 +48,19 @@ namespace FlaxEngine.Json.JsonCustomSerializers Type ResolveType(TypeKey key) { Type? type = null; - if (key.assemblyName is null) { // No assembly name, attempt to find globally + if (key.assemblyName is null) + { + // No assembly name, attempt to find globally type = FindTypeGlobal(key.typeName); } - if (type is null && key.assemblyName is not null) { // Type not found yet, but we have assembly name + if (type is null && key.assemblyName is not null) + { + // Type not found yet, but we have assembly name var assembly = ResolveAssembly(new(key.assemblyName)); - type = FindTypeInAssembly(key.typeName, assembly); // We have assembly, attempt to load from assembly + // We have assembly, attempt to load from assembly + type = FindTypeInAssembly(key.typeName, assembly); } //if (type is null) @@ -74,18 +76,13 @@ namespace FlaxEngine.Json.JsonCustomSerializers Assembly ResolveAssembly(AssemblyName name) { Assembly? assembly = null; - assembly = FindScriptingAssembly(name); // Attempt to find in scripting assemblies - if (assembly is null) assembly = FindLoadAssembly(name); // Attempt to load - if (assembly is null) assembly = FindDomainAssembly(name); // Attempt to find in the current domain - if (assembly is null) throw MakeAsmResolutionException(name.FullName); // Assembly failed to resolve - return assembly; } @@ -98,15 +95,14 @@ namespace FlaxEngine.Json.JsonCustomSerializers /// Attempt to find the assembly in the current domain Assembly? FindDomainAssembly(AssemblyName assemblyName) { - var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToArray(); - - foreach (Assembly assembly in assemblies) { // Looking in domain may be necessary (in case of anon dynamic assembly for example) + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly assembly in assemblies) + { + // Looking in domain may be necessary (in case of anon dynamic assembly for example) var curName = assembly.GetName(); - if (curName == assemblyName || curName.Name == assemblyName.Name) return assembly; } - return null; } @@ -114,24 +110,20 @@ namespace FlaxEngine.Json.JsonCustomSerializers Assembly? FindLoadAssembly(AssemblyName assemblyName) { Assembly? assembly = null; - assembly = Assembly.Load(assemblyName); - - if (assembly is null) + if (assembly is null && assemblyName.Name is not null) +#pragma warning disable CS0618 // Type or member is obsolete assembly = Assembly.LoadWithPartialName(assemblyName.Name); // Copying behavior of DefaultSerializationBinder - +#pragma warning restore CS0618 // Type or member is obsolete return assembly; } - /// Attempt to find a type in a specified assembly Type? FindTypeInAssembly(string typeName, Assembly assembly) { var type = assembly.GetType(typeName); // Attempt to load directly - if (type is null && typeName.IndexOf('`') >= 0) // Attempt failed, but name has generic variant tick, try resolving generic manually type = FindTypeGeneric(typeName, assembly); - return type; } @@ -146,7 +138,6 @@ namespace FlaxEngine.Json.JsonCustomSerializers { return _typeCache.GetOrAdd(key, _resolveType); } - /********************************************* ** Below code is adapted from Newtonsoft.Json @@ -157,26 +148,33 @@ namespace FlaxEngine.Json.JsonCustomSerializers { Type? type = null; int openBracketIndex = typeName.IndexOf('[', StringComparison.Ordinal); - if (openBracketIndex >= 0) { + if (openBracketIndex >= 0) + { string genericTypeDefName = typeName.Substring(0, openBracketIndex); // Find the unspecialized type Type? genericTypeDef = assembly.GetType(genericTypeDefName); - if (genericTypeDef != null) { + if (genericTypeDef != null) + { List genericTypeArguments = new List(); // Recursively resolve the arguments int scope = 0; int typeArgStartIndex = 0; int endIndex = typeName.Length - 1; - for (int i = openBracketIndex + 1; i < endIndex; ++i) { + for (int i = openBracketIndex + 1; i < endIndex; ++i) + { char current = typeName[i]; - switch (current) { + switch (current) + { case '[': - if (scope == 0) { + if (scope == 0) + { typeArgStartIndex = i + 1; } ++scope; break; case ']': --scope; - if (scope == 0) { // All arguments resolved, compose our type + if (scope == 0) + { + // All arguments resolved, compose our type string typeArgAssemblyQualifiedName = typeName.Substring(typeArgStartIndex, i - typeArgStartIndex); TypeKey typeNameKey = SplitFullyQualifiedTypeName(typeArgAssemblyQualifiedName); @@ -189,7 +187,6 @@ namespace FlaxEngine.Json.JsonCustomSerializers type = genericTypeDef.MakeGenericType(genericTypeArguments.ToArray()); } } - return type; } @@ -197,18 +194,17 @@ namespace FlaxEngine.Json.JsonCustomSerializers private static TypeKey SplitFullyQualifiedTypeName(string fullyQualifiedTypeName) { int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName); - - ReadOnlySpan typeName; - ReadOnlySpan assemblyName; - - if (assemblyDelimiterIndex != null) { + ReadOnlySpan typeName, assemblyName; + if (assemblyDelimiterIndex != null) + { typeName = fullyQualifiedTypeName.AsSpan().Slice(0, assemblyDelimiterIndex ?? 0); assemblyName = fullyQualifiedTypeName.AsSpan().Slice((assemblyDelimiterIndex ?? 0) + 1); - } else { + } + else + { typeName = fullyQualifiedTypeName; assemblyName = null; } - return new(new(assemblyName), new(typeName)); } @@ -218,9 +214,11 @@ namespace FlaxEngine.Json.JsonCustomSerializers // we need to get the first comma following all surrounded in brackets because of generic types // e.g. System.Collections.Generic.Dictionary`2[[System.String, mscorlib,Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 int scope = 0; - for (int i = 0; i < fullyQualifiedTypeName.Length; i++) { + for (int i = 0; i < fullyQualifiedTypeName.Length; i++) + { char current = fullyQualifiedTypeName[i]; - switch (current) { + switch (current) + { case '[': scope++; break; @@ -228,7 +226,8 @@ namespace FlaxEngine.Json.JsonCustomSerializers scope--; break; case ',': - if (scope == 0) { + if (scope == 0) + { return i; } break; @@ -243,7 +242,7 @@ namespace FlaxEngine.Json.JsonCustomSerializers { return new($"Could not load assembly '{asmName}'."); } - + private static JsonSerializationException MakeTypeResolutionException(string? asmName, string typeName) { if (asmName is null) diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index 2eb9eec99..a5d6e0771 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -19,7 +19,7 @@ namespace FlaxEngine.Json { internal class SerializerCache { - public readonly JsonSerializerSettings settings; + public readonly JsonSerializerSettings JsonSettings; public Newtonsoft.Json.JsonSerializer JsonSerializer; public StringBuilder StringBuilder; public StringWriter StringWriter; @@ -29,23 +29,27 @@ namespace FlaxEngine.Json public StreamReader Reader; public bool IsWriting; public bool IsReading; - public uint cacheVersion; +#if FLAX_EDITOR + public uint CacheVersion; +#endif public unsafe SerializerCache(JsonSerializerSettings settings) { - this.settings = settings; - + JsonSettings = settings; StringBuilder = new StringBuilder(256); StringWriter = new StringWriter(StringBuilder, CultureInfo.InvariantCulture); MemoryStream = new UnmanagedMemoryStream((byte*)0, 0); - lock (cacheSyncRoot) +#if FLAX_EDITOR + lock (CurrentCacheSyncRoot) +#endif { BuildSerializer(); BuildRead(); BuildWrite(); - - cacheVersion = currentCacheVersion; +#if FLAX_EDITOR + CacheVersion = Json.JsonSerializer.CurrentCacheVersion; +#endif } } @@ -57,7 +61,6 @@ namespace FlaxEngine.Json if (IsReading) BuildRead(); - IsWriting = false; IsReading = true; } @@ -88,29 +91,30 @@ namespace FlaxEngine.Json /// Check that the cache is up to date, rebuild it if it isn't private void CheckCacheVersionRebuild() { - var cCV = currentCacheVersion; - if (cacheVersion == cCV) +#if FLAX_EDITOR + var version = Json.JsonSerializer.CurrentCacheVersion; + if (CacheVersion == version) return; - lock (cacheSyncRoot) + lock (CurrentCacheSyncRoot) { - cCV = currentCacheVersion; - if (cacheVersion == cCV) + version = Json.JsonSerializer.CurrentCacheVersion; + if (CacheVersion == version) return; BuildSerializer(); - BuildRead(); BuildWrite(); - cacheVersion = cCV; + CacheVersion = version; } +#endif } /// Builds the serializer private void BuildSerializer() { - JsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(settings); + JsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(Settings); JsonSerializer.Formatting = Formatting.Indented; JsonSerializer.ReferenceLoopHandling = ReferenceLoopHandling.Serialize; } @@ -125,7 +129,8 @@ namespace FlaxEngine.Json private void BuildWrite() { SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer); - JsonWriter = new JsonTextWriter(StringWriter) { + JsonWriter = new JsonTextWriter(StringWriter) + { IndentChar = '\t', Indentation = 1, Formatting = JsonSerializer.Formatting, @@ -143,24 +148,24 @@ namespace FlaxEngine.Json internal static JsonSerializerSettings SettingsManagedOnly = CreateDefaultSettings(true); internal static ExtendedSerializationBinder SerializationBinder; internal static FlaxObjectConverter ObjectConverter; - internal static ThreadLocal Current = new ThreadLocal(); internal static ThreadLocal Cache = new ThreadLocal(() => new SerializerCache(Settings)); internal static ThreadLocal CacheManagedOnly = new ThreadLocal(() => new SerializerCache(SettingsManagedOnly)); internal static ThreadLocal CachedGuidBuffer = new ThreadLocal(() => Marshal.AllocHGlobal(32 * sizeof(char)), true); internal static string CachedGuidDigits = "0123456789abcdef"; +#if FLAX_EDITOR /// The version of the cache, used to check that a cache is not out of date - internal static uint currentCacheVersion = 0; + internal static uint CurrentCacheVersion = 0; + /// Used to synchronize cache operations such as rebuild, and - internal static readonly object cacheSyncRoot = new(); + internal static readonly object CurrentCacheSyncRoot = new(); +#endif internal static JsonSerializerSettings CreateDefaultSettings(bool isManagedOnly) { //Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals = ValueEquals; - if (SerializationBinder is null) SerializationBinder = new(); - var settings = new JsonSerializerSettings { ContractResolver = new ExtendedDefaultContractResolver(isManagedOnly), @@ -185,17 +190,22 @@ namespace FlaxEngine.Json return settings; } - /// Called to reset the serialization cache +#if FLAX_EDITOR + /// Resets the serialization cache. internal static void ResetCache() { - lock (cacheSyncRoot) + lock (CurrentCacheSyncRoot) { - unchecked { currentCacheVersion++; } + unchecked + { + CurrentCacheVersion++; + } Newtonsoft.Json.JsonSerializer.ClearCache(); SerializationBinder.ResetCache(); } } +#endif internal static void Dispose() { From 930b1b978cad606c4db54230015fd0cd3f07d375 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 13 Nov 2023 10:25:46 +0100 Subject: [PATCH 40/57] Fix incorrect spline length calculation if first point is not at spline origin #1876 --- Source/Engine/Level/Actors/Spline.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 194dbd9a9..df93ea840 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -152,6 +152,11 @@ float Spline::GetSplineLength() const const int32 slices = 20; const float step = 1.0f / (float)slices; Vector3 prevPoint = Vector3::Zero; + if (Curve.GetKeyframes().Count() != 0) + { + const auto& a = Curve[0]; + prevPoint = a.Value.Translation * _transform.Scale; + } for (int32 i = 1; i < Curve.GetKeyframes().Count(); i++) { const auto& a = Curve[i - 1]; From 418918920e3f804c5a7bb38e8d506bf7105a14b9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 13 Nov 2023 11:38:37 +0100 Subject: [PATCH 41/57] Add `GetSplineSegmentLength` to get spline segment length #1879 --- Source/Engine/Level/Actors/Spline.cpp | 35 +++++++++++++++++++++++++-- Source/Engine/Level/Actors/Spline.h | 7 ++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index df93ea840..dfe7c2be8 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -149,8 +149,8 @@ float Spline::GetSplineDuration() const float Spline::GetSplineLength() const { float sum = 0.0f; - const int32 slices = 20; - const float step = 1.0f / (float)slices; + constexpr int32 slices = 20; + constexpr float step = 1.0f / (float)slices; Vector3 prevPoint = Vector3::Zero; if (Curve.GetKeyframes().Count() != 0) { @@ -181,6 +181,37 @@ float Spline::GetSplineLength() const return Math::Sqrt(sum); } +float Spline::GetSplineSegmentLength(int32 index) const +{ + if (index == 0) + return 0.0f; + CHECK_RETURN(index > 0 && index < GetSplinePointsCount(), 0.0f); + float sum = 0.0f; + constexpr int32 slices = 20; + constexpr float step = 1.0f / (float)slices; + const auto& a = Curve[index - 1]; + const auto& b = Curve[index]; + Vector3 startPoint = a.Value.Translation * _transform.Scale; + { + const float length = Math::Abs(b.Time - a.Time); + Vector3 leftTangent, rightTangent; + AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent); + AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent); + + // TODO: implement sth more analytical than brute-force solution + for (int32 slice = 0; slice < slices; slice++) + { + const float t = (float)slice * step; + Vector3 pos; + AnimationUtils::Bezier(a.Value.Translation, leftTangent, rightTangent, b.Value.Translation, t, pos); + pos *= _transform.Scale; + sum += (float)Vector3::DistanceSquared(pos, startPoint); + startPoint = pos; + } + } + return Math::Sqrt(sum); +} + float Spline::GetSplineTime(int32 index) const { CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), 0.0f) diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index cd022890b..4a81ee186 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -167,6 +167,13 @@ public: /// API_PROPERTY() float GetSplineLength() const; + /// + /// Gets the length of the spline segment (distance between pair of two points). + /// + /// The index of the segment end index. Zero-based, smaller than GetSplinePointsCount(). + /// The spline segment length. + API_FUNCTION() float GetSplineSegmentLength(int32 index) const; + /// /// Gets the time of the spline keyframe. /// From 422fb34c695ddff26275254103fd6437230dd190 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 13 Nov 2023 14:41:33 +0100 Subject: [PATCH 42/57] Rename `NumberOfItemsToShow` to `ShowMaxItemsCount` #1826 --- Source/Engine/UI/GUI/Common/Dropdown.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 92067398d..ecca2978f 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -258,10 +258,10 @@ namespace FlaxEngine.GUI public bool ShowAllItems { get; set; } = true; /// - /// Gets or sets the number of items to show. Only used if ShowAllItems is false. + /// Gets or sets the maximum number of items to show at once. Only used if ShowAllItems is false. /// [EditorOrder(4), VisibleIf(nameof(ShowAllItems), true), Limit(1), Tooltip("The number of items to show in the drop down.")] - public int NumberOfItemsToShow { get; set; } = 5; + public int ShowMaxItemsCount { get; set; } = 5; /// /// Event fired when selected index gets changed. @@ -527,14 +527,14 @@ namespace FlaxEngine.GUI } } - if (ShowAllItems || _items.Count < NumberOfItemsToShow) + if (ShowAllItems || _items.Count < ShowMaxItemsCount) { popup.Size = new Float2(itemsWidth, height); panel.Size = popup.Size; } else { - popup.Size = new Float2(itemsWidth, (itemsHeight + container.Spacing) * NumberOfItemsToShow); + popup.Size = new Float2(itemsWidth, (itemsHeight + container.Spacing) * ShowMaxItemsCount); panel.Size = popup.Size; } From 46f82aabcdc64162b35489bcc0311de73f6fba7e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 13 Nov 2023 14:48:40 +0100 Subject: [PATCH 43/57] Fix ui navigation regression from 2f9343c2365c1ca6cbd9ad04e2bc0505295a85f4 --- Source/Engine/UI/GUI/CanvasRootControl.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/Engine/UI/GUI/CanvasRootControl.cs b/Source/Engine/UI/GUI/CanvasRootControl.cs index b2ea9aaa0..54ad75295 100644 --- a/Source/Engine/UI/GUI/CanvasRootControl.cs +++ b/Source/Engine/UI/GUI/CanvasRootControl.cs @@ -198,8 +198,12 @@ namespace FlaxEngine.GUI /// public override void Update(float deltaTime) { - // UI navigation + // Update navigation if (SkipEvents) + { + _navigationHeldTimeUp = _navigationHeldTimeDown = _navigationHeldTimeLeft = _navigationHeldTimeRight = 0; + _navigationRateTimeUp = _navigationRateTimeDown = _navigationRateTimeLeft = _navigationRateTimeRight = 0; + } { UpdateNavigation(deltaTime, _canvas.NavigateUp.Name, NavDirection.Up, ref _navigationHeldTimeUp, ref _navigationRateTimeUp); UpdateNavigation(deltaTime, _canvas.NavigateDown.Name, NavDirection.Down, ref _navigationHeldTimeDown, ref _navigationRateTimeDown); @@ -207,11 +211,6 @@ namespace FlaxEngine.GUI UpdateNavigation(deltaTime, _canvas.NavigateRight.Name, NavDirection.Right, ref _navigationHeldTimeRight, ref _navigationRateTimeRight); UpdateNavigation(deltaTime, _canvas.NavigateSubmit.Name, ref _navigationHeldTimeSubmit, ref _navigationRateTimeSubmit, SubmitFocused); } - else - { - _navigationHeldTimeUp = _navigationHeldTimeDown = _navigationHeldTimeLeft = _navigationHeldTimeRight = 0; - _navigationRateTimeUp = _navigationRateTimeDown = _navigationRateTimeLeft = _navigationRateTimeRight = 0; - } base.Update(deltaTime); } From 97a28d443162c8fe0fd08fa27a1e61dfc2c26a5c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 13 Nov 2023 15:48:12 +0100 Subject: [PATCH 44/57] Add security lockers for managed typeinfo access --- Source/Engine/Scripting/BinaryModule.cpp | 1 + Source/Engine/Scripting/Runtime/DotNet.cpp | 26 +++++++++++++++++++--- Source/Engine/Scripting/Scripting.cpp | 1 + 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 679ead4e3..5bbaf8d0b 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -906,6 +906,7 @@ void ManagedBinaryModule::OnLoaded(MAssembly* assembly) #if !COMPILE_WITHOUT_CSHARP PROFILE_CPU(); ASSERT(ClassToTypeIndex.IsEmpty()); + ScopeLock lock(Locker); const auto& classes = assembly->GetClasses(); diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 200efa650..c4371736d 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -719,6 +719,7 @@ void GetAssemblyName(void* assemblyHandle, StringAnsi& name, StringAnsi& fullnam DEFINE_INTERNAL_CALL(void) NativeInterop_CreateClass(NativeClassDefinitions* managedClass, void* assemblyHandle) { + ScopeLock lock(BinaryModule::Locker); MAssembly* assembly = GetAssembly(assemblyHandle); if (assembly == nullptr) { @@ -732,7 +733,18 @@ DEFINE_INTERNAL_CALL(void) NativeInterop_CreateClass(NativeClassDefinitions* man MClass* klass = New(assembly, managedClass->typeHandle, managedClass->name, managedClass->fullname, managedClass->namespace_, managedClass->typeAttributes); if (assembly != nullptr) { - const_cast(assembly->GetClasses()).Add(klass->GetFullName(), klass); + auto& classes = const_cast(assembly->GetClasses()); + MClass* oldKlass; + if (classes.TryGet(klass->GetFullName(), oldKlass)) + { + LOG(Warning, "Class '{0}' was already added to assembly '{1}'", String(klass->GetFullName()), String(assembly->GetName())); + Delete(klass); + klass = oldKlass; + } + else + { + classes.Add(klass->GetFullName(), klass); + } } managedClass->nativePointer = klass; } @@ -873,7 +885,7 @@ MClass::MClass(const MAssembly* parentAssembly, void* handle, const char* name, static void* TypeIsEnumPtr = GetStaticMethodPointer(TEXT("TypeIsEnum")); _isEnum = CallStaticMethod(TypeIsEnumPtr, handle); - CachedClassHandles.Add(handle, this); + CachedClassHandles[handle] = this; } bool MAssembly::ResolveMissingFile(String& assemblyPath) const @@ -1551,6 +1563,7 @@ const Array& MProperty::GetAttributes() const MAssembly* GetAssembly(void* assemblyHandle) { + ScopeLock lock(BinaryModule::Locker); MAssembly* assembly; if (CachedAssemblyHandles.TryGet(assemblyHandle, assembly)) return assembly; @@ -1559,6 +1572,7 @@ MAssembly* GetAssembly(void* assemblyHandle) MClass* GetClass(MType* typeHandle) { + ScopeLock lock(BinaryModule::Locker); MClass* klass = nullptr; CachedClassHandles.TryGet(typeHandle, klass); return nullptr; @@ -1568,6 +1582,7 @@ MClass* GetOrCreateClass(MType* typeHandle) { if (!typeHandle) return nullptr; + ScopeLock lock(BinaryModule::Locker); MClass* klass; if (!CachedClassHandles.TryGet(typeHandle, klass)) { @@ -1579,7 +1594,12 @@ MClass* GetOrCreateClass(MType* typeHandle) klass = New(assembly, classInfo.typeHandle, classInfo.name, classInfo.fullname, classInfo.namespace_, classInfo.typeAttributes); if (assembly != nullptr) { - const_cast(assembly->GetClasses()).Add(klass->GetFullName(), klass); + auto& classes = const_cast(assembly->GetClasses()); + if (classes.ContainsKey(klass->GetFullName())) + { + LOG(Warning, "Class '{0}' was already added to assembly '{1}'", String(klass->GetFullName()), String(assembly->GetName())); + } + classes[klass->GetFullName()] = klass; } if (typeHandle != classInfo.typeHandle) diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index bdf9f60f5..e9dc0421c 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -436,6 +436,7 @@ bool Scripting::Load() PROFILE_CPU(); // Note: this action can be called from main thread (due to Mono problems with assemblies actions from other threads) ASSERT(IsInMainThread()); + ScopeLock lock(BinaryModule::Locker); #if USE_CSHARP // Load C# core assembly From 618273977c7bc2bba2dc061b2d02a8f8fca9d21c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 13 Nov 2023 17:17:05 +0100 Subject: [PATCH 45/57] Minor improvements to code style #1541 --- Source/Editor/GUI/AssetPicker.cs | 2 +- Source/Editor/GUI/Row.cs | 2 +- Source/Editor/GUI/Timeline/Timeline.cs | 9 +++++---- Source/Editor/Options/OptionsModule.cs | 10 +++------- Source/Editor/Options/ThemeOptions.cs | 7 +++++-- Source/Editor/Windows/Profiler/Assets.cs | 5 +++-- Source/Editor/Windows/Profiler/CPU.cs | 5 +++-- Source/Editor/Windows/Profiler/GPU.cs | 5 +++-- Source/Editor/Windows/Profiler/MemoryGPU.cs | 5 +++-- Source/Editor/Windows/Profiler/Network.cs | 5 +++-- Source/Editor/Windows/Profiler/SingleChart.cs | 6 +++--- 11 files changed, 33 insertions(+), 28 deletions(-) diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 7e7b9816f..84f58daf1 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -173,7 +173,7 @@ namespace FlaxEditor.GUI else { // No element selected - Render2D.FillRectangle(iconRect, Style.Current.BackgroundNormal); + Render2D.FillRectangle(iconRect, style.BackgroundNormal); Render2D.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); } diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index 81a9d459d..f6bd5b02a 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -98,7 +98,7 @@ namespace FlaxEditor.GUI rect.Width -= leftDepthMargin; Render2D.PushClip(rect); - Render2D.DrawText(style.FontMedium, text, rect, Style.Current.Foreground, column.CellAlignment, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, text, rect, style.Foreground, column.CellAlignment, TextAlignment.Center); Render2D.PopClip(); x += width; diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs index a32b35692..6b4d7bf4c 100644 --- a/Source/Editor/GUI/Timeline/Timeline.cs +++ b/Source/Editor/GUI/Timeline/Timeline.cs @@ -627,10 +627,11 @@ namespace FlaxEditor.GUI.Timeline Parent = this }; + var style = Style.Current; var headerTopArea = new ContainerControl { AutoFocus = false, - BackgroundColor = Style.Current.LightBackground, + BackgroundColor = style.LightBackground, AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, HeaderTopAreaHeight), Parent = _splitter.Panel1 @@ -683,7 +684,7 @@ namespace FlaxEditor.GUI.Timeline { AutoFocus = false, ClipChildren = false, - BackgroundColor = Style.Current.LightBackground, + BackgroundColor = style.LightBackground, AnchorPreset = AnchorPresets.HorizontalStretchBottom, Offsets = new Margin(0, 0, -playbackButtonsSize, playbackButtonsSize), Parent = _splitter.Panel1 @@ -845,7 +846,7 @@ namespace FlaxEditor.GUI.Timeline _timeIntervalsHeader = new TimeIntervalsHeader(this) { AutoFocus = false, - BackgroundColor = Style.Current.Background.RGBMultiplied(0.9f), + BackgroundColor = style.Background.RGBMultiplied(0.9f), AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, HeaderTopAreaHeight), Parent = _splitter.Panel2 @@ -854,7 +855,7 @@ namespace FlaxEditor.GUI.Timeline { AutoFocus = false, ClipChildren = false, - BackgroundColor = Style.Current.Background.RGBMultiplied(0.7f), + BackgroundColor = style.Background.RGBMultiplied(0.7f), AnchorPreset = AnchorPresets.StretchAll, Offsets = new Margin(0, 0, HeaderTopAreaHeight, 0), Parent = _splitter.Panel2 diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 51aa70257..1137e4c37 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -208,13 +208,13 @@ namespace FlaxEditor.Options // If a non-default style was chosen, switch to that style string styleName = themeOptions.SelectedStyle; - if (styleName != "Default" && styleName != "LightDefault" && themeOptions.Styles.TryGetValue(styleName, out var style) && style != null) + if (styleName != ThemeOptions.DefaultName && styleName != ThemeOptions.LightDefault && themeOptions.Styles.TryGetValue(styleName, out var style) && style != null) { Style.Current = style; } else { - if (styleName == "LightDefault") + if (styleName == ThemeOptions.LightDefault) { Style.Current = CreateLightStyle(); } @@ -231,7 +231,6 @@ namespace FlaxEditor.Options /// The style object. public Style CreateDefaultStyle() { - // Metro Style colors var options = Options; var style = new Style { @@ -282,7 +281,6 @@ namespace FlaxEditor.Options SharedTooltip = new Tooltip(), }; style.DragWindow = style.BackgroundSelected * 0.7f; - return style; } @@ -292,7 +290,6 @@ namespace FlaxEditor.Options /// The style object. public Style CreateLightStyle() { - // Metro Style colors var options = Options; var style = new Style { @@ -334,9 +331,8 @@ namespace FlaxEditor.Options Scale = Editor.Icons.Scale32, Scalar = Editor.Icons.Scalar32, - SharedTooltip = new Tooltip() + SharedTooltip = new Tooltip(), }; - return style; } diff --git a/Source/Editor/Options/ThemeOptions.cs b/Source/Editor/Options/ThemeOptions.cs index a033b34da..674e281af 100644 --- a/Source/Editor/Options/ThemeOptions.cs +++ b/Source/Editor/Options/ThemeOptions.cs @@ -15,6 +15,9 @@ namespace FlaxEditor.Options [CustomEditor(typeof(ThemeOptionsEditor))] public sealed class ThemeOptions { + internal const string DefaultName = "Default"; + internal const string LightDefault = "LightDefault"; + internal class ThemeOptionsEditor : Editor { private LabelElement _infoLabel; @@ -64,8 +67,8 @@ namespace FlaxEditor.Options { var themeOptions = (ThemeOptions)ParentEditor.Values[0]; var options = new string[themeOptions.Styles.Count + 2]; - options[0] = "Default"; - options[1] = "LightDefault"; + options[0] = DefaultName; + options[1] = LightDefault; int i = 0; foreach (var styleName in themeOptions.Styles.Keys) diff --git a/Source/Editor/Windows/Profiler/Assets.cs b/Source/Editor/Windows/Profiler/Assets.cs index b8b898246..536d65a74 100644 --- a/Source/Editor/Windows/Profiler/Assets.cs +++ b/Source/Editor/Windows/Profiler/Assets.cs @@ -62,8 +62,9 @@ namespace FlaxEditor.Windows.Profiler _memoryUsageChart.SelectedSampleChanged += OnSelectedSampleChanged; // Table - var headerColor = Style.Current.LightBackground; - var textColor = Style.Current.Foreground; + var style = Style.Current; + var headerColor = style.LightBackground; + var textColor = style.Foreground; _table = new Table { Columns = new[] diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs index df4d5b59b..0cbb3fa9b 100644 --- a/Source/Editor/Windows/Profiler/CPU.cs +++ b/Source/Editor/Windows/Profiler/CPU.cs @@ -92,8 +92,9 @@ namespace FlaxEditor.Windows.Profiler }; // Table - var headerColor = Style.Current.LightBackground; - var textColor = Style.Current.Foreground; + var style = Style.Current; + var headerColor = style.LightBackground; + var textColor = style.Foreground; _table = new Table { Columns = new[] diff --git a/Source/Editor/Windows/Profiler/GPU.cs b/Source/Editor/Windows/Profiler/GPU.cs index e6a93df72..d2c34d335 100644 --- a/Source/Editor/Windows/Profiler/GPU.cs +++ b/Source/Editor/Windows/Profiler/GPU.cs @@ -63,8 +63,9 @@ namespace FlaxEditor.Windows.Profiler }; // Table - var headerColor = Style.Current.LightBackground; - var textColor = Style.Current.Foreground; + var style = Style.Current; + var headerColor = style.LightBackground; + var textColor = style.Foreground; _table = new Table { Columns = new[] diff --git a/Source/Editor/Windows/Profiler/MemoryGPU.cs b/Source/Editor/Windows/Profiler/MemoryGPU.cs index e894ef0e2..e7c085362 100644 --- a/Source/Editor/Windows/Profiler/MemoryGPU.cs +++ b/Source/Editor/Windows/Profiler/MemoryGPU.cs @@ -63,8 +63,9 @@ namespace FlaxEditor.Windows.Profiler _memoryUsageChart.SelectedSampleChanged += OnSelectedSampleChanged; // Table - var headerColor = Style.Current.LightBackground; - var textColor = Style.Current.Foreground; + var style = Style.Current; + var headerColor = style.LightBackground; + var textColor = style.Foreground; _table = new Table { Columns = new[] diff --git a/Source/Editor/Windows/Profiler/Network.cs b/Source/Editor/Windows/Profiler/Network.cs index 669cf4a65..1ac9777c1 100644 --- a/Source/Editor/Windows/Profiler/Network.cs +++ b/Source/Editor/Windows/Profiler/Network.cs @@ -252,8 +252,9 @@ namespace FlaxEditor.Windows.Profiler private static Table InitTable(ContainerControl parent, string name) { - var headerColor = Style.Current.LightBackground; - var textColor = Style.Current.Foreground; + var style = Style.Current; + var headerColor = style.LightBackground; + var textColor = style.Foreground; var table = new Table { Columns = new[] diff --git a/Source/Editor/Windows/Profiler/SingleChart.cs b/Source/Editor/Windows/Profiler/SingleChart.cs index 29780150a..4f36692e5 100644 --- a/Source/Editor/Windows/Profiler/SingleChart.cs +++ b/Source/Editor/Windows/Profiler/SingleChart.cs @@ -105,7 +105,7 @@ namespace FlaxEditor.Windows.Profiler if (_selectedSampleIndex != -1) { float selectedX = Width - (_samples.Count - _selectedSampleIndex - 1) * PointsOffset; - Render2D.DrawLine(new Float2(selectedX, 0), new Float2(selectedX, chartHeight), Style.Current.Foreground, 1.5f); + Render2D.DrawLine(new Float2(selectedX, 0), new Float2(selectedX, chartHeight), style.Foreground, 1.5f); } int samplesInViewCount = Math.Min((int)(Width / PointsOffset), _samples.Count) - 1; @@ -138,8 +138,8 @@ namespace FlaxEditor.Windows.Profiler var headerRect = new Rectangle(0, chartHeight, Width, TitleHeight); var headerTextRect = new Rectangle(2, chartHeight, Width - 4, TitleHeight); Render2D.FillRectangle(headerRect, style.BackgroundNormal); - Render2D.DrawText(style.FontMedium, Title, headerTextRect, Style.Current.ForegroundGrey, TextAlignment.Near, TextAlignment.Center); - Render2D.DrawText(style.FontMedium, _sample, headerTextRect, Style.Current.Foreground, TextAlignment.Far, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Title, headerTextRect, style.ForegroundGrey, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _sample, headerTextRect, style.Foreground, TextAlignment.Far, TextAlignment.Center); } private void OnClick(ref Float2 location) From a9e1568edc7a6276475926bfdc3941dc8380891e Mon Sep 17 00:00:00 2001 From: "Mr. Capybara" Date: Mon, 13 Nov 2023 14:26:42 -0400 Subject: [PATCH 46/57] Auto WakeUp rigidbodies when set "isKinematic" to false --- Source/Engine/Physics/Actors/RigidBody.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Actors/RigidBody.cpp b/Source/Engine/Physics/Actors/RigidBody.cpp index a090104ff..da8006c7d 100644 --- a/Source/Engine/Physics/Actors/RigidBody.cpp +++ b/Source/Engine/Physics/Actors/RigidBody.cpp @@ -34,8 +34,12 @@ void RigidBody::SetIsKinematic(const bool value) if (value == GetIsKinematic()) return; _isKinematic = value; - if (_actor) + if (_actor && _isActive) + { PhysicsBackend::SetRigidDynamicActorFlag(_actor, PhysicsBackend::RigidDynamicFlags::Kinematic, value); + if (!value) + WakeUp(); + } } void RigidBody::SetLinearDamping(float value) From 4ceed361e254c155dfd2a79f73bfaad3ae3f2c81 Mon Sep 17 00:00:00 2001 From: "Mr. Capybara" Date: Mon, 13 Nov 2023 14:28:16 -0400 Subject: [PATCH 47/57] small fix --- Source/Engine/Physics/Actors/RigidBody.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/Actors/RigidBody.cpp b/Source/Engine/Physics/Actors/RigidBody.cpp index da8006c7d..4aaf3d285 100644 --- a/Source/Engine/Physics/Actors/RigidBody.cpp +++ b/Source/Engine/Physics/Actors/RigidBody.cpp @@ -34,10 +34,10 @@ void RigidBody::SetIsKinematic(const bool value) if (value == GetIsKinematic()) return; _isKinematic = value; - if (_actor && _isActive) + if (_actor) { PhysicsBackend::SetRigidDynamicActorFlag(_actor, PhysicsBackend::RigidDynamicFlags::Kinematic, value); - if (!value) + if (!value && _isActive) WakeUp(); } } From eaafb72ca935d090e9d23ed289e1a904dd848dea Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 13 Nov 2023 23:54:07 +0100 Subject: [PATCH 48/57] Optimize some includes and use automatic serializers for platform settings --- .../Engine/Core/Config/PlatformSettingsBase.h | 2 +- .../Engine/Graphics/Materials/MaterialInfo.h | 1 - .../DirectX/DX11/GPUDeviceDX11.h | 1 + .../DirectX/DX12/GPUPipelineStateDX12.h | 1 + .../Android/AndroidPlatformSettings.h | 17 +++---------- .../Platform/Apple/ApplePlatformSettings.h | 9 +------ .../Engine/Platform/GDK/GDKPlatformSettings.h | 25 ++----------------- .../Platform/Linux/LinuxPlatformSettings.h | 19 ++------------ .../Engine/Platform/Mac/MacPlatformSettings.h | 13 +--------- .../Engine/Platform/UWP/UWPPlatformSettings.h | 18 ++----------- .../Windows/WindowsPlatformSettings.h | 22 ++-------------- .../Engine/Platform/iOS/iOSPlatformSettings.h | 12 +-------- Source/Engine/Serialization/Serialization.h | 1 + .../Engine/Serialization/SerializationFwd.h | 3 ++- 14 files changed, 20 insertions(+), 124 deletions(-) diff --git a/Source/Engine/Core/Config/PlatformSettingsBase.h b/Source/Engine/Core/Config/PlatformSettingsBase.h index 6d7e8601e..b372e3dd5 100644 --- a/Source/Engine/Core/Config/PlatformSettingsBase.h +++ b/Source/Engine/Core/Config/PlatformSettingsBase.h @@ -3,7 +3,7 @@ #pragma once #include "Engine/Core/Config/Settings.h" -#include "Engine/Serialization/Serialization.h" +#include "Engine/Serialization/SerializationFwd.h" /// /// Specifies the display mode of a game window. diff --git a/Source/Engine/Graphics/Materials/MaterialInfo.h b/Source/Engine/Graphics/Materials/MaterialInfo.h index 8e4933150..afa23a6cc 100644 --- a/Source/Engine/Graphics/Materials/MaterialInfo.h +++ b/Source/Engine/Graphics/Materials/MaterialInfo.h @@ -3,7 +3,6 @@ #pragma once #include "../Enums.h" -#include "Engine/Core/Math/Math.h" /// /// Material domain type. Material domain defines the target usage of the material shader. diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h index 6205a628d..c0e0d8fd5 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h @@ -4,6 +4,7 @@ #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUResource.h" +#include "Engine/Core/Collections/Dictionary.h" #include "../GPUDeviceDX.h" #include "../IncludeDirectXHeaders.h" diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.h index 2764ce235..27aba0c4b 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.h @@ -7,6 +7,7 @@ #include "Engine/Graphics/GPUPipelineState.h" #include "GPUDeviceDX12.h" #include "Types.h" +#include "Engine/Core/Collections/Dictionary.h" #include "../IncludeDirectXHeaders.h" class GPUTextureViewDX12; diff --git a/Source/Engine/Platform/Android/AndroidPlatformSettings.h b/Source/Engine/Platform/Android/AndroidPlatformSettings.h index 0a877fc18..d5e7190f2 100644 --- a/Source/Engine/Platform/Android/AndroidPlatformSettings.h +++ b/Source/Engine/Platform/Android/AndroidPlatformSettings.h @@ -6,16 +6,15 @@ #include "Engine/Core/Config/PlatformSettingsBase.h" #include "Engine/Scripting/SoftObjectReference.h" - -class Texture; +#include "Engine/Content/Assets/Texture.h" /// /// Android platform settings. /// API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API AndroidPlatformSettings : public SettingsBase { -DECLARE_SCRIPTING_TYPE_MINIMAL(AndroidPlatformSettings); -public: + DECLARE_SCRIPTING_TYPE_MINIMAL(AndroidPlatformSettings); + API_AUTO_SERIALIZATION(); /// /// The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}. @@ -35,20 +34,10 @@ public: API_FIELD(Attributes="EditorOrder(1030), EditorDisplay(\"Other\")") SoftObjectReference OverrideIcon; -public: - /// /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// static AndroidPlatformSettings* Get(); - - // [SettingsBase] - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override - { - DESERIALIZE(PackageName); - DESERIALIZE(Permissions); - DESERIALIZE(OverrideIcon); - } }; #if PLATFORM_ANDROID diff --git a/Source/Engine/Platform/Apple/ApplePlatformSettings.h b/Source/Engine/Platform/Apple/ApplePlatformSettings.h index 57ad58449..a03cdd630 100644 --- a/Source/Engine/Platform/Apple/ApplePlatformSettings.h +++ b/Source/Engine/Platform/Apple/ApplePlatformSettings.h @@ -16,6 +16,7 @@ class Texture; API_CLASS(Abstract, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API ApplePlatformSettings : public SettingsBase { DECLARE_SCRIPTING_TYPE_MINIMAL(ApplePlatformSettings); + API_AUTO_SERIALIZATION(); /// /// The app identifier (reversed DNS, eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}. @@ -28,14 +29,6 @@ API_CLASS(Abstract, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_AP /// API_FIELD(Attributes="EditorOrder(1000), EditorDisplay(\"Other\")") SoftObjectReference OverrideIcon; - -public: - // [SettingsBase] - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override - { - DESERIALIZE(AppIdentifier); - DESERIALIZE(OverrideIcon); - } }; #endif diff --git a/Source/Engine/Platform/GDK/GDKPlatformSettings.h b/Source/Engine/Platform/GDK/GDKPlatformSettings.h index 88822047d..1827427c8 100644 --- a/Source/Engine/Platform/GDK/GDKPlatformSettings.h +++ b/Source/Engine/Platform/GDK/GDKPlatformSettings.h @@ -16,7 +16,8 @@ class Texture; API_CLASS(Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API GDKPlatformSettings : public SettingsBase { DECLARE_SCRIPTING_TYPE_MINIMAL(GDKPlatformSettings); -public: + API_AUTO_SERIALIZATION(); + /// /// Game identity name stored in game package manifest (for store). If empty the product name will be used from Game Settings. /// @@ -118,28 +119,6 @@ public: /// API_FIELD(Attributes="EditorOrder(420), EditorDisplay(\"Media Capture\")") bool BlockGameDVR = false; - -public: - // [SettingsBase] - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override - { - DESERIALIZE(Name); - DESERIALIZE(PublisherName); - DESERIALIZE(PublisherDisplayName); - DESERIALIZE(Square150x150Logo); - DESERIALIZE(Square480x480Logo); - DESERIALIZE(Square44x44Logo); - DESERIALIZE(SplashScreenImage); - DESERIALIZE(StoreLogo); - DESERIALIZE(BackgroundColor); - DESERIALIZE(TitleId); - DESERIALIZE(StoreId); - DESERIALIZE(RequiresXboxLive); - DESERIALIZE(SCID); - DESERIALIZE(GameDVRSystemComponent); - DESERIALIZE(BlockBroadcast); - DESERIALIZE(BlockGameDVR); - } }; #endif diff --git a/Source/Engine/Platform/Linux/LinuxPlatformSettings.h b/Source/Engine/Platform/Linux/LinuxPlatformSettings.h index 8384b3d51..429149367 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatformSettings.h +++ b/Source/Engine/Platform/Linux/LinuxPlatformSettings.h @@ -14,8 +14,8 @@ class Texture; /// API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API LinuxPlatformSettings : public SettingsBase { -DECLARE_SCRIPTING_TYPE_MINIMAL(LinuxPlatformSettings); -public: + DECLARE_SCRIPTING_TYPE_MINIMAL(LinuxPlatformSettings); + API_AUTO_SERIALIZATION(); /// /// The default game window mode. @@ -65,25 +65,10 @@ public: API_FIELD(Attributes="EditorOrder(2000), DefaultValue(true), EditorDisplay(\"Graphics\")") bool SupportVulkan = true; -public: - /// /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// static LinuxPlatformSettings* Get(); - - // [SettingsBase] - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override - { - DESERIALIZE(WindowMode); - DESERIALIZE(ScreenWidth); - DESERIALIZE(ScreenHeight); - DESERIALIZE(RunInBackground); - DESERIALIZE(ResizableWindow); - DESERIALIZE(ForceSingleInstance); - DESERIALIZE(OverrideIcon); - DESERIALIZE(SupportVulkan); - } }; #if PLATFORM_LINUX diff --git a/Source/Engine/Platform/Mac/MacPlatformSettings.h b/Source/Engine/Platform/Mac/MacPlatformSettings.h index d3ef21fc4..c7e334bcd 100644 --- a/Source/Engine/Platform/Mac/MacPlatformSettings.h +++ b/Source/Engine/Platform/Mac/MacPlatformSettings.h @@ -12,6 +12,7 @@ API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API MacPlatformSettings : public ApplePlatformSettings { DECLARE_SCRIPTING_TYPE_MINIMAL(MacPlatformSettings); + API_AUTO_SERIALIZATION(); /// /// The default game window mode. @@ -43,22 +44,10 @@ API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API API_FIELD(Attributes="EditorOrder(1010), EditorDisplay(\"Other\", \"Run In Background\")") bool RunInBackground = false; -public: /// /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// static MacPlatformSettings* Get(); - - // [SettingsBase] - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override - { - ApplePlatformSettings::Deserialize(stream, modifier); - DESERIALIZE(WindowMode); - DESERIALIZE(ScreenWidth); - DESERIALIZE(ScreenHeight); - DESERIALIZE(ResizableWindow); - DESERIALIZE(RunInBackground); - } }; #if PLATFORM_MAC diff --git a/Source/Engine/Platform/UWP/UWPPlatformSettings.h b/Source/Engine/Platform/UWP/UWPPlatformSettings.h index 98a2fedce..2c67898bf 100644 --- a/Source/Engine/Platform/UWP/UWPPlatformSettings.h +++ b/Source/Engine/Platform/UWP/UWPPlatformSettings.h @@ -11,8 +11,8 @@ /// API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API UWPPlatformSettings : public SettingsBase { -DECLARE_SCRIPTING_TYPE_MINIMAL(UWPPlatformSettings); -public: + DECLARE_SCRIPTING_TYPE_MINIMAL(UWPPlatformSettings); + API_AUTO_SERIALIZATION(); /// /// The preferred launch windowing mode. @@ -66,8 +66,6 @@ public: All = Landscape | LandscapeFlipped | Portrait | PortraitFlipped }; -public: - /// /// The preferred launch windowing mode. Always fullscreen on Xbox. /// @@ -98,22 +96,10 @@ public: API_FIELD(Attributes="EditorOrder(2010), DefaultValue(false), EditorDisplay(\"Graphics\", \"Support DirectX 10\")") bool SupportDX10 = false; -public: - /// /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// static UWPPlatformSettings* Get(); - - // [SettingsBase] - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override - { - DESERIALIZE(PreferredLaunchWindowingMode); - DESERIALIZE(AutoRotationPreferences); - DESERIALIZE(CertificateLocation); - DESERIALIZE(SupportDX11); - DESERIALIZE(SupportDX10); - } }; #if PLATFORM_UWP diff --git a/Source/Engine/Platform/Windows/WindowsPlatformSettings.h b/Source/Engine/Platform/Windows/WindowsPlatformSettings.h index 4c380fda3..02bfc2bc6 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatformSettings.h +++ b/Source/Engine/Platform/Windows/WindowsPlatformSettings.h @@ -14,8 +14,8 @@ class Texture; /// API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API WindowsPlatformSettings : public SettingsBase { -DECLARE_SCRIPTING_TYPE_MINIMAL(WindowsPlatformSettings); -public: + DECLARE_SCRIPTING_TYPE_MINIMAL(WindowsPlatformSettings); + API_AUTO_SERIALIZATION(); /// /// The default game window mode. @@ -83,28 +83,10 @@ public: API_FIELD(Attributes="EditorOrder(2030), DefaultValue(false), EditorDisplay(\"Graphics\")") bool SupportVulkan = false; -public: - /// /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// static WindowsPlatformSettings* Get(); - - // [SettingsBase] - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override - { - DESERIALIZE(WindowMode); - DESERIALIZE(ScreenWidth); - DESERIALIZE(ScreenHeight); - DESERIALIZE(RunInBackground); - DESERIALIZE(ResizableWindow); - DESERIALIZE(ForceSingleInstance); - DESERIALIZE(OverrideIcon); - DESERIALIZE(SupportDX12); - DESERIALIZE(SupportDX11); - DESERIALIZE(SupportDX10); - DESERIALIZE(SupportVulkan); - } }; #if PLATFORM_WINDOWS diff --git a/Source/Engine/Platform/iOS/iOSPlatformSettings.h b/Source/Engine/Platform/iOS/iOSPlatformSettings.h index 53218e0f6..9eed39f41 100644 --- a/Source/Engine/Platform/iOS/iOSPlatformSettings.h +++ b/Source/Engine/Platform/iOS/iOSPlatformSettings.h @@ -12,6 +12,7 @@ API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API iOSPlatformSettings : public ApplePlatformSettings { DECLARE_SCRIPTING_TYPE_MINIMAL(ApplePlatformSettings); + API_AUTO_SERIALIZATION(); /// /// The app export destination methods. @@ -79,17 +80,6 @@ API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// static iOSPlatformSettings* Get(); - - // [SettingsBase] - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override - { - ApplePlatformSettings::Deserialize(stream, modifier); - DESERIALIZE(AppTeamId); - DESERIALIZE(AppVersion); - DESERIALIZE(ExportMethod); - DESERIALIZE(SupportedInterfaceOrientationsiPhone); - DESERIALIZE(SupportedInterfaceOrientationsiPad); - } }; #if PLATFORM_IOS diff --git a/Source/Engine/Serialization/Serialization.h b/Source/Engine/Serialization/Serialization.h index f9dc0870a..ed1008922 100644 --- a/Source/Engine/Serialization/Serialization.h +++ b/Source/Engine/Serialization/Serialization.h @@ -3,6 +3,7 @@ #pragma once #include "SerializationFwd.h" +#include "ISerializeModifier.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Scripting/ScriptingObject.h" diff --git a/Source/Engine/Serialization/SerializationFwd.h b/Source/Engine/Serialization/SerializationFwd.h index 6315ec058..2ac7ae1de 100644 --- a/Source/Engine/Serialization/SerializationFwd.h +++ b/Source/Engine/Serialization/SerializationFwd.h @@ -3,10 +3,11 @@ #pragma once #include "Engine/Core/ISerializable.h" -#include "ISerializeModifier.h" #include "Json.h" #include "JsonWriter.h" +class ISerializeModifier; + // The floating-point values serialization epsilon for equality checks precision #define SERIALIZE_EPSILON 1e-7f #define SERIALIZE_EPSILON_DOUBLE 1e-17 From 05ea803582a00cd407eec7d6497c57b520a7b7f0 Mon Sep 17 00:00:00 2001 From: "Mr. Capybara" Date: Mon, 13 Nov 2023 20:05:05 -0400 Subject: [PATCH 49/57] add check for GetStartAwake --- Source/Engine/Physics/Actors/RigidBody.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Actors/RigidBody.cpp b/Source/Engine/Physics/Actors/RigidBody.cpp index 4aaf3d285..afbac6611 100644 --- a/Source/Engine/Physics/Actors/RigidBody.cpp +++ b/Source/Engine/Physics/Actors/RigidBody.cpp @@ -37,7 +37,7 @@ void RigidBody::SetIsKinematic(const bool value) if (_actor) { PhysicsBackend::SetRigidDynamicActorFlag(_actor, PhysicsBackend::RigidDynamicFlags::Kinematic, value); - if (!value && _isActive) + if (!value && _isActive && _startAwake) WakeUp(); } } From ab5534da7f4e08f26de5dcb53eb6b1e5e9502369 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 13 Nov 2023 20:38:04 -0600 Subject: [PATCH 50/57] Add limits to cloth brush values. --- Source/Editor/Tools/ClothPainting.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Editor/Tools/ClothPainting.cs b/Source/Editor/Tools/ClothPainting.cs index f12fca9db..28536ea83 100644 --- a/Source/Editor/Tools/ClothPainting.cs +++ b/Source/Editor/Tools/ClothPainting.cs @@ -18,21 +18,25 @@ namespace FlaxEngine.Tools /// /// Brush radius (world-space). /// + [Limit(0)] public float BrushSize = 50.0f; /// /// Brush paint intensity. /// + [Limit(0)] public float BrushStrength = 2.0f; /// /// Brush paint falloff. Hardens or softens painting. /// + [Limit(0)] public float BrushFalloff = 1.5f; /// /// Value to paint with. Hold Ctrl hey to paint with inverse value (1 - value). /// + [Limit(0, 1, 0.01f)] public float PaintValue = 0.0f; /// From f7fb366233e7dbb57202020eddea394a77caa1fb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 14 Nov 2023 10:31:11 +0100 Subject: [PATCH 51/57] Minor doc updates --- Source/Engine/Level/Actors/DirectionalLight.h | 2 +- Source/Engine/Level/Actors/Light.h | 10 +++++----- Source/Engine/Renderer/Lightmaps.h | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Level/Actors/DirectionalLight.h b/Source/Engine/Level/Actors/DirectionalLight.h index 3f57e74bb..d5f31324a 100644 --- a/Source/Engine/Level/Actors/DirectionalLight.h +++ b/Source/Engine/Level/Actors/DirectionalLight.h @@ -13,7 +13,7 @@ class FLAXENGINE_API DirectionalLight : public LightWithShadow DECLARE_SCENE_OBJECT(DirectionalLight); public: /// - /// The number of cascades used for slicing the range of depth covered by the light. Values are 1, 2 or 4 cascades; a typical scene uses 4 cascades. + /// The number of cascades used for slicing the range of depth covered by the light during shadow rendering. Values are 1, 2 or 4 cascades; a typical scene uses 4 cascades. /// API_FIELD(Attributes="EditorOrder(65), DefaultValue(4), Limit(1, 4), EditorDisplay(\"Shadow\")") int32 CascadeCount = 4; diff --git a/Source/Engine/Level/Actors/Light.h b/Source/Engine/Level/Actors/Light.h index 86ca59ef5..efb137b60 100644 --- a/Source/Engine/Level/Actors/Light.h +++ b/Source/Engine/Level/Actors/Light.h @@ -29,7 +29,7 @@ public: float Brightness = 3.14f; /// - /// Controls light visibility range. The distance at which the light be completely faded. Use value 0 to always draw light. + /// Controls light visibility range. The distance at which the light becomes completely faded. Use a value of 0 to always draw light. /// API_FIELD(Attributes="EditorOrder(35), Limit(0, float.MaxValue, 10.0f), EditorDisplay(\"Light\")") float ViewDistance = 0.0f; @@ -87,19 +87,19 @@ public: float MinRoughness = 0.04f; /// - /// The light shadows casting distance from view. + /// Shadows casting distance from view. /// API_FIELD(Attributes="EditorOrder(80), EditorDisplay(\"Shadow\", \"Distance\"), Limit(0, 1000000)") float ShadowsDistance = 5000.0f; /// - /// The light shadows fade off distance + /// Shadows fade off distance. /// API_FIELD(Attributes="EditorOrder(90), EditorDisplay(\"Shadow\", \"Fade Distance\"), Limit(0.0f, 10000.0f, 0.1f)") float ShadowsFadeDistance = 500.0f; /// - /// The light shadows edges sharpness + /// TheShadows edges sharpness. /// API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Shadow\", \"Sharpness\"), Limit(1.0f, 10.0f, 0.001f)") float ShadowsSharpness = 1.0f; @@ -129,7 +129,7 @@ public: float ContactShadowsLength = 0.0f; /// - /// Shadows casting mode by this visual element + /// Describes how a visual element casts shadows. /// API_FIELD(Attributes="EditorOrder(60), EditorDisplay(\"Shadow\", \"Mode\")") ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All; diff --git a/Source/Engine/Renderer/Lightmaps.h b/Source/Engine/Renderer/Lightmaps.h index ee3a8ab39..ee4b56495 100644 --- a/Source/Engine/Renderer/Lightmaps.h +++ b/Source/Engine/Renderer/Lightmaps.h @@ -108,19 +108,19 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(LightmapSettings); }; /// - /// Controls how much all lights will contribute indirect lighting. + /// Controls how much all lights will contribute to indirect lighting. /// API_FIELD(Attributes="EditorOrder(0), Limit(0, 100.0f, 0.1f)") float IndirectLightingIntensity = 1.0f; /// - /// Global scale for objects in lightmap to increase quality + /// Global scale for objects in the lightmap to increase quality /// API_FIELD(Attributes="EditorOrder(10), Limit(0, 100.0f, 0.1f)") float GlobalObjectsScale = 1.0f; /// - /// Amount of pixels space between charts in lightmap atlas + /// Amount of pixel space between charts in lightmap atlas /// API_FIELD(Attributes="EditorOrder(20), Limit(0, 16, 0.1f)") int32 ChartsPadding = 3; From c0f0bd87aa8a1c5efca38d90a1df1154f26b449e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 14 Nov 2023 10:33:40 +0100 Subject: [PATCH 52/57] Fix error in `Tabs.SelectedTab` #1932 --- Source/Editor/GUI/Tabs/Tabs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/GUI/Tabs/Tabs.cs b/Source/Editor/GUI/Tabs/Tabs.cs index b5e2cfe39..3c70363e7 100644 --- a/Source/Editor/GUI/Tabs/Tabs.cs +++ b/Source/Editor/GUI/Tabs/Tabs.cs @@ -239,7 +239,7 @@ namespace FlaxEditor.GUI.Tabs /// public Tab SelectedTab { - get => _selectedIndex == -1 && Children.Count > _selectedIndex + 1 ? null : Children[_selectedIndex + 1] as Tab; + get => _selectedIndex < 0 || Children.Count <= _selectedIndex ? null : Children[_selectedIndex + 1] as Tab; set => SelectedTabIndex = value != null ? Children.IndexOf(value) - 1 : -1; } From 7c53b1e99ad1a4b424d4595009f0ac6832382d49 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 14 Nov 2023 11:22:17 +0100 Subject: [PATCH 53/57] Add clearing BT memory in non-release builds to make issues spotting easier --- Source/Engine/AI/BehaviorKnowledge.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/AI/BehaviorKnowledge.cpp b/Source/Engine/AI/BehaviorKnowledge.cpp index a33738d31..ffc011818 100644 --- a/Source/Engine/AI/BehaviorKnowledge.cpp +++ b/Source/Engine/AI/BehaviorKnowledge.cpp @@ -150,7 +150,13 @@ void BehaviorKnowledge::InitMemory(BehaviorTree* tree) RelevantNodes.Resize(tree->Graph.NodesCount, false); RelevantNodes.SetAll(false); if (!Memory && tree->Graph.NodesStatesSize) + { Memory = Allocator::Allocate(tree->Graph.NodesStatesSize); +#if !BUILD_RELEASE + // Clear memory to make it easier to spot missing data issues (eg. zero GCHandle in C# BT node due to missing state init) + Platform::MemoryClear(Memory, tree->Graph.NodesStatesSize); +#endif + } } void BehaviorKnowledge::FreeMemory() From e0de6744e2aebd74ae133292ed9813c0afb545f3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 14 Nov 2023 11:22:41 +0100 Subject: [PATCH 54/57] Add better errors logging to BT nodes in case of issues --- Source/Engine/AI/BehaviorTree.cs | 16 +++++++++++----- Source/Engine/AI/BehaviorTreeNodes.cpp | 2 ++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Source/Engine/AI/BehaviorTree.cs b/Source/Engine/AI/BehaviorTree.cs index 699c72976..a1c863e6b 100644 --- a/Source/Engine/AI/BehaviorTree.cs +++ b/Source/Engine/AI/BehaviorTree.cs @@ -95,12 +95,16 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T GetState(IntPtr memory) where T : struct { - var ptr = IntPtr.Add(memory, _memoryOffset).ToPointer(); - var handle = GCHandle.FromIntPtr(Unsafe.Read(ptr)); + var ptr = Unsafe.Read(IntPtr.Add(memory, _memoryOffset).ToPointer()); +#if !BUILD_RELEASE + if (ptr == IntPtr.Zero) + throw new Exception($"Missing state '{typeof(T).FullName}' for node '{GetType().FullName}'"); +#endif + var handle = GCHandle.FromIntPtr(ptr); var state = handle.Target; #if !BUILD_RELEASE if (state == null) - throw new NullReferenceException(); + throw new Exception($"Missing state '{typeof(T).FullName}' for node '{GetType().FullName}'"); #endif return ref Unsafe.Unbox(state); } @@ -111,8 +115,10 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FreeState(IntPtr memory) { - var ptr = IntPtr.Add(memory, _memoryOffset).ToPointer(); - var handle = GCHandle.FromIntPtr(Unsafe.Read(ptr)); + var ptr = Unsafe.Read(IntPtr.Add(memory, _memoryOffset).ToPointer()); + if (ptr == IntPtr.Zero) + return; + var handle = GCHandle.FromIntPtr(ptr); handle.Free(); } } diff --git a/Source/Engine/AI/BehaviorTreeNodes.cpp b/Source/Engine/AI/BehaviorTreeNodes.cpp index 4be336b35..fb49a007e 100644 --- a/Source/Engine/AI/BehaviorTreeNodes.cpp +++ b/Source/Engine/AI/BehaviorTreeNodes.cpp @@ -85,6 +85,8 @@ BehaviorUpdateResult BehaviorTreeNode::InvokeUpdate(const BehaviorUpdateContext& result = BehaviorUpdateResult::Failed; else result = Update(context); + if ((int32)result < 0 || (int32)result > (int32)BehaviorUpdateResult::Failed) + result = BehaviorUpdateResult::Failed; // Invalid value is a failure // Post-process result from decorators for (BehaviorTreeDecorator* decorator : _decorators) From 3320c76e141965cb035da42371c39c7c8cd2438d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 14 Nov 2023 11:23:15 +0100 Subject: [PATCH 55/57] Add soft check for null managed object value for unboxing --- Source/Engine/Scripting/ManagedCLR/MUtils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.h b/Source/Engine/Scripting/ManagedCLR/MUtils.h index a9b2d8414..9a79a83c9 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.h +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.h @@ -64,8 +64,8 @@ struct MConverter, TNot& data) From 0360f7786d65c68c02a3948aec5760c369fe8b3d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 14 Nov 2023 11:47:44 +0100 Subject: [PATCH 56/57] Fix crash hen reading`BehaviorKnowledgeSelector` value in C# when type doesn't match exactly --- Source/Engine/AI/BehaviorKnowledgeSelector.cs | 4 +- Source/Engine/Utilities/VariantUtils.cs | 73 ++++++++++++++++++- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/Source/Engine/AI/BehaviorKnowledgeSelector.cs b/Source/Engine/AI/BehaviorKnowledgeSelector.cs index 84e923ccf..5c642e92a 100644 --- a/Source/Engine/AI/BehaviorKnowledgeSelector.cs +++ b/Source/Engine/AI/BehaviorKnowledgeSelector.cs @@ -202,7 +202,7 @@ namespace FlaxEngine public T Get(BehaviorKnowledge knowledge) { if (knowledge != null && knowledge.Get(Path, out var value)) - return (T)value; + return Utilities.VariantUtils.Cast(value); return default; } @@ -218,7 +218,7 @@ namespace FlaxEngine object tmp = null; bool result = knowledge != null && knowledge.Get(Path, out tmp); if (result) - value = (T)tmp; + value = Utilities.VariantUtils.Cast(tmp); return result; } diff --git a/Source/Engine/Utilities/VariantUtils.cs b/Source/Engine/Utilities/VariantUtils.cs index 465a72f04..81520ae1b 100644 --- a/Source/Engine/Utilities/VariantUtils.cs +++ b/Source/Engine/Utilities/VariantUtils.cs @@ -8,7 +8,6 @@ using System.IO; using FlaxEditor.Scripting; using FlaxEditor.Utilities; #endif -using FlaxEngine; using Newtonsoft.Json; namespace FlaxEngine.Utilities @@ -18,6 +17,78 @@ namespace FlaxEngine.Utilities /// public static class VariantUtils { + /// + /// Casts the generic value to a given type. Matches native Variant casting logic that favors returning null/zero value rather than error. + /// + /// The destination value type. + /// The input value to cast. + /// The result value. + internal static T Cast(object value) + { + if (value == null) + return default; + var type = value.GetType(); + if (type != typeof(T)) + { + if (typeof(T) == typeof(Vector2)) + { + if (value is Float2 asFloat2) + return (T)(object)new Vector2(asFloat2.X, asFloat2.Y); + if (value is Float3 asFloat3) + return (T)(object)new Vector2(asFloat3.X, asFloat3.Y); + if (value is Float4 asFloat4) + return (T)(object)new Vector2(asFloat4.X, asFloat4.Y); + } + else if (typeof(T) == typeof(Vector3)) + { + if (value is Float2 asFloat2) + return (T)(object)new Vector3(asFloat2.X, asFloat2.Y, 0); + if (value is Float3 asFloat3) + return (T)(object)new Vector3(asFloat3.X, asFloat3.Y, asFloat3.Z); + if (value is Float4 asFloat4) + return (T)(object)new Vector3(asFloat4.X, asFloat4.Y, asFloat4.Z); + } + else if (typeof(T) == typeof(Vector4)) + { + if (value is Float2 asFloat2) + return (T)(object)new Vector4(asFloat2.X, asFloat2.Y, 0, 0); + if (value is Float3 asFloat3) + return (T)(object)new Vector4(asFloat3.X, asFloat3.Y, asFloat3.Z, 0); + if (value is Vector4 asFloat4) + return (T)(object)new Vector4(asFloat4.X, asFloat4.Y, asFloat4.Z, asFloat4.W); + } + else if (typeof(T) == typeof(Float2)) + { + if (value is Vector2 asVector2) + return (T)(object)new Float2(asVector2.X, asVector2.Y); + if (value is Vector3 asVector3) + return (T)(object)new Float2(asVector3.X, asVector3.Y); + if (value is Vector4 asVector4) + return (T)(object)new Float2(asVector4.X, asVector4.Y); + } + else if (typeof(T) == typeof(Float3)) + { + if (value is Vector2 asVector2) + return (T)(object)new Float3(asVector2.X, asVector2.Y, 0); + if (value is Vector3 asVector3) + return (T)(object)new Float3(asVector3.X, asVector3.Y, asVector3.Z); + if (value is Vector4 asFloat4) + return (T)(object)new Float3(asFloat4.X, asFloat4.Y, asFloat4.Z); + } + else if (typeof(T) == typeof(Float4)) + { + if (value is Vector2 asVector2) + return (T)(object)new Float4(asVector2.X, asVector2.Y, 0, 0); + if (value is Vector3 asVector3) + return (T)(object)new Float4(asVector3.X, asVector3.Y, asVector3.Z, 0); + if (value is Vector4 asVector4) + return (T)(object)new Float4(asVector4.X, asVector4.Y, asVector4.Z, asVector4.W); + } + return (T)Convert.ChangeType(value, typeof(T)); + } + return (T)value; + } + internal enum VariantType { Null = 0, From 6fd34bf5cc50769f28f5acad921469a3b4e418d2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 14 Nov 2023 12:15:51 +0100 Subject: [PATCH 57/57] Fix compilation regression --- .../Platform/Android/AndroidPlatformSettings.h | 3 ++- .../Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Android/AndroidPlatformSettings.h b/Source/Engine/Platform/Android/AndroidPlatformSettings.h index d5e7190f2..9cfe7f880 100644 --- a/Source/Engine/Platform/Android/AndroidPlatformSettings.h +++ b/Source/Engine/Platform/Android/AndroidPlatformSettings.h @@ -6,7 +6,8 @@ #include "Engine/Core/Config/PlatformSettingsBase.h" #include "Engine/Scripting/SoftObjectReference.h" -#include "Engine/Content/Assets/Texture.h" + +class Texture; /// /// Android platform settings. diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 10e4d5846..991ff196b 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1793,6 +1793,18 @@ namespace Flax.Build.Bindings var apiTypeInfo = FindApiTypeInfo(buildData, typeInfo, caller); if (apiTypeInfo != null && apiTypeInfo.IsInterface) return true; + + // Add includes to properly compile bindings (eg. SoftObjectReference) + CppReferencesFiles.Add(apiTypeInfo?.File); + if (typeInfo.GenericArgs != null) + { + for (int i = 0; i < typeInfo.GenericArgs.Count; i++) + { + var t = FindApiTypeInfo(buildData, typeInfo.GenericArgs[i], caller); + CppReferencesFiles.Add(t?.File); + } + } + return false; }