From 666e6d880ba25cae31eb4ba772cbd9ac900740ea Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 2 Dec 2023 13:33:04 -0600 Subject: [PATCH 01/68] Changes to default post process settings. --- Source/Engine/Graphics/PostProcessSettings.h | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 1d695f353..b4432af0d 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -948,7 +948,7 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable /// The effect rendering mode used for the exposure processing. /// API_FIELD(Attributes="EditorOrder(0), PostProcessSetting((int)EyeAdaptationSettingsOverride.Mode)") - EyeAdaptationMode Mode = EyeAdaptationMode::AutomaticHistogram; + EyeAdaptationMode Mode = EyeAdaptationMode::None; /// /// The speed at which the exposure changes when the scene brightness moves from a dark area to a bright area (brightness goes up). @@ -960,7 +960,7 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable /// The speed at which the exposure changes when the scene brightness moves from a bright area to a dark area (brightness goes down). /// API_FIELD(Attributes="Limit(0, 100.0f, 0.01f), EditorOrder(2), PostProcessSetting((int)EyeAdaptationSettingsOverride.SpeedDown)") - float SpeedDown = 1.0f; + float SpeedDown = 10.0f; /// /// The pre-exposure value applied to the scene color before performing post-processing (such as bloom, lens flares, etc.). @@ -984,7 +984,7 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable /// The maximum brightness for the auto exposure which limits the upper brightness the eye can adapt within. /// API_FIELD(Attributes="Limit(0, 100.0f, 0.01f), EditorOrder(6), PostProcessSetting((int)EyeAdaptationSettingsOverride.MaxBrightness), EditorDisplay(null, \"Maximum Brightness\")") - float MaxBrightness = 2.0f; + float MaxBrightness = 15.0f; /// /// The lower bound for the luminance histogram of the scene color. This value is in percent and limits the pixels below this brightness. Use values in the range of 60-80. Used only in AutomaticHistogram mode. @@ -996,7 +996,7 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable /// The upper bound for the luminance histogram of the scene color. This value is in percent and limits the pixels above this brightness. Use values in the range of 80-95. Used only in AutomaticHistogram mode. /// API_FIELD(Attributes="Limit(1, 99, 0.001f), EditorOrder(3), PostProcessSetting((int)EyeAdaptationSettingsOverride.HistogramHighPercent)") - float HistogramHighPercent = 98.0f; + float HistogramHighPercent = 90.0f; public: /// @@ -1082,13 +1082,13 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable /// Strength of the vignette effect. Value 0 hides it. The default value is 0.8. /// API_FIELD(Attributes="Limit(0, 2, 0.001f), EditorOrder(0), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteIntensity)") - float VignetteIntensity = 0.8f; + float VignetteIntensity = 0.0f; /// /// Color of the vignette. /// API_FIELD(Attributes="EditorOrder(1), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteColor)") - Float3 VignetteColor = Float3(0, 0, 0.001f); + Float3 VignetteColor = Float3::Zero; /// /// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape. The default value is 0.125. @@ -1230,25 +1230,25 @@ API_STRUCT() struct FLAXENGINE_API LensFlaresSettings : ISerializable /// Strength of the effect. A value of 0 disables it. /// API_FIELD(Attributes="Limit(0, 10.0f, 0.01f), EditorOrder(0), PostProcessSetting((int)LensFlaresSettingsOverride.Intensity)") - float Intensity = 1.0f; + float Intensity = 0.5f; /// /// Amount of lens flares ghosts. /// API_FIELD(Attributes="Limit(0, 16), EditorOrder(1), PostProcessSetting((int)LensFlaresSettingsOverride.Ghosts)") - int32 Ghosts = 8; + int32 Ghosts = 4; /// /// Lens flares halo width. /// API_FIELD(Attributes="EditorOrder(2), PostProcessSetting((int)LensFlaresSettingsOverride.HaloWidth)") - float HaloWidth = 0.16f; + float HaloWidth = 0.04f; /// /// Lens flares halo intensity. /// API_FIELD(Attributes="Limit(0, 10.0f, 0.01f), EditorOrder(3), PostProcessSetting((int)LensFlaresSettingsOverride.HaloIntensity)") - float HaloIntensity = 0.666f; + float HaloIntensity = 0.5f; /// /// Ghost samples dispersal parameter. @@ -1584,7 +1584,7 @@ API_STRUCT() struct FLAXENGINE_API MotionBlurSettings : ISerializable /// The blur effect strength. A value of 0 disables it, while higher values increase the effect. /// API_FIELD(Attributes="Limit(0, 5, 0.01f), EditorOrder(1), PostProcessSetting((int)MotionBlurSettingsOverride.Scale)") - float Scale = 1.0f; + float Scale = 0.5f; /// /// The amount of sample points used during motion blur rendering. It affects blur quality and performance. From 74baffd76cc107002d145ec1e1a2bf09807d6ccd Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 2 Dec 2023 13:39:16 -0600 Subject: [PATCH 02/68] Fix comment --- Source/Engine/Graphics/PostProcessSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index b4432af0d..649ec46df 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -1079,7 +1079,7 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable CameraArtifactsSettingsOverride OverrideFlags = Override::None; /// - /// Strength of the vignette effect. Value 0 hides it. The default value is 0.8. + /// Strength of the vignette effect. Value 0 hides it. The default value is 0. /// API_FIELD(Attributes="Limit(0, 2, 0.001f), EditorOrder(0), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteIntensity)") float VignetteIntensity = 0.0f; From 7d36cdb67d4374cfa336c535520ac29d43f54ef9 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 6 Dec 2023 09:33:59 -0600 Subject: [PATCH 03/68] Change vignette settings. --- Source/Engine/Graphics/PostProcessSettings.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 649ec46df..52db566da 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -1079,16 +1079,16 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable CameraArtifactsSettingsOverride OverrideFlags = Override::None; /// - /// Strength of the vignette effect. Value 0 hides it. The default value is 0. + /// Strength of the vignette effect. Value 0 hides it. The default value is 0.4. /// API_FIELD(Attributes="Limit(0, 2, 0.001f), EditorOrder(0), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteIntensity)") - float VignetteIntensity = 0.0f; + float VignetteIntensity = 0.4f; /// /// Color of the vignette. /// API_FIELD(Attributes="EditorOrder(1), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteColor)") - Float3 VignetteColor = Float3::Zero; + Float3 VignetteColor = Float3(0 ,0, 0.001f); /// /// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape. The default value is 0.125. From 1f6b63291de310d8fa84d1a88ad952273eb7494b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 6 Dec 2023 09:39:16 -0600 Subject: [PATCH 04/68] Fix code style error --- Source/Engine/Graphics/PostProcessSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 52db566da..1c86b9f48 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -1088,7 +1088,7 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable /// Color of the vignette. /// API_FIELD(Attributes="EditorOrder(1), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteColor)") - Float3 VignetteColor = Float3(0 ,0, 0.001f); + Float3 VignetteColor = Float3(0, 0, 0.001f); /// /// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape. The default value is 0.125. From 9e38a01acc45dca23eb031c6fa7baf0c5958dca4 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:52:25 +0100 Subject: [PATCH 05/68] add units support in float, double and Float3 input --- .../CustomEditors/Editors/DoubleEditor.cs | 5 ++ .../CustomEditors/Editors/FloatEditor.cs | 5 ++ .../CustomEditors/Editors/Vector3Editor.cs | 15 +++++ .../Elements/DoubleValueElement.cs | 10 +++ .../Elements/FloatValueElement.cs | 10 +++ Source/Editor/GUI/Input/DoubleValueBox.cs | 8 ++- Source/Editor/GUI/Input/FloatValueBox.cs | 8 ++- Source/Editor/Utilities/ShuntingYardParser.cs | 17 ++++- Source/Editor/Utilities/Units.cs | 9 +++ Source/Editor/Utilities/Utils.cs | 64 ++++++++++++++++++- .../Editor/ValueCategoryAttribute.cs | 35 ++++++++++ .../Flax.Build.Tests/Flax.Build.Tests.csproj | 2 +- Source/Tools/Flax.Build/Flax.Build.csproj | 1 - 13 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 Source/Editor/Utilities/Units.cs create mode 100644 Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index c490046b9..0906d22af 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -25,6 +26,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); + var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var limit = attributes.FirstOrDefault(x => x is LimitAttribute); @@ -32,6 +35,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Use double value editor with limit var doubleValue = layout.DoubleValue(); + doubleValue.SetCategory(valueCategory); doubleValue.SetLimits((LimitAttribute)limit); doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.SlidingEnd += ClearToken; @@ -43,6 +47,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Use double value editor var doubleValue = layout.DoubleValue(); + doubleValue.SetCategory(valueCategory); doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.SlidingEnd += ClearToken; _element = doubleValue; diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index 07c07030e..525541e99 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -30,6 +31,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); + var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var range = attributes.FirstOrDefault(x => x is RangeAttribute); @@ -49,6 +52,7 @@ namespace FlaxEditor.CustomEditors.Editors // Use float value editor with limit var floatValue = layout.FloatValue(); floatValue.SetLimits((LimitAttribute)limit); + floatValue.SetCategory(valueCategory); floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.SlidingEnd += ClearToken; _element = floatValue; @@ -59,6 +63,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Use float value editor var floatValue = layout.FloatValue(); + floatValue.SetCategory(valueCategory); floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.SlidingEnd += ClearToken; _element = floatValue; diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index c3edd3913..9826c339f 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -4,6 +4,7 @@ using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; using FlaxEngine.GUI; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -70,23 +71,30 @@ namespace FlaxEditor.CustomEditors.Editors LimitAttribute limit = null; var attributes = Values.GetAttributes(); + var category = Utils.ValueCategory.None; if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); + var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + if (categoryAttribute != null) + category = categoryAttribute.Category; } XElement = grid.FloatValue(); XElement.SetLimits(limit); + XElement.SetCategory(category); XElement.ValueBox.ValueChanged += OnXValueChanged; XElement.ValueBox.SlidingEnd += ClearToken; YElement = grid.FloatValue(); YElement.SetLimits(limit); + YElement.SetCategory(category); YElement.ValueBox.ValueChanged += OnYValueChanged; YElement.ValueBox.SlidingEnd += ClearToken; ZElement = grid.FloatValue(); ZElement.SetLimits(limit); + ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnZValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; } @@ -248,24 +256,31 @@ namespace FlaxEditor.CustomEditors.Editors gridControl.SlotsVertically = 1; LimitAttribute limit = null; + Utils.ValueCategory category = Utils.ValueCategory.None; var attributes = Values.GetAttributes(); if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); + var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + if (categoryAttribute != null) + category = categoryAttribute.Category; } XElement = grid.DoubleValue(); XElement.SetLimits(limit); + XElement.SetCategory(category); XElement.ValueBox.ValueChanged += OnValueChanged; XElement.ValueBox.SlidingEnd += ClearToken; YElement = grid.DoubleValue(); YElement.SetLimits(limit); + YElement.SetCategory(category); YElement.ValueBox.ValueChanged += OnValueChanged; YElement.ValueBox.SlidingEnd += ClearToken; ZElement = grid.DoubleValue(); ZElement.SetLimits(limit); + ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; } diff --git a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs index 07af5e991..460a344ff 100644 --- a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs @@ -5,6 +5,7 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Elements { @@ -51,6 +52,15 @@ namespace FlaxEditor.CustomEditors.Elements } } + /// + /// Set the value category of this float element + /// + /// + public void SetCategory(Utils.ValueCategory category) + { + ValueBox.Category = category; + } + /// /// Sets the editor limits from member . /// diff --git a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs index 789d8966e..413a341c1 100644 --- a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs @@ -5,6 +5,7 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Elements { @@ -51,6 +52,15 @@ namespace FlaxEditor.CustomEditors.Elements } } + /// + /// Set the value category of this float element + /// + /// + public void SetCategory(Utils.ValueCategory category) + { + ValueBox.Category = category; + } + /// /// Sets the editor limits from member . /// diff --git a/Source/Editor/GUI/Input/DoubleValueBox.cs b/Source/Editor/GUI/Input/DoubleValueBox.cs index 0ceb10114..46e6402cb 100644 --- a/Source/Editor/GUI/Input/DoubleValueBox.cs +++ b/Source/Editor/GUI/Input/DoubleValueBox.cs @@ -3,6 +3,7 @@ using System; using FlaxEditor.Utilities; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.GUI.Input { @@ -129,10 +130,15 @@ namespace FlaxEditor.GUI.Input Value = Value; } + /// + /// Get or set the category of the value. This can either be none for just a number, a distance or an angle. + /// + public Utils.ValueCategory Category = Utils.ValueCategory.None; + /// protected sealed override void UpdateText() { - SetText(Utilities.Utils.FormatFloat(_value)); + SetText(Utilities.Utils.FormatFloat(_value, Category)); } /// diff --git a/Source/Editor/GUI/Input/FloatValueBox.cs b/Source/Editor/GUI/Input/FloatValueBox.cs index cdb3b08b1..64ea000fa 100644 --- a/Source/Editor/GUI/Input/FloatValueBox.cs +++ b/Source/Editor/GUI/Input/FloatValueBox.cs @@ -4,6 +4,7 @@ using System; using System.Globalization; using FlaxEditor.Utilities; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.GUI.Input { @@ -137,10 +138,15 @@ namespace FlaxEditor.GUI.Input Value = Value; } + /// + /// Get or set the category of the value. This can either be none for just a number, a distance or an angle. + /// + public Utils.ValueCategory Category = Utils.ValueCategory.None; + /// protected sealed override void UpdateText() { - SetText(Utilities.Utils.FormatFloat(_value)); + SetText(Utilities.Utils.FormatFloat(_value, Category)); } /// diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index d139b003a..e7c5b5298 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -121,6 +121,9 @@ namespace FlaxEditor.Utilities ["e"] = Math.E, ["infinity"] = double.MaxValue, ["-infinity"] = -double.MaxValue, + ["m"] = Units.Meters2Units, + ["cm"] = Units.Meters2Units / 100, + ["km"] = Units.Meters2Units * 1000 }; /// @@ -170,7 +173,7 @@ namespace FlaxEditor.Utilities public static IEnumerable Tokenize(string text) { // Prepare text - text = text.Replace(',', '.'); + text = text.Replace(',', '.').Replace("°", ""); // Necessary to correctly parse negative numbers var previous = TokenType.WhiteSpace; @@ -372,6 +375,18 @@ namespace FlaxEditor.Utilities } } + // if stack has more than one item we're not finished with evaluating + // we assume the remaining values are all factors to be multiplied + if (stack.Count > 1) + { + var stackContent = string.Join(",", stack.ToList()); + Debug.Log($"parsing numbers, stack is {stackContent}"); + var v1 = stack.Pop(); + Debug.Log($"first on stack: {v1}"); + while (stack.Count > 0) + v1 *= stack.Pop(); + return v1; + } return stack.Pop(); } diff --git a/Source/Editor/Utilities/Units.cs b/Source/Editor/Utilities/Units.cs new file mode 100644 index 000000000..00d53a463 --- /dev/null +++ b/Source/Editor/Utilities/Units.cs @@ -0,0 +1,9 @@ +namespace FlaxEditor.Utilities; + +public class Units +{ + /// + /// Factor of units per meter. + /// + public static readonly float Meters2Units = 100f; +} diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 3d790ff81..0df4fe573 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -58,6 +58,16 @@ namespace FlaxEditor.Utilities /// public static readonly string FlaxEngineAssemblyName = "FlaxEngine.CSharp"; + /// + /// A category of number values used for formatting and input boxes + /// + public enum ValueCategory + { + None, + Distance, + Angle + } + /// /// Tries to parse number in the name brackets at the end of the value and then increment it to create a new name. /// Supports numbers at the end without brackets. @@ -1171,6 +1181,56 @@ namespace FlaxEditor.Utilities return StringUtils.GetPathWithoutExtension(path); } + /// + /// Format a float value either as-is, with a distance unit or with a degree sign + /// + /// the value to format + /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign + /// the formatted string + public static string FormatFloat(float value, ValueCategory category) + { + switch (category) + { + case ValueCategory.Distance: + var absValue = Mathf.Abs(value); + // in case a unit != cm this would be (value / Maters2Units * 100) + if (absValue < Units.Meters2Units) + return value.ToString("g7", CultureInfo.InvariantCulture) + "cm"; + if (absValue < Units.Meters2Units * 1000) + return (value / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "km"; + case ValueCategory.Angle: return value.ToString("g7", CultureInfo.InvariantCulture) + "°"; + case ValueCategory.None: + default: + return FormatFloat(value); + } + } + + /// + /// Format a double value either as-is, with a distance unit or with a degree sign + /// + /// the value to format + /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign + /// the formatted string + public static string FormatFloat(double value, ValueCategory category) + { + switch (category) + { + case ValueCategory.Distance: + var absValue = Mathf.Abs(value); + // in case a unit != cm this would be (value / Maters2Units * 100) + if (absValue < Units.Meters2Units) + return value.ToString("g17", CultureInfo.InvariantCulture) + "cm"; + if (absValue < Units.Meters2Units * 1000) + return (value / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "km"; + case ValueCategory.Angle: return value.ToString("g17", CultureInfo.InvariantCulture) + "°"; + case ValueCategory.None: + default: + return FormatFloat(value); + } + } + /// /// Formats the floating point value (double precision) into the readable text representation. /// @@ -1182,7 +1242,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (float.IsNegativeInfinity(value) || value == float.MinValue) return "-Infinity"; - string str = value.ToString("r", CultureInfo.InvariantCulture); + string str = value.ToString("g7", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } @@ -1197,7 +1257,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (double.IsNegativeInfinity(value) || value == double.MinValue) return "-Infinity"; - string str = value.ToString("r", CultureInfo.InvariantCulture); + string str = value.ToString("g17", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } diff --git a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs new file mode 100644 index 000000000..3c6da2286 --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine +{ + /// + /// Used to specify the value category of a numeric value as either as-is (a scalar), a distance (formatted as cm/m/km) + /// or an angle (formatted with a degree sign). + /// + /// + [Serializable] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public sealed class ValueCategoryAttribute : Attribute + { + /// + /// The value category used for formatting. + /// + public FlaxEditor.Utilities.Utils.ValueCategory Category; + + private ValueCategoryAttribute() + { + Category = FlaxEditor.Utilities.Utils.ValueCategory.None; + } + + /// + /// Initializes a new instance of the class. + /// + /// The value category. + public ValueCategoryAttribute(FlaxEditor.Utilities.Utils.ValueCategory category) + { + Category = category; + } + } +} diff --git a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj index df3b9851b..78b23ec07 100644 --- a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj +++ b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj @@ -2,7 +2,7 @@ net7.0 - 11.0 + 12 disable annotations true diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index 1837bf0ec..7b189ce56 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -2,7 +2,6 @@ Exe net7.0 - 11.0 disable annotations false From d28b0ab2a6d354336d6178b92e849f5950b880bb Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Sun, 28 Jan 2024 21:41:08 +0100 Subject: [PATCH 06/68] revert unrelated change in .csproj --- Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj | 2 +- Source/Tools/Flax.Build/Flax.Build.csproj | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj index 78b23ec07..df3b9851b 100644 --- a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj +++ b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj @@ -2,7 +2,7 @@ net7.0 - 12 + 11.0 disable annotations true diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index 7b189ce56..1837bf0ec 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -2,6 +2,7 @@ Exe net7.0 + 11.0 disable annotations false From 2625144945e42da581a2285a1d81e280bb3561b5 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:28:35 +0100 Subject: [PATCH 07/68] rename attribute and fix parsing --- .../CustomEditors/Editors/DoubleEditor.cs | 6 ++--- .../CustomEditors/Editors/FloatEditor.cs | 6 ++--- .../CustomEditors/Editors/Vector3Editor.cs | 6 ++--- .../Elements/DoubleValueElement.cs | 2 +- .../Elements/FloatValueElement.cs | 2 +- Source/Editor/GUI/Input/DoubleValueBox.cs | 2 +- Source/Editor/GUI/Input/FloatValueBox.cs | 2 +- Source/Editor/Utilities/ShuntingYardParser.cs | 8 +++--- Source/Editor/Utilities/Utils.cs | 26 ++++++------------- ...ttribute.cs => NumberCategoryAttribute.cs} | 12 ++++----- Source/Engine/Utilities/Utils.cs | 10 +++++++ 11 files changed, 42 insertions(+), 40 deletions(-) rename Source/Engine/Scripting/Attributes/Editor/{ValueCategoryAttribute.cs => NumberCategoryAttribute.cs} (64%) diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index 0906d22af..a428fd448 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -4,7 +4,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -26,8 +26,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); - var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; + var categoryAttribute = attributes.FirstOrDefault(x => x is NumberCategoryAttribute); + var valueCategory = ((NumberCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var limit = attributes.FirstOrDefault(x => x is LimitAttribute); diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index 525541e99..c739269ab 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -4,7 +4,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -31,8 +31,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); - var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; + var categoryAttribute = attributes.FirstOrDefault(x => x is NumberCategoryAttribute); + var valueCategory = ((NumberCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var range = attributes.FirstOrDefault(x => x is RangeAttribute); diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index 9826c339f..fe3ff2b74 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -4,7 +4,7 @@ using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -75,7 +75,7 @@ namespace FlaxEditor.CustomEditors.Editors if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); - var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var categoryAttribute = (NumberCategoryAttribute)attributes.FirstOrDefault(x => x is NumberCategoryAttribute); if (categoryAttribute != null) category = categoryAttribute.Category; } @@ -261,7 +261,7 @@ namespace FlaxEditor.CustomEditors.Editors if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); - var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var categoryAttribute = (NumberCategoryAttribute)attributes.FirstOrDefault(x => x is NumberCategoryAttribute); if (categoryAttribute != null) category = categoryAttribute.Category; } diff --git a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs index 460a344ff..4f121204f 100644 --- a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs @@ -5,7 +5,7 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Elements { diff --git a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs index 413a341c1..9661e391d 100644 --- a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs @@ -5,7 +5,7 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Elements { diff --git a/Source/Editor/GUI/Input/DoubleValueBox.cs b/Source/Editor/GUI/Input/DoubleValueBox.cs index 46e6402cb..d7c4950f6 100644 --- a/Source/Editor/GUI/Input/DoubleValueBox.cs +++ b/Source/Editor/GUI/Input/DoubleValueBox.cs @@ -3,7 +3,7 @@ using System; using FlaxEditor.Utilities; using FlaxEngine; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.GUI.Input { diff --git a/Source/Editor/GUI/Input/FloatValueBox.cs b/Source/Editor/GUI/Input/FloatValueBox.cs index 64ea000fa..2fccba8e8 100644 --- a/Source/Editor/GUI/Input/FloatValueBox.cs +++ b/Source/Editor/GUI/Input/FloatValueBox.cs @@ -4,7 +4,7 @@ using System; using System.Globalization; using FlaxEditor.Utilities; using FlaxEngine; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.GUI.Input { diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index e7c5b5298..f8fa05e5b 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -243,6 +243,11 @@ namespace FlaxEditor.Utilities } else if (type == TokenType.Variable) { + if (previous == TokenType.Number) + { + previous = TokenType.Operator; + yield return new Token(TokenType.Operator, "*"); + } // Continue till the end of the variable while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Variable) { @@ -379,10 +384,7 @@ namespace FlaxEditor.Utilities // we assume the remaining values are all factors to be multiplied if (stack.Count > 1) { - var stackContent = string.Join(",", stack.ToList()); - Debug.Log($"parsing numbers, stack is {stackContent}"); var v1 = stack.Pop(); - Debug.Log($"first on stack: {v1}"); while (stack.Count > 0) v1 *= stack.Pop(); return v1; diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 0df4fe573..d1db68046 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -58,16 +58,6 @@ namespace FlaxEditor.Utilities /// public static readonly string FlaxEngineAssemblyName = "FlaxEngine.CSharp"; - /// - /// A category of number values used for formatting and input boxes - /// - public enum ValueCategory - { - None, - Distance, - Angle - } - /// /// Tries to parse number in the name brackets at the end of the value and then increment it to create a new name. /// Supports numbers at the end without brackets. @@ -1187,11 +1177,11 @@ namespace FlaxEditor.Utilities /// the value to format /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign /// the formatted string - public static string FormatFloat(float value, ValueCategory category) + public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { switch (category) { - case ValueCategory.Distance: + case FlaxEngine.Utils.ValueCategory.Distance: var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) @@ -1199,8 +1189,8 @@ namespace FlaxEditor.Utilities if (absValue < Units.Meters2Units * 1000) return (value / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "m"; return (value / 1000 / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "km"; - case ValueCategory.Angle: return value.ToString("g7", CultureInfo.InvariantCulture) + "°"; - case ValueCategory.None: + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g7", CultureInfo.InvariantCulture) + "°"; + case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); } @@ -1212,11 +1202,11 @@ namespace FlaxEditor.Utilities /// the value to format /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign /// the formatted string - public static string FormatFloat(double value, ValueCategory category) + public static string FormatFloat(double value, FlaxEngine.Utils.ValueCategory category) { switch (category) { - case ValueCategory.Distance: + case FlaxEngine.Utils.ValueCategory.Distance: var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) @@ -1224,8 +1214,8 @@ namespace FlaxEditor.Utilities if (absValue < Units.Meters2Units * 1000) return (value / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "m"; return (value / 1000 / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "km"; - case ValueCategory.Angle: return value.ToString("g17", CultureInfo.InvariantCulture) + "°"; - case ValueCategory.None: + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g17", CultureInfo.InvariantCulture) + "°"; + case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); } diff --git a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs similarity index 64% rename from Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs rename to Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs index 3c6da2286..6b5726bb1 100644 --- a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs @@ -11,23 +11,23 @@ namespace FlaxEngine /// [Serializable] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public sealed class ValueCategoryAttribute : Attribute + public sealed class NumberCategoryAttribute : Attribute { /// /// The value category used for formatting. /// - public FlaxEditor.Utilities.Utils.ValueCategory Category; + public Utils.ValueCategory Category; - private ValueCategoryAttribute() + private NumberCategoryAttribute() { - Category = FlaxEditor.Utilities.Utils.ValueCategory.None; + Category = Utils.ValueCategory.None; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The value category. - public ValueCategoryAttribute(FlaxEditor.Utilities.Utils.ValueCategory category) + public NumberCategoryAttribute(Utils.ValueCategory category) { Category = category; } diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index cb385efcf..195bacf96 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -1038,5 +1038,15 @@ namespace FlaxEngine parameterTypes = Array.Empty(); return parameterTypes; } + + /// + /// A category of number values used for formatting and input boxes + /// + public enum ValueCategory + { + None, + Distance, + Angle + } } } From 20dbe1565198b8241a81451a4472232364f067d2 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:52:52 +0100 Subject: [PATCH 08/68] add configuration options --- Source/Editor/Utilities/Units.cs | 12 ++++++++++++ Source/Editor/Utilities/Utils.cs | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/Source/Editor/Utilities/Units.cs b/Source/Editor/Utilities/Units.cs index 00d53a463..1256ccd63 100644 --- a/Source/Editor/Utilities/Units.cs +++ b/Source/Editor/Utilities/Units.cs @@ -6,4 +6,16 @@ public class Units /// Factor of units per meter. /// public static readonly float Meters2Units = 100f; + + // the next two bools could be cached values in the user preferences + + /// + /// Set it to false to always show game units without any postfix + /// + public static bool UseUnitsFormatting = true; + + /// + /// If set to true, the distance unit is chosen on the magnitude, otherwise it's meters + /// + public static bool AutomaticUnitsFormatting = true; } diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index d1db68046..c5e418ae2 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1179,9 +1179,13 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { + if (!Units.UseUnitsFormatting) + return FormatFloat(value); switch (category) { case FlaxEngine.Utils.ValueCategory.Distance: + if (!Units.AutomaticUnitsFormatting) + return (value / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "m"; var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) @@ -1204,9 +1208,13 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(double value, FlaxEngine.Utils.ValueCategory category) { + if (!Units.UseUnitsFormatting) + return FormatFloat(value); switch (category) { case FlaxEngine.Utils.ValueCategory.Distance: + if (!Units.AutomaticUnitsFormatting) + return (value / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "m"; var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) From ef41ad6074a9e157496b2be3d46e0c195668ddc3 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Mon, 29 Jan 2024 07:34:58 +0100 Subject: [PATCH 09/68] add units to transform editor, some collider values --- Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs | 6 ++++++ Source/Engine/Physics/Colliders/Collider.h | 4 ++-- Source/Engine/Physics/Colliders/SphereCollider.h | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 4c153e759..a33163aab 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -42,10 +42,13 @@ namespace FlaxEditor.CustomEditors.Editors var grayOutFactor = 0.6f; XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor); XElement.ValueBox.BorderSelectedColor = AxisColorX; + XElement.ValueBox.Category = Utils.ValueCategory.Distance; YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor); YElement.ValueBox.BorderSelectedColor = AxisColorY; + YElement.ValueBox.Category = Utils.ValueCategory.Distance; ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor); ZElement.ValueBox.BorderSelectedColor = AxisColorZ; + ZElement.ValueBox.Category = Utils.ValueCategory.Distance; } } @@ -65,10 +68,13 @@ namespace FlaxEditor.CustomEditors.Editors var grayOutFactor = 0.6f; XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor); XElement.ValueBox.BorderSelectedColor = AxisColorX; + XElement.ValueBox.Category = Utils.ValueCategory.Angle; YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor); YElement.ValueBox.BorderSelectedColor = AxisColorY; + YElement.ValueBox.Category = Utils.ValueCategory.Angle; ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor); ZElement.ValueBox.BorderSelectedColor = AxisColorZ; + ZElement.ValueBox.Category = Utils.ValueCategory.Angle; } } diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index bd17aa27a..e5a873b77 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -52,7 +52,7 @@ public: /// /// Gets the center of the collider, measured in the object's local space. /// - API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE Vector3 GetCenter() const { return _center; @@ -66,7 +66,7 @@ public: /// /// Gets the contact offset. Colliders whose distance is less than the sum of their ContactOffset values will generate contacts. The contact offset must be positive. Contact offset allows the collision detection system to predictively enforce the contact constraint even when the objects are slightly separated. /// - API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(2.0f), Limit(0, 100), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(2.0f), Limit(0, 100), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetContactOffset() const { return _contactOffset; diff --git a/Source/Engine/Physics/Colliders/SphereCollider.h b/Source/Engine/Physics/Colliders/SphereCollider.h index 061372af4..6adaf76ba 100644 --- a/Source/Engine/Physics/Colliders/SphereCollider.h +++ b/Source/Engine/Physics/Colliders/SphereCollider.h @@ -21,7 +21,7 @@ public: /// Gets the radius of the sphere, measured in the object's local space. /// /// The sphere radius will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetRadius() const { return _radius; From a0741dd9de409a1090e216623072a077952d7d19 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:24:55 +0100 Subject: [PATCH 10/68] units for quaternion, transform and more collider values --- .../Editor/CustomEditors/Editors/QuaternionEditor.cs | 3 +++ Source/Engine/Core/Math/Transform.h | 4 ++-- Source/Engine/Physics/Colliders/BoxCollider.h | 2 +- Source/Engine/Physics/Colliders/CapsuleCollider.h | 4 ++-- Source/Engine/Physics/Colliders/CharacterController.h | 10 +++++----- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs index 3a469dd1a..1e41264d0 100644 --- a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs @@ -45,14 +45,17 @@ namespace FlaxEditor.CustomEditors.Editors gridControl.SlotsVertically = 1; XElement = grid.FloatValue(); + XElement.ValueBox.Category = Utils.ValueCategory.Angle; XElement.ValueBox.ValueChanged += OnValueChanged; XElement.ValueBox.SlidingEnd += ClearToken; YElement = grid.FloatValue(); + YElement.ValueBox.Category = Utils.ValueCategory.Angle; YElement.ValueBox.ValueChanged += OnValueChanged; YElement.ValueBox.SlidingEnd += ClearToken; ZElement = grid.FloatValue(); + ZElement.ValueBox.Category = Utils.ValueCategory.Angle; ZElement.ValueBox.ValueChanged += OnValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h index 1079b4c23..46b3168c4 100644 --- a/Source/Engine/Core/Math/Transform.h +++ b/Source/Engine/Core/Math/Transform.h @@ -18,12 +18,12 @@ API_STRUCT() struct FLAXENGINE_API Transform /// /// The translation vector of the transform. /// - API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\")") Vector3 Translation; + API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\"), NumberCategory(Utils.ValueCategory.Distance)") Vector3 Translation; /// /// The rotation of the transform. /// - API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\")") Quaternion Orientation; + API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\"), NumberCategory(Utils.ValueCategory.Angle)") Quaternion Orientation; /// /// The scale vector of the transform. diff --git a/Source/Engine/Physics/Colliders/BoxCollider.h b/Source/Engine/Physics/Colliders/BoxCollider.h index 3c15ce640..a52817d66 100644 --- a/Source/Engine/Physics/Colliders/BoxCollider.h +++ b/Source/Engine/Physics/Colliders/BoxCollider.h @@ -23,7 +23,7 @@ public: /// Gets the size of the box, measured in the object's local space. /// /// The box size will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Float3), \"100,100,100\"), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Float3), \"100,100,100\"), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE Float3 GetSize() const { return _size; diff --git a/Source/Engine/Physics/Colliders/CapsuleCollider.h b/Source/Engine/Physics/Colliders/CapsuleCollider.h index 8eff1c164..e6be1e2c2 100644 --- a/Source/Engine/Physics/Colliders/CapsuleCollider.h +++ b/Source/Engine/Physics/Colliders/CapsuleCollider.h @@ -25,7 +25,7 @@ public: /// Gets the radius of the sphere, measured in the object's local space. /// /// The sphere radius will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetRadius() const { return _radius; @@ -41,7 +41,7 @@ public: /// Gets the height of the capsule, measured in the object's local space between the centers of the hemispherical ends. /// /// The capsule height will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetHeight() const { return _height; diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index 737d98728..5eb4df965 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -73,7 +73,7 @@ public: /// /// Gets the radius of the sphere, measured in the object's local space. The sphere radius will be scaled by the actor's world scale. /// - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") float GetRadius() const; /// @@ -84,7 +84,7 @@ public: /// /// Gets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale. /// - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") float GetHeight() const; /// @@ -95,7 +95,7 @@ public: /// /// Gets the slope limit (in degrees). Limits the collider to only climb slopes that are less steep (in degrees) than the indicated value. /// - API_PROPERTY(Attributes="EditorOrder(210), DefaultValue(45.0f), Limit(0, 100), EditorDisplay(\"Character Controller\")") + API_PROPERTY(Attributes="EditorOrder(210), DefaultValue(45.0f), Limit(0, 100), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Angle)") float GetSlopeLimit() const; /// @@ -117,7 +117,7 @@ public: /// /// Gets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controller’s height or it will generate an error. /// - API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\")") + API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Distance)") float GetStepOffset() const; /// @@ -139,7 +139,7 @@ public: /// /// Gets the minimum move distance of the character controller. The minimum traveled distance to consider. If traveled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small. /// - API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\")") + API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Distance)") float GetMinMoveDistance() const; /// From dcda7d96bbaea79143a59d44b94ce237598beb5e Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:06:05 +0100 Subject: [PATCH 11/68] use C# default for double formatting --- Source/Editor/Utilities/Utils.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index c5e418ae2..ac5a3c3c6 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1214,15 +1214,15 @@ namespace FlaxEditor.Utilities { case FlaxEngine.Utils.ValueCategory.Distance: if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "m"; + return (value / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "m"; var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) - return value.ToString("g17", CultureInfo.InvariantCulture) + "cm"; + return value.ToString("g15", CultureInfo.InvariantCulture) + "cm"; if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "m"; - return (value / 1000 / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "km"; - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g17", CultureInfo.InvariantCulture) + "°"; + return (value / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "km"; + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g15", CultureInfo.InvariantCulture) + "°"; case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); @@ -1255,7 +1255,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (double.IsNegativeInfinity(value) || value == double.MinValue) return "-Infinity"; - string str = value.ToString("g17", CultureInfo.InvariantCulture); + string str = value.ToString("g15", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } From 3dc34e2d9c6d585bd20a4a7a0a822cdcad482d63 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Mon, 29 Jan 2024 22:51:02 +0100 Subject: [PATCH 12/68] add more units and categories --- Source/Editor/Utilities/ShuntingYardParser.cs | 46 ++++++++++++++++++- Source/Editor/Utilities/Utils.cs | 21 ++++++--- Source/Engine/Utilities/Utils.cs | 9 +++- 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index f8fa05e5b..0034896e1 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -123,7 +123,34 @@ namespace FlaxEditor.Utilities ["-infinity"] = -double.MaxValue, ["m"] = Units.Meters2Units, ["cm"] = Units.Meters2Units / 100, - ["km"] = Units.Meters2Units * 1000 + ["km"] = Units.Meters2Units * 1000, + ["s"] = 1, + ["ms"] = 0.001, + ["min"] = 60, + ["h"] = 3600, + ["cm²"] = (Units.Meters2Units / 100) * (Units.Meters2Units / 100), + ["cm³"] = (Units.Meters2Units / 100) * (Units.Meters2Units / 100) * (Units.Meters2Units / 100), + ["dm²"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10), + ["dm³"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10) * (Units.Meters2Units / 10), + ["l"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10) * (Units.Meters2Units / 10), + ["m²"] = Units.Meters2Units * Units.Meters2Units, + ["m³"] = Units.Meters2Units * Units.Meters2Units * Units.Meters2Units, + ["kg"] = 1, + ["g"] = 0.001, + ["N"] = Units.Meters2Units + }; + + /// + /// List known units which cannot be handled as a variable easily because they contain operator + /// symbols (mostly a forward slash). The value is the factor to calculate game units. + /// + private static readonly IDictionary UnitSymbols = new Dictionary + { + ["cm/s"] = Units.Meters2Units / 100, + ["cm/s²"] = Units.Meters2Units / 100, + ["m/s"] = Units.Meters2Units, + ["m/s²"] = Units.Meters2Units, + ["km/h"] = 1/3.6 * Units.Meters2Units }; /// @@ -174,6 +201,23 @@ namespace FlaxEditor.Utilities { // Prepare text text = text.Replace(',', '.').Replace("°", ""); + foreach (var kv in UnitSymbols) + { + int idx; + do + { + idx = text.IndexOf(kv.Key, StringComparison.InvariantCulture); + if (idx > 0) + { + if (DetermineType(text[idx - 1]) != TokenType.Number) + throw new ParsingException($"unit found without a number: {kv.Key} at {idx} in {text}"); + if (Mathf.Abs(kv.Value - 1) < Mathf.Epsilon) + text = text.Remove(idx, kv.Key.Length); + else + text = text.Replace(kv.Key, "*" + kv.Value); + } + } while (idx > 0); + } // Necessary to correctly parse negative numbers var previous = TokenType.WhiteSpace; diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index ac5a3c3c6..be5399111 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1179,21 +1179,30 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { + const string format = "g7"; if (!Units.UseUnitsFormatting) return FormatFloat(value); switch (category) { case FlaxEngine.Utils.ValueCategory.Distance: if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "m"; + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; var absValue = Mathf.Abs(value); - // in case a unit != cm this would be (value / Maters2Units * 100) + // in case a unit != cm this would be (value / Meters2Units * 100) if (absValue < Units.Meters2Units) - return value.ToString("g7", CultureInfo.InvariantCulture) + "cm"; + return value.ToString(format, CultureInfo.InvariantCulture) + "cm"; if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "m"; - return (value / 1000 / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "km"; - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g7", CultureInfo.InvariantCulture) + "°"; + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "km"; + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; + case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + "s"; + // some fonts have a symbol for that: "\u33A7" + case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m/s"; + case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m/s²"; + case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m²"; + case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m³"; + case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + "kg"; + case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "N"; case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index 195bacf96..9ae42fa43 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -1046,7 +1046,14 @@ namespace FlaxEngine { None, Distance, - Angle + Area, + Volume, + Mass, + Angle, + Speed, + Acceleration, + Time, + Force } } } From 9e5f866b75011dd0d0b49ce069a598c69c990a67 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Tue, 30 Jan 2024 07:44:50 +0100 Subject: [PATCH 13/68] fix Newton parsing --- Source/Editor/Utilities/ShuntingYardParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index 0034896e1..ca3780e8f 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -137,7 +137,7 @@ namespace FlaxEditor.Utilities ["m³"] = Units.Meters2Units * Units.Meters2Units * Units.Meters2Units, ["kg"] = 1, ["g"] = 0.001, - ["N"] = Units.Meters2Units + ["n"] = Units.Meters2Units }; /// @@ -387,7 +387,7 @@ namespace FlaxEditor.Utilities } else { - throw new ParsingException("unknown variable"); + throw new ParsingException($"unknown variable : {token.Value}"); } } else From e7b0375a0e062c43a30b626273b7f81989e4f139 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Tue, 30 Jan 2024 07:45:19 +0100 Subject: [PATCH 14/68] code cleanup --- Source/Editor/Utilities/Utils.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index be5399111..531f2b50b 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1179,7 +1179,7 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { - const string format = "g7"; + const string format = "g7"; if (!Units.UseUnitsFormatting) return FormatFloat(value); switch (category) @@ -1217,21 +1217,22 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(double value, FlaxEngine.Utils.ValueCategory category) { + const string format = "g15"; if (!Units.UseUnitsFormatting) return FormatFloat(value); switch (category) { case FlaxEngine.Utils.ValueCategory.Distance: if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "m"; + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) - return value.ToString("g15", CultureInfo.InvariantCulture) + "cm"; + return value.ToString(format, CultureInfo.InvariantCulture) + "cm"; if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "m"; - return (value / 1000 / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "km"; - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g15", CultureInfo.InvariantCulture) + "°"; + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "km"; + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); From 49beb2c7bab76d75a993249182d9380d257c35ed Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Tue, 30 Jan 2024 08:52:27 +0100 Subject: [PATCH 15/68] add toggle formatting --- .../CustomEditors/Editors/DoubleEditor.cs | 14 +++++++++++ .../CustomEditors/Editors/FloatEditor.cs | 15 ++++++++++++ .../CustomEditors/Editors/Vector3Editor.cs | 24 +++++++++++++++++++ Source/Editor/GUI/Input/FloatValueBox.cs | 10 +++++--- 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index a428fd448..97c0d4782 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -40,6 +40,13 @@ namespace FlaxEditor.CustomEditors.Editors doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.SlidingEnd += ClearToken; _element = doubleValue; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + mb.AutoCheck = true; + mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None; + }; return; } } @@ -50,6 +57,13 @@ namespace FlaxEditor.CustomEditors.Editors doubleValue.SetCategory(valueCategory); doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.SlidingEnd += ClearToken; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + mb.AutoCheck = true; + mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None; + }; _element = doubleValue; } } diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index c739269ab..88777474b 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; +using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using Utils = FlaxEngine.Utils; @@ -56,6 +57,13 @@ namespace FlaxEditor.CustomEditors.Editors floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.SlidingEnd += ClearToken; _element = floatValue; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + mb.AutoCheck = true; + mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None; + }; return; } } @@ -67,6 +75,13 @@ namespace FlaxEditor.CustomEditors.Editors floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.SlidingEnd += ClearToken; _element = floatValue; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + mb.AutoCheck = true; + mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None; + }; } } diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index fe3ff2b74..c01a841c2 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -97,6 +97,18 @@ namespace FlaxEditor.CustomEditors.Editors ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnZValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => + { + XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + }); + mb.AutoCheck = true; + mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None; + }; } private void OnXValueChanged() @@ -283,6 +295,18 @@ namespace FlaxEditor.CustomEditors.Editors ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => + { + XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + }); + mb.AutoCheck = true; + mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None; + }; } private void OnValueChanged() diff --git a/Source/Editor/GUI/Input/FloatValueBox.cs b/Source/Editor/GUI/Input/FloatValueBox.cs index 2fccba8e8..5495fe534 100644 --- a/Source/Editor/GUI/Input/FloatValueBox.cs +++ b/Source/Editor/GUI/Input/FloatValueBox.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Globalization; using FlaxEditor.Utilities; using FlaxEngine; using Utils = FlaxEngine.Utils; @@ -138,10 +137,15 @@ namespace FlaxEditor.GUI.Input Value = Value; } + private Utils.ValueCategory _category = Utils.ValueCategory.None; + /// - /// Get or set the category of the value. This can either be none for just a number, a distance or an angle. + /// Get or set the category of the value. This can be none for just a number or a more specific one like a distance. /// - public Utils.ValueCategory Category = Utils.ValueCategory.None; + public Utils.ValueCategory Category { + get => _category; + set { _category = value; UpdateText(); } + } /// protected sealed override void UpdateText() From 2a0320677849698e388dcf95cb2e6e769f2634a5 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Tue, 30 Jan 2024 09:24:44 +0100 Subject: [PATCH 16/68] add formatting preference to editor options --- Source/Editor/Options/InterfaceOptions.cs | 37 +++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 95a273f19..482946e9a 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using FlaxEditor.GUI.Docking; +using FlaxEditor.Utilities; using FlaxEngine; namespace FlaxEditor.Options @@ -90,6 +91,25 @@ namespace FlaxEditor.Options PlayScenes, } + /// + /// Options for formatting numerical values + /// + public enum ValueFormattingType + { + /// + /// No formatting + /// + None, + /// + /// Format using the base SI unit + /// + BaseUnit, + /// + /// Format using a unit that matches the value best + /// + AutoUnit + } + /// /// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required. /// @@ -148,6 +168,23 @@ namespace FlaxEditor.Options [EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")] public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal; + private ValueFormattingType _valueFormatting = ValueFormattingType.None; + + /// + /// Gets or sets the formatting option for numeric values in the editor. + /// + [DefaultValue(ValueFormattingType.None)] + [EditorDisplay("Interface"), EditorOrder(300), Tooltip("Formatting of numeric values.")] + public ValueFormattingType ValueFormating { + get => _valueFormatting; + set + { + _valueFormatting = value; + Units.UseUnitsFormatting = _valueFormatting != ValueFormattingType.None; + Units.AutomaticUnitsFormatting = _valueFormatting == ValueFormattingType.AutoUnit; + } + } + /// /// Gets or sets the timestamps prefix mode for output log messages. /// From 1f8314b66302f2a5242ce818e1ec96933c963160 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Tue, 30 Jan 2024 09:29:05 +0100 Subject: [PATCH 17/68] fixed typo --- Source/Editor/Options/InterfaceOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 482946e9a..5c4a88b1e 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -175,7 +175,7 @@ namespace FlaxEditor.Options /// [DefaultValue(ValueFormattingType.None)] [EditorDisplay("Interface"), EditorOrder(300), Tooltip("Formatting of numeric values.")] - public ValueFormattingType ValueFormating { + public ValueFormattingType ValueFormatting { get => _valueFormatting; set { From 22600a2c29e6a005e6d9ba0f8aba23bc78af99f2 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:01:22 +0100 Subject: [PATCH 18/68] add torque unit --- Source/Editor/Utilities/ShuntingYardParser.cs | 5 ++++- Source/Editor/Utilities/Utils.cs | 1 + Source/Engine/Physics/Actors/RigidBody.h | 2 +- Source/Engine/Utilities/Utils.cs | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index ca3780e8f..356fec2b5 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -144,13 +144,16 @@ namespace FlaxEditor.Utilities /// List known units which cannot be handled as a variable easily because they contain operator /// symbols (mostly a forward slash). The value is the factor to calculate game units. /// + // Nm is here because these values are compared case-sensitive and we don't want to confuse + // nanometers and Newtonmeters private static readonly IDictionary UnitSymbols = new Dictionary { ["cm/s"] = Units.Meters2Units / 100, ["cm/s²"] = Units.Meters2Units / 100, ["m/s"] = Units.Meters2Units, ["m/s²"] = Units.Meters2Units, - ["km/h"] = 1/3.6 * Units.Meters2Units + ["km/h"] = 1/3.6 * Units.Meters2Units, + ["Nm"] = Units.Meters2Units * Units.Meters2Units }; /// diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 531f2b50b..a0422bf7d 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1203,6 +1203,7 @@ namespace FlaxEditor.Utilities case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m³"; case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + "kg"; case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "N"; + case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "Nm"; case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); diff --git a/Source/Engine/Physics/Actors/RigidBody.h b/Source/Engine/Physics/Actors/RigidBody.h index 2ef50ca7a..c7fb20038 100644 --- a/Source/Engine/Physics/Actors/RigidBody.h +++ b/Source/Engine/Physics/Actors/RigidBody.h @@ -181,7 +181,7 @@ public: /// /// Gets the mass value measured in kilograms (use override value only if OverrideMass is checked). /// - API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\")") + API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\"), NumberCategory(Utils.ValueCategory.Mass)") float GetMass() const; /// diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index 9ae42fa43..e4b7c544b 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -1053,7 +1053,8 @@ namespace FlaxEngine Speed, Acceleration, Time, - Force + Force, + Torque } } } From 7a802a2eeef381904300c3cc69e4ec3cbe5c1979 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 20:39:39 +0100 Subject: [PATCH 19/68] format +/- infinity --- Source/Editor/Utilities/Utils.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index a0422bf7d..445520f76 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1179,6 +1179,10 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { + if (float.IsPositiveInfinity(value) || value == float.MaxValue) + return "Infinity"; + if (float.IsNegativeInfinity(value) || value == float.MinValue) + return "-Infinity"; const string format = "g7"; if (!Units.UseUnitsFormatting) return FormatFloat(value); @@ -1218,6 +1222,10 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(double value, FlaxEngine.Utils.ValueCategory category) { + if (double.IsPositiveInfinity(value) || value == double.MaxValue) + return "Infinity"; + if (double.IsNegativeInfinity(value) || value == double.MinValue) + return "-Infinity"; const string format = "g15"; if (!Units.UseUnitsFormatting) return FormatFloat(value); From 245cf63b0b5c269a248def238897f7f85c888352 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 20:40:06 +0100 Subject: [PATCH 20/68] format break limits on joints --- Source/Engine/Physics/Joints/Joint.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/Joints/Joint.h b/Source/Engine/Physics/Joints/Joint.h index f96e47b71..e5805631d 100644 --- a/Source/Engine/Physics/Joints/Joint.h +++ b/Source/Engine/Physics/Joints/Joint.h @@ -38,7 +38,7 @@ public: /// /// Gets the break force. Determines the maximum force the joint can apply before breaking. Broken joints no longer participate in physics simulation. /// - API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\")") + API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Force)") FORCE_INLINE float GetBreakForce() const { return _breakForce; @@ -52,7 +52,7 @@ public: /// /// Gets the break torque. Determines the maximum torque the joint can apply before breaking. Broken joints no longer participate in physics simulation. /// - API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\")") + API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Torque)") FORCE_INLINE float GetBreakTorque() const { return _breakTorque; From 54658cbb3b7083385817b4f35dbae965882c08da Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 22:05:24 +0100 Subject: [PATCH 21/68] =?UTF-8?q?fix=20=C2=B2=20and=20=C2=B3=20in=20parser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/Editor/Utilities/ShuntingYardParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index 356fec2b5..b362823eb 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -189,7 +189,7 @@ namespace FlaxEditor.Utilities if (Operators.ContainsKey(str)) return TokenType.Operator; - if (char.IsLetter(c)) + if (char.IsLetter(c) || c=='²' || c=='³') return TokenType.Variable; throw new ParsingException("wrong character"); From 7f95dab511cf38990520cd2b9c2641d6a4f138f2 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 22:05:54 +0100 Subject: [PATCH 22/68] more joint values formatted --- Source/Engine/Physics/Joints/DistanceJoint.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/Joints/DistanceJoint.h b/Source/Engine/Physics/Joints/DistanceJoint.h index 96ddc9538..0ae98414e 100644 --- a/Source/Engine/Physics/Joints/DistanceJoint.h +++ b/Source/Engine/Physics/Joints/DistanceJoint.h @@ -67,7 +67,7 @@ public: /// Gets the allowed minimum distance for the joint. /// /// Used only when DistanceJointFlag.MinDistance flag is set. The minimum distance must be no more than the maximum distance. Default: 0, Range: [0, float.MaxValue]. - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(0.0f), Limit(0.0f), EditorDisplay(\"Joint\")") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(0.0f), Limit(0.0f), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetMinDistance() const { return _minDistance; @@ -83,7 +83,7 @@ public: /// Gets the allowed maximum distance for the joint. /// /// Used only when DistanceJointFlag.MaxDistance flag is set. The maximum distance must be no less than the minimum distance. Default: 0, Range: [0, float.MaxValue]. - API_PROPERTY(Attributes="EditorOrder(120), DefaultValue(10.0f), Limit(0.0f), EditorDisplay(\"Joint\")") + API_PROPERTY(Attributes="EditorOrder(120), DefaultValue(10.0f), Limit(0.0f), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetMaxDistance() const { return _maxDistance; From 8aa402bcb9b7ec22cb68d98011c6832d85b1c381 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 23:21:57 +0100 Subject: [PATCH 23/68] add space option and avoid duplicated code --- Source/Editor/Options/InterfaceOptions.cs | 15 ++++ Source/Editor/Utilities/Units.cs | 23 ++++-- Source/Editor/Utilities/Utils.cs | 87 ++++++++++------------- 3 files changed, 72 insertions(+), 53 deletions(-) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 5c4a88b1e..3e32b0933 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -185,6 +185,21 @@ namespace FlaxEditor.Options } } + private bool _spaceNumberAndUnits = false; + + /// + /// Gets or sets the option to put a space between numbers and units for unit formatting + /// + [DefaultValue(false)] + [EditorDisplay("Interface"), EditorOrder(310), Tooltip("Put a space between numbers and units.")] + public bool SpaceNumberAndUnits { get => _spaceNumberAndUnits; + set + { + _spaceNumberAndUnits = value; + Units.SpaceNumberAndUnits = _spaceNumberAndUnits; + } + } + /// /// Gets or sets the timestamps prefix mode for output log messages. /// diff --git a/Source/Editor/Utilities/Units.cs b/Source/Editor/Utilities/Units.cs index 1256ccd63..cd4e67882 100644 --- a/Source/Editor/Utilities/Units.cs +++ b/Source/Editor/Utilities/Units.cs @@ -7,15 +7,30 @@ public class Units /// public static readonly float Meters2Units = 100f; - // the next two bools could be cached values in the user preferences - /// - /// Set it to false to always show game units without any postfix + /// Set it to false to always show game units without any postfix. /// public static bool UseUnitsFormatting = true; /// - /// If set to true, the distance unit is chosen on the magnitude, otherwise it's meters + /// Add a space between numbers and units for readability. + /// + public static bool SpaceNumberAndUnits = true; + + /// + /// If set to true, the distance unit is chosen on the magnitude, otherwise it's meters. /// public static bool AutomaticUnitsFormatting = true; + + /// + /// Return the unit according to user settings. + /// + /// + /// + public static string Unit(string unit) + { + if (SpaceNumberAndUnits) + return $" {unit}"; + return unit; + } } diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 445520f76..17456babf 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1171,6 +1171,36 @@ namespace FlaxEditor.Utilities return StringUtils.GetPathWithoutExtension(path); } + private static string InternalFormat(double value, string format, FlaxEngine.Utils.ValueCategory category) + { + switch (category) + { + case FlaxEngine.Utils.ValueCategory.Distance: + if (!Units.AutomaticUnitsFormatting) + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); + var absValue = Mathf.Abs(value); + // in case a unit != cm this would be (value / Meters2Units * 100) + if (absValue < Units.Meters2Units) + return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("cm"); + if (absValue < Units.Meters2Units * 1000) + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); + return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("km"); + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; + case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("s"); + // some fonts have a symbol for that: "\u33A7" + case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s"); + case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s²"); + case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m²"); + case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m³"); + case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("kg"); + case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("N"); + case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("Nm"); + case FlaxEngine.Utils.ValueCategory.None: + default: + return FormatFloat(value); + } + } + /// /// Format a float value either as-is, with a distance unit or with a degree sign /// @@ -1183,35 +1213,10 @@ namespace FlaxEditor.Utilities return "Infinity"; if (float.IsNegativeInfinity(value) || value == float.MinValue) return "-Infinity"; - const string format = "g7"; - if (!Units.UseUnitsFormatting) + if (!Units.UseUnitsFormatting || category == FlaxEngine.Utils.ValueCategory.None) return FormatFloat(value); - switch (category) - { - case FlaxEngine.Utils.ValueCategory.Distance: - if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; - var absValue = Mathf.Abs(value); - // in case a unit != cm this would be (value / Meters2Units * 100) - if (absValue < Units.Meters2Units) - return value.ToString(format, CultureInfo.InvariantCulture) + "cm"; - if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; - return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "km"; - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; - case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + "s"; - // some fonts have a symbol for that: "\u33A7" - case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m/s"; - case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m/s²"; - case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m²"; - case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m³"; - case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + "kg"; - case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "N"; - case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "Nm"; - case FlaxEngine.Utils.ValueCategory.None: - default: - return FormatFloat(value); - } + const string format = "G7"; + return InternalFormat(value, format, category); } /// @@ -1226,26 +1231,10 @@ namespace FlaxEditor.Utilities return "Infinity"; if (double.IsNegativeInfinity(value) || value == double.MinValue) return "-Infinity"; - const string format = "g15"; - if (!Units.UseUnitsFormatting) + if (!Units.UseUnitsFormatting || category == FlaxEngine.Utils.ValueCategory.None) return FormatFloat(value); - switch (category) - { - case FlaxEngine.Utils.ValueCategory.Distance: - if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; - var absValue = Mathf.Abs(value); - // in case a unit != cm this would be (value / Maters2Units * 100) - if (absValue < Units.Meters2Units) - return value.ToString(format, CultureInfo.InvariantCulture) + "cm"; - if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; - return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "km"; - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; - case FlaxEngine.Utils.ValueCategory.None: - default: - return FormatFloat(value); - } + const string format = "G15"; + return InternalFormat(value, format, category); } /// @@ -1259,7 +1248,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (float.IsNegativeInfinity(value) || value == float.MinValue) return "-Infinity"; - string str = value.ToString("g7", CultureInfo.InvariantCulture); + string str = value.ToString("R", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } @@ -1274,7 +1263,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (double.IsNegativeInfinity(value) || value == double.MinValue) return "-Infinity"; - string str = value.ToString("g15", CultureInfo.InvariantCulture); + string str = value.ToString("R", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } From 92c109c00368ad572edf3e6af27cd5546617a1eb Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 9 Mar 2024 23:47:39 +0200 Subject: [PATCH 24/68] Fix `PhysicsScene` change not working in physics actors --- Source/Engine/Physics/Actors/RigidBody.cpp | 2 +- Source/Engine/Physics/Colliders/Collider.cpp | 2 +- Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp | 7 +++++-- Source/Engine/Physics/PhysicsBackend.h | 2 +- Source/Engine/Physics/PhysicsBackendEmpty.cpp | 2 +- Source/Engine/Terrain/TerrainPatch.cpp | 2 +- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Physics/Actors/RigidBody.cpp b/Source/Engine/Physics/Actors/RigidBody.cpp index 870bba867..04672552c 100644 --- a/Source/Engine/Physics/Actors/RigidBody.cpp +++ b/Source/Engine/Physics/Actors/RigidBody.cpp @@ -569,7 +569,7 @@ void RigidBody::OnTransformChanged() void RigidBody::OnPhysicsSceneChanged(PhysicsScene* previous) { - PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _actor); + PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _actor, true); void* scene = GetPhysicsScene()->GetPhysicsScene(); PhysicsBackend::AddSceneActor(scene, _actor); const bool putToSleep = !_startAwake && GetEnableSimulation() && !GetIsKinematic() && IsActiveInHierarchy(); diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index 1ad6b4946..d8541a222 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -438,7 +438,7 @@ void Collider::OnPhysicsSceneChanged(PhysicsScene* previous) if (_staticActor != nullptr) { - PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _staticActor); + PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _staticActor, true); void* scene = GetPhysicsScene()->GetPhysicsScene(); PhysicsBackend::AddSceneActor(scene, _staticActor); } diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index 0c77738f6..5d86ed8c9 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -1998,11 +1998,14 @@ void PhysicsBackend::AddSceneActor(void* scene, void* actor) FlushLocker.Unlock(); } -void PhysicsBackend::RemoveSceneActor(void* scene, void* actor) +void PhysicsBackend::RemoveSceneActor(void* scene, void* actor, bool immediately) { auto scenePhysX = (ScenePhysX*)scene; FlushLocker.Lock(); - scenePhysX->RemoveActors.Add((PxActor*)actor); + if (immediately) + scenePhysX->Scene->removeActor(*(PxActor*)actor); + else + scenePhysX->RemoveActors.Add((PxActor*)actor); FlushLocker.Unlock(); } diff --git a/Source/Engine/Physics/PhysicsBackend.h b/Source/Engine/Physics/PhysicsBackend.h index 518201573..8bd493267 100644 --- a/Source/Engine/Physics/PhysicsBackend.h +++ b/Source/Engine/Physics/PhysicsBackend.h @@ -113,7 +113,7 @@ public: static void SetSceneBounceThresholdVelocity(void* scene, float value); static void SetSceneOrigin(void* scene, const Vector3& oldOrigin, const Vector3& newOrigin); static void AddSceneActor(void* scene, void* actor); - static void RemoveSceneActor(void* scene, void* actor); + static void RemoveSceneActor(void* scene, void* actor, bool immediately = false); static void AddSceneActorAction(void* scene, void* actor, ActionType action); #if COMPILE_WITH_PROFILER static void GetSceneStatistics(void* scene, PhysicsStatistics& result); diff --git a/Source/Engine/Physics/PhysicsBackendEmpty.cpp b/Source/Engine/Physics/PhysicsBackendEmpty.cpp index 26054c342..941813b2e 100644 --- a/Source/Engine/Physics/PhysicsBackendEmpty.cpp +++ b/Source/Engine/Physics/PhysicsBackendEmpty.cpp @@ -115,7 +115,7 @@ void PhysicsBackend::AddSceneActor(void* scene, void* actor) { } -void PhysicsBackend::RemoveSceneActor(void* scene, void* actor) +void PhysicsBackend::RemoveSceneActor(void* scene, void* actor, bool immediately) { } diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 053ab10e0..792b6c899 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -2580,7 +2580,7 @@ void TerrainPatch::Deserialize(DeserializeStream& stream, ISerializeModifier* mo void TerrainPatch::OnPhysicsSceneChanged(PhysicsScene* previous) { - PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _physicsActor); + PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _physicsActor, true); void* scene = _terrain->GetPhysicsScene()->GetPhysicsScene(); PhysicsBackend::AddSceneActor(scene, _physicsActor); } From e12919da98d9828e257017c2c5885f792db5337d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 13:05:02 +0100 Subject: [PATCH 25/68] Fix crash in `StaticModel::GetMaterial` when model is not loaded or has invalid entries count --- Source/Engine/Level/Actors/StaticModel.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index aa6772904..07ff609bd 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -553,13 +553,11 @@ const Span StaticModel::GetMaterialSlots() const MaterialBase* StaticModel::GetMaterial(int32 entryIndex) { - if (Model) - Model->WaitForLoaded(); - else + if (!Model || Model->WaitForLoaded()) return nullptr; CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr); MaterialBase* material = Entries[entryIndex].Material.Get(); - if (!material) + if (!material && entryIndex < Model->MaterialSlots.Count()) { material = Model->MaterialSlots[entryIndex].Material.Get(); if (!material) From 94520d30adfa5121123d0712d643e6e2a9297936 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 13:22:01 +0100 Subject: [PATCH 26/68] Fix debug shapes from prefab window visible in main viewport --- Source/Editor/Viewport/PrefabWindowViewport.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index b9ee1c730..2fdb39b4f 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -54,6 +54,7 @@ namespace FlaxEditor.Viewport private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); private PrefabSpritesRenderer _spritesRenderer; + private IntPtr _tempDebugDrawContext; /// /// Drag and drop handlers @@ -258,11 +259,19 @@ namespace FlaxEditor.Viewport var selectedParents = TransformGizmo.SelectedParents; if (selectedParents.Count > 0) { + // Use temporary Debug Draw context to pull any debug shapes drawing in Scene Graph Nodes - those are used in OnDebugDraw down below + if (_tempDebugDrawContext == IntPtr.Zero) + _tempDebugDrawContext = DebugDraw.AllocateContext(); + DebugDraw.SetContext(_tempDebugDrawContext); + DebugDraw.UpdateContext(_tempDebugDrawContext, 1.0f); + for (int i = 0; i < selectedParents.Count; i++) { if (selectedParents[i].IsActiveInHierarchy) selectedParents[i].OnDebugDraw(_debugDrawData); } + + DebugDraw.SetContext(IntPtr.Zero); } } @@ -792,6 +801,13 @@ namespace FlaxEditor.Viewport /// public override void OnDestroy() { + if (IsDisposing) + return; + if (_tempDebugDrawContext != IntPtr.Zero) + { + DebugDraw.FreeContext(_tempDebugDrawContext); + _tempDebugDrawContext = IntPtr.Zero; + } FlaxEngine.Object.Destroy(ref SelectionOutline); FlaxEngine.Object.Destroy(ref _spritesRenderer); From fc29d687b5132be9258de8282430ddb6790564ef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 13:25:25 +0100 Subject: [PATCH 27/68] Add more insights on potentially missing asset file data chunks --- Source/Engine/Content/BinaryAsset.cpp | 27 ++++++++++++++++++- Source/Engine/Content/Storage/AssetHeader.h | 25 +++-------------- Source/Engine/Content/Storage/FlaxStorage.cpp | 24 +++++++++++++++++ 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/Source/Engine/Content/BinaryAsset.cpp b/Source/Engine/Content/BinaryAsset.cpp index 688a3f7b5..85ffd66ec 100644 --- a/Source/Engine/Content/BinaryAsset.cpp +++ b/Source/Engine/Content/BinaryAsset.cpp @@ -584,7 +584,32 @@ Asset::LoadResult BinaryAsset::loadAsset() ASSERT(Storage && _header.ID.IsValid() && _header.TypeName.HasChars()); auto lock = Storage->Lock(); - return load(); + const LoadResult result = load(); +#if !BUILD_RELEASE + if (result == LoadResult::MissingDataChunk) + { + // Provide more insights on potentially missing asset data chunk + Char chunksBitMask[ASSET_FILE_DATA_CHUNKS + 1]; + Char chunksExistBitMask[ASSET_FILE_DATA_CHUNKS + 1]; + Char chunksLoadBitMask[ASSET_FILE_DATA_CHUNKS + 1]; + for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) + { + if (const FlaxChunk* chunk = _header.Chunks[i]) + { + chunksBitMask[i] = '1'; + chunksExistBitMask[i] = chunk->ExistsInFile() ? '1' : '0'; + chunksLoadBitMask[i] = chunk->IsLoaded() ? '1' : '0'; + } + else + { + chunksBitMask[i] = chunksExistBitMask[i] = chunksLoadBitMask[i] = '0'; + } + } + chunksBitMask[ASSET_FILE_DATA_CHUNKS] = chunksExistBitMask[ASSET_FILE_DATA_CHUNKS] = chunksLoadBitMask[ASSET_FILE_DATA_CHUNKS] = 0; + LOG(Warning, "Asset reports missing data chunk. Chunks bitmask: {}, existing chunks: {} loaded chunks: {}. '{}'", chunksBitMask, chunksExistBitMask, chunksLoadBitMask, ToString()); + } +#endif + return result; } void BinaryAsset::releaseStorage() diff --git a/Source/Engine/Content/Storage/AssetHeader.h b/Source/Engine/Content/Storage/AssetHeader.h index 0730be025..883a16f16 100644 --- a/Source/Engine/Content/Storage/AssetHeader.h +++ b/Source/Engine/Content/Storage/AssetHeader.h @@ -81,36 +81,17 @@ public: /// /// Gets the amount of created asset chunks. /// - /// Created asset chunks - int32 GetChunksCount() const - { - int32 result = 0; - for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) - { - if (Chunks[i] != nullptr) - result++; - } - return result; - } + int32 GetChunksCount() const; /// /// Deletes all chunks. Warning! Chunks are managed internally, use with caution! /// - void DeleteChunks() - { - for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) - { - SAFE_DELETE(Chunks[i]); - } - } + void DeleteChunks(); /// /// Unlinks all chunks. /// - void UnlinkChunks() - { - Platform::MemoryClear(Chunks, sizeof(Chunks)); - } + void UnlinkChunks(); /// /// Gets string with a human-readable info about that header diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index 0ba268e21..acc5be881 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -20,6 +20,30 @@ #endif #include +int32 AssetHeader::GetChunksCount() const +{ + int32 result = 0; + for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) + { + if (Chunks[i] != nullptr) + result++; + } + return result; +} + +void AssetHeader::DeleteChunks() +{ + for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) + { + SAFE_DELETE(Chunks[i]); + } +} + +void AssetHeader::UnlinkChunks() +{ + Platform::MemoryClear(Chunks, sizeof(Chunks)); +} + String AssetHeader::ToString() const { return String::Format(TEXT("ID: {0}, TypeName: {1}, Chunks Count: {2}"), ID, TypeName, GetChunksCount()); From 66b68bff27005a97fc5b8288400377b0831ebea8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 17:48:16 +0100 Subject: [PATCH 28/68] Cleanup editor viewports code --- Source/Editor/Gizmo/GizmosCollection.cs | 15 ++++ Source/Editor/Viewport/EditorGizmoViewport.cs | 37 ++++++++++ .../Viewport/MainEditorGizmoViewport.cs | 69 +++---------------- .../Editor/Viewport/PrefabWindowViewport.cs | 66 +++--------------- .../Editor/Viewport/ViewportDraggingHelper.cs | 4 +- Source/Editor/Windows/Assets/PrefabWindow.cs | 36 ++++------ 6 files changed, 88 insertions(+), 139 deletions(-) diff --git a/Source/Editor/Gizmo/GizmosCollection.cs b/Source/Editor/Gizmo/GizmosCollection.cs index b77d22e84..0f69c0756 100644 --- a/Source/Editor/Gizmo/GizmosCollection.cs +++ b/Source/Editor/Gizmo/GizmosCollection.cs @@ -161,5 +161,20 @@ namespace FlaxEditor.Gizmo } throw new ArgumentException("Not added mode to activate."); } + + /// + /// Gets the gizmo of a given type or returns null if not added. + /// + /// Type of the gizmo. + /// Found gizmo or null. + public T Get() where T : GizmoBase + { + foreach (var e in this) + { + if (e is T asT) + return asT; + } + return null; + } } } diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index 8f038ce17..f1c4fed70 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -41,6 +41,7 @@ namespace FlaxEditor.Viewport Gizmos[i].Update(deltaTime); } } + /// public EditorViewport Viewport => this; @@ -121,5 +122,41 @@ namespace FlaxEditor.Viewport base.OnDestroy(); } + + internal static readonly float[] TranslateSnapValues = + { + 0.1f, + 0.5f, + 1.0f, + 5.0f, + 10.0f, + 100.0f, + 1000.0f, + }; + + internal static readonly float[] RotateSnapValues = + { + 1.0f, + 5.0f, + 10.0f, + 15.0f, + 30.0f, + 45.0f, + 60.0f, + 90.0f, + }; + + internal static readonly float[] ScaleSnapValues = + { + 0.05f, + 0.1f, + 0.25f, + 0.5f, + 1.0f, + 2.0f, + 4.0f, + 6.0f, + 8.0f, + }; } } diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 541bbcfba..1e63888bf 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -268,16 +268,14 @@ namespace FlaxEditor.Viewport Parent = scaleSnappingWidget }; enableScaleSnapping.Toggled += OnScaleSnappingToggle; - var scaleSnappingCM = new ContextMenu(); _scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM) { TooltipText = "Scale snapping values" }; - - for (int i = 0; i < EditorViewportScaleSnapValues.Length; i++) + for (int i = 0; i < ScaleSnapValues.Length; i++) { - var v = EditorViewportScaleSnapValues[i]; + var v = ScaleSnapValues[i]; var button = scaleSnappingCM.AddButton(v.ToString()); button.Tag = v; } @@ -295,16 +293,14 @@ namespace FlaxEditor.Viewport Parent = rotateSnappingWidget }; enableRotateSnapping.Toggled += OnRotateSnappingToggle; - var rotateSnappingCM = new ContextMenu(); _rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM) { TooltipText = "Rotation snapping values" }; - - for (int i = 0; i < EditorViewportRotateSnapValues.Length; i++) + for (int i = 0; i < RotateSnapValues.Length; i++) { - var v = EditorViewportRotateSnapValues[i]; + var v = RotateSnapValues[i]; var button = rotateSnappingCM.AddButton(v.ToString()); button.Tag = v; } @@ -322,7 +318,6 @@ namespace FlaxEditor.Viewport Parent = translateSnappingWidget }; enableTranslateSnapping.Toggled += OnTranslateSnappingToggle; - var translateSnappingCM = new ContextMenu(); _translateSnapping = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM) { @@ -330,16 +325,14 @@ namespace FlaxEditor.Viewport }; if (TransformGizmo.TranslationSnapValue < 0.0f) _translateSnapping.Text = "Bounding Box"; - - for (int i = 0; i < EditorViewportTranslateSnapValues.Length; i++) + for (int i = 0; i < TranslateSnapValues.Length; i++) { - var v = EditorViewportTranslateSnapValues[i]; + var v = TranslateSnapValues[i]; var button = translateSnappingCM.AddButton(v.ToString()); button.Tag = v; } var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume"); buttonBB.Tag = -1.0f; - translateSnappingCM.ButtonClicked += OnWidgetTranslateSnapClick; translateSnappingCM.VisibleChanged += OnWidgetTranslateSnapShowHide; _translateSnapping.Parent = translateSnappingWidget; @@ -552,7 +545,7 @@ namespace FlaxEditor.Viewport var task = renderContext.Task; // Render editor primitives, gizmo and debug shapes in debug view modes - // Note: can use Output buffer as both input and output because EditorPrimitives is using a intermediate buffers + // Note: can use Output buffer as both input and output because EditorPrimitives is using an intermediate buffer if (EditorPrimitives && EditorPrimitives.CanRender()) { EditorPrimitives.Render(context, ref renderContext, task.Output, task.Output); @@ -619,19 +612,6 @@ namespace FlaxEditor.Viewport _gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale; } - private static readonly float[] EditorViewportScaleSnapValues = - { - 0.05f, - 0.1f, - 0.25f, - 0.5f, - 1.0f, - 2.0f, - 4.0f, - 6.0f, - 8.0f, - }; - private void OnWidgetScaleSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -651,25 +631,11 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } - private static readonly float[] EditorViewportRotateSnapValues = - { - 1.0f, - 5.0f, - 10.0f, - 15.0f, - 30.0f, - 45.0f, - 60.0f, - 90.0f, - }; - private void OnWidgetRotateSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -689,24 +655,11 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } - private static readonly float[] EditorViewportTranslateSnapValues = - { - 0.1f, - 0.5f, - 1.0f, - 5.0f, - 10.0f, - 100.0f, - 1000.0f, - }; - private void OnWidgetTranslateSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -729,9 +682,7 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 2fdb39b4f..f66d4e861 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -131,16 +131,14 @@ namespace FlaxEditor.Viewport Parent = scaleSnappingWidget }; enableScaleSnapping.Toggled += OnScaleSnappingToggle; - var scaleSnappingCM = new ContextMenu(); _scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM) { TooltipText = "Scale snapping values" }; - - for (int i = 0; i < EditorViewportScaleSnapValues.Length; i++) + for (int i = 0; i < EditorGizmoViewport.ScaleSnapValues.Length; i++) { - var v = EditorViewportScaleSnapValues[i]; + var v = EditorGizmoViewport.ScaleSnapValues[i]; var button = scaleSnappingCM.AddButton(v.ToString()); button.Tag = v; } @@ -158,16 +156,14 @@ namespace FlaxEditor.Viewport Parent = rotateSnappingWidget }; enableRotateSnapping.Toggled += OnRotateSnappingToggle; - var rotateSnappingCM = new ContextMenu(); _rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM) { TooltipText = "Rotation snapping values" }; - - for (int i = 0; i < EditorViewportRotateSnapValues.Length; i++) + for (int i = 0; i < EditorGizmoViewport.RotateSnapValues.Length; i++) { - var v = EditorViewportRotateSnapValues[i]; + var v = EditorGizmoViewport.RotateSnapValues[i]; var button = rotateSnappingCM.AddButton(v.ToString()); button.Tag = v; } @@ -185,16 +181,14 @@ namespace FlaxEditor.Viewport Parent = translateSnappingWidget }; enableTranslateSnapping.Toggled += OnTranslateSnappingToggle; - var translateSnappingCM = new ContextMenu(); _translateSnappng = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM) { TooltipText = "Position snapping values" }; - - for (int i = 0; i < EditorViewportTranslateSnapValues.Length; i++) + for (int i = 0; i < EditorGizmoViewport.TranslateSnapValues.Length; i++) { - var v = EditorViewportTranslateSnapValues[i]; + var v = EditorGizmoViewport.TranslateSnapValues[i]; var button = translateSnappingCM.AddButton(v.ToString()); button.Tag = v; } @@ -428,19 +422,6 @@ namespace FlaxEditor.Viewport _gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale; } - private static readonly float[] EditorViewportScaleSnapValues = - { - 0.05f, - 0.1f, - 0.25f, - 0.5f, - 1.0f, - 2.0f, - 4.0f, - 6.0f, - 8.0f, - }; - private void OnWidgetScaleSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -459,25 +440,11 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } - private static readonly float[] EditorViewportRotateSnapValues = - { - 1.0f, - 5.0f, - 10.0f, - 15.0f, - 30.0f, - 45.0f, - 60.0f, - 90.0f, - }; - private void OnWidgetRotateSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -496,24 +463,11 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } - private static readonly float[] EditorViewportTranslateSnapValues = - { - 0.1f, - 0.5f, - 1.0f, - 5.0f, - 10.0f, - 100.0f, - 1000.0f, - }; - private void OnWidgetTranslateSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -532,9 +486,7 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } diff --git a/Source/Editor/Viewport/ViewportDraggingHelper.cs b/Source/Editor/Viewport/ViewportDraggingHelper.cs index 72fcce828..86a127647 100644 --- a/Source/Editor/Viewport/ViewportDraggingHelper.cs +++ b/Source/Editor/Viewport/ViewportDraggingHelper.cs @@ -146,7 +146,7 @@ namespace FlaxEditor.Viewport var gridPlane = new Plane(Vector3.Zero, Vector3.Up); var flags = SceneGraphNode.RayCastData.FlagTypes.SkipColliders | SceneGraphNode.RayCastData.FlagTypes.SkipEditorPrimitives; hit = _owner.SceneGraphRoot.RayCast(ref ray, ref view, out var closest, out var normal, flags); - var girdGizmo = (GridGizmo)_owner.Gizmos.FirstOrDefault(x => x is GridGizmo); + var girdGizmo = _owner.Gizmos.Get(); if (hit != null) { // Use hit location @@ -180,7 +180,7 @@ namespace FlaxEditor.Viewport var location = hitLocation + new Vector3(0, bottomToCenter, 0); // Apply grid snapping if enabled - var transformGizmo = (TransformGizmo)_owner.Gizmos.FirstOrDefault(x => x is TransformGizmo); + var transformGizmo = _owner.Gizmos.Get(); if (transformGizmo != null && (_owner.UseSnapping || transformGizmo.TranslationSnapEnable)) { float snapValue = transformGizmo.TranslationSnapValue; diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 2e3e700a2..eb5070cb1 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -132,7 +132,7 @@ namespace FlaxEditor.Windows.Assets IsScrollable = false, Offsets = new Margin(0, 0, 0, 18 + 6), }; - _searchBox = new SearchBox() + _searchBox = new SearchBox { AnchorPreset = AnchorPresets.HorizontalStretchMiddle, Parent = headerPanel, @@ -140,7 +140,8 @@ namespace FlaxEditor.Windows.Assets }; _searchBox.TextChanged += OnSearchBoxTextChanged; - _treePanel = new Panel() + // Prefab structure tree + _treePanel = new Panel { AnchorPreset = AnchorPresets.StretchAll, Offsets = new Margin(0.0f, 0.0f, headerPanel.Bottom, 0.0f), @@ -148,8 +149,6 @@ namespace FlaxEditor.Windows.Assets IsScrollable = true, Parent = sceneTreePanel, }; - - // Prefab structure tree Graph = new LocalSceneGraph(new CustomRootNode(this)); Graph.Root.TreeNode.Expand(true); _tree = new PrefabTree @@ -316,11 +315,7 @@ namespace FlaxEditor.Windows.Assets return; // Restore - _viewport.Prefab = _asset; - Graph.MainActor = _viewport.Instance; - Selection.Clear(); - Select(Graph.Main); - Graph.Root.TreeNode.Expand(true); + OnPrefabOpened(); _undo.Clear(); ClearEditedFlag(); } @@ -346,6 +341,15 @@ namespace FlaxEditor.Windows.Assets } } + private void OnPrefabOpened() + { + _viewport.Prefab = _asset; + Graph.MainActor = _viewport.Instance; + Selection.Clear(); + Select(Graph.Main); + Graph.Root.TreeNode.Expand(true); + } + /// public override void Save() { @@ -417,13 +421,8 @@ namespace FlaxEditor.Windows.Assets return; } - _viewport.Prefab = _asset; - Graph.MainActor = _viewport.Instance; + OnPrefabOpened(); _focusCamera = true; - Selection.Clear(); - Select(Graph.Main); - Graph.Root.TreeNode.Expand(true); - _undo.Clear(); ClearEditedFlag(); @@ -468,11 +467,7 @@ namespace FlaxEditor.Windows.Assets _viewport.Prefab = null; if (_asset.IsLoaded) { - _viewport.Prefab = _asset; - Graph.MainActor = _viewport.Instance; - Selection.Clear(); - Select(Graph.Main); - Graph.Root.TreeNode.ExpandAll(true); + OnPrefabOpened(); } } finally @@ -484,7 +479,6 @@ namespace FlaxEditor.Windows.Assets if (_focusCamera && _viewport.Task.FrameCount > 1) { _focusCamera = false; - Editor.GetActorEditorSphere(_viewport.Instance, out BoundingSphere bounds); _viewport.ViewPosition = bounds.Center - _viewport.ViewDirection * (bounds.Radius * 1.2f); } From f4033578c3d97794506f844e66b20992605e69d2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 17:48:51 +0100 Subject: [PATCH 29/68] Refactor UI Control linkage in the prefab previews to prevent bugs --- Source/Editor/Editor.cs | 22 -------- .../Editor/Viewport/Previews/PrefabPreview.cs | 53 +++++++++---------- 2 files changed, 25 insertions(+), 50 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 4cee6d971..d0076068e 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -290,7 +290,6 @@ namespace FlaxEditor StateMachine = new EditorStateMachine(this); Undo = new EditorUndo(this); - UIControl.FallbackParentGetDelegate += OnUIControlFallbackParentGet; if (newProject) InitProject(); @@ -355,27 +354,6 @@ namespace FlaxEditor StateMachine.LoadingState.StartInitEnding(skipCompile); } - private ContainerControl OnUIControlFallbackParentGet(UIControl control) - { - // Check if prefab root control is this UIControl - var loadingPreview = Viewport.Previews.PrefabPreview.LoadingPreview; - var activePreviews = Viewport.Previews.PrefabPreview.ActivePreviews; - if (activePreviews != null) - { - foreach (var preview in activePreviews) - { - if (preview == loadingPreview || - (preview.Instance != null && (preview.Instance == control || preview.Instance.HasActorInHierarchy(control)))) - { - // Link it to the prefab preview to see it in the editor - preview.customControlLinked = control; - return preview; - } - } - } - return null; - } - internal void RegisterModule(EditorModule module) { Log("Register Editor module " + module); diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index ff235bd04..a17850557 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; -using System.Collections.Generic; using FlaxEngine; using Object = FlaxEngine.Object; @@ -13,19 +12,9 @@ namespace FlaxEditor.Viewport.Previews /// public class PrefabPreview : AssetPreview { - /// - /// The currently spawned prefab instance owner. Used to link some actors such as UIControl to preview scene and view. - /// - internal static PrefabPreview LoadingPreview; - - /// - /// The list of active prefab previews. Used to link some actors such as UIControl to preview scene and view. - /// - internal static List ActivePreviews; - private Prefab _prefab; private Actor _instance; - internal UIControl customControlLinked; + internal UIControl _uiControlLinked; /// /// Gets or sets the prefab asset to preview. @@ -54,13 +43,10 @@ namespace FlaxEditor.Viewport.Previews _prefab.WaitForLoaded(); // Spawn prefab - LoadingPreview = this; var instance = PrefabManager.SpawnPrefab(_prefab, null); - LoadingPreview = null; if (instance == null) { _prefab = null; - ActivePreviews.Remove(this); throw new Exception("Failed to spawn a prefab for the preview."); } @@ -84,11 +70,11 @@ namespace FlaxEditor.Viewport.Previews if (_instance) { // Unlink UI control - if (customControlLinked) + if (_uiControlLinked) { - if (customControlLinked.Control?.Parent == this) - customControlLinked.Control.Parent = null; - customControlLinked = null; + if (_uiControlLinked.Control?.Parent == this) + _uiControlLinked.Control.Parent = null; + _uiControlLinked = null; } // Remove for the preview @@ -101,13 +87,27 @@ namespace FlaxEditor.Viewport.Previews { // Add to the preview Task.AddCustomActor(_instance); - - // Link UI canvases to the preview - LinkCanvas(_instance); + UpdateLinkage(); } } } + private void UpdateLinkage() + { + // Link UI canvases to the preview (eg. after canvas added to the prefab) + LinkCanvas(_instance); + + // Link UI control to the preview + if (_uiControlLinked == null && + _instance is UIControl uiControl && + uiControl.Control != null && + uiControl.Control.Parent == null) + { + uiControl.Control.Parent = this; + _uiControlLinked = uiControl; + } + } + private void LinkCanvas(Actor actor) { if (actor is UICanvas uiCanvas) @@ -126,9 +126,6 @@ namespace FlaxEditor.Viewport.Previews public PrefabPreview(bool useWidgets) : base(useWidgets) { - if (ActivePreviews == null) - ActivePreviews = new List(); - ActivePreviews.Add(this); } /// @@ -138,15 +135,15 @@ namespace FlaxEditor.Viewport.Previews if (_instance != null) { - // Link UI canvases to the preview (eg. after canvas added to the prefab) - LinkCanvas(_instance); + UpdateLinkage(); } } /// public override void OnDestroy() { - ActivePreviews.Remove(this); + if (IsDisposing) + return; Prefab = null; base.OnDestroy(); From 6d792f1f74ad1dc1fa84fd58ec758e61aaef9372 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 17:59:41 +0100 Subject: [PATCH 30/68] Fix critical regression in terrain heightmap data upgrade --- Source/Engine/Terrain/TerrainPatch.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 053ab10e0..9ca25cf14 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -2148,7 +2148,9 @@ bool TerrainPatch::CreateHeightField() if (collisionHeader->CheckOldMagicNumber != MAX_int32 || collisionHeader->Version != TerrainCollisionDataHeader::CurrentVersion) { // Reset height map - return InitializeHeightMap(); + PROFILE_CPU_NAMED("ResetHeightMap"); + const float* data = GetHeightmapData(); + return SetupHeightMap(_cachedHeightMap.Count(), data); } // Create heightfield object from the data From 574e0ab1f805e7b04bfaaf4a9654a5e0a1bde70e Mon Sep 17 00:00:00 2001 From: Terence Lee Date: Wed, 13 Mar 2024 17:34:26 +0800 Subject: [PATCH 31/68] Imports the IntPtr type to the local module. Sometimes a "foreign member exception" was being created in Cecil when using external libraries. --- .../Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs index 5cf7bab62..8b4e7e6c9 100644 --- a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs +++ b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs @@ -839,10 +839,11 @@ namespace Flax.Build.Plugins module.GetType("System.IntPtr", out var intPtrType); module.GetType("FlaxEngine.Object", out var scriptingObjectType); var fromUnmanagedPtr = scriptingObjectType.Resolve().GetMethod("FromUnmanagedPtr"); + TypeReference intPtr = module.ImportReference(intPtrType); var m = new MethodDefinition(name + "Native", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, context.VoidType); - m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtrType)); - m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, intPtrType)); + m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtr)); + m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, intPtr)); TypeReference networkStream = module.ImportReference(context.NetworkStreamType); ILProcessor il = m.Body.GetILProcessor(); il.Emit(OpCodes.Nop); @@ -1645,12 +1646,13 @@ namespace Flax.Build.Plugins module.GetType("FlaxEngine.Object", out var scriptingObjectType); var fromUnmanagedPtr = scriptingObjectType.Resolve().GetMethod("FromUnmanagedPtr"); TypeReference networkStream = module.ImportReference(networkStreamType); + TypeReference intPtr = module.ImportReference(intPtrType); // Generate static method to execute RPC locally { var m = new MethodDefinition(method.Name + "_Execute", MethodAttributes.Static | MethodAttributes.Assembly | MethodAttributes.HideBySig, voidType); - m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtrType)); - m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, module.ImportReference(intPtrType))); + m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtr)); + m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, intPtr)); ILProcessor ilp = m.Body.GetILProcessor(); var il = new DotnetIlContext(ilp, method); il.Emit(OpCodes.Nop); From 2b82da95117c80c6cd81d2c2fb45a1efe59f2f48 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Mar 2024 17:29:10 +0100 Subject: [PATCH 32/68] Fix bug with null object being selected in prefab --- .../Editor/Windows/Assets/PrefabWindow.Selection.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs b/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs index cb4c7fd0f..be20c176c 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs @@ -3,11 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; -using FlaxEditor.Gizmo; using FlaxEditor.GUI.Tree; using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph.GUI; -using FlaxEditor.Viewport.Cameras; using FlaxEngine; namespace FlaxEditor.Windows.Assets @@ -64,8 +62,11 @@ namespace FlaxEditor.Windows.Assets private void OnSelectionUndo(SceneGraphNode[] toSelect) { Selection.Clear(); - Selection.AddRange(toSelect); - + foreach (var e in toSelect) + { + if (e != null) + Selection.Add(e); + } OnSelectionChanges(); } @@ -118,11 +119,13 @@ namespace FlaxEditor.Windows.Assets /// The nodes. public void Select(List nodes) { + nodes?.RemoveAll(x => x == null); if (nodes == null || nodes.Count == 0) { Deselect(); return; } + if (Utils.ArraysEqual(Selection, nodes)) return; From 7545e49284d9e08e027102ba46722bb2f424fc2d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Mar 2024 17:30:28 +0100 Subject: [PATCH 33/68] Add manual chunks loading before Binary Asset load in case content streaming flushed them out --- Source/Engine/Content/BinaryAsset.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Source/Engine/Content/BinaryAsset.cpp b/Source/Engine/Content/BinaryAsset.cpp index 85ffd66ec..7735d8b28 100644 --- a/Source/Engine/Content/BinaryAsset.cpp +++ b/Source/Engine/Content/BinaryAsset.cpp @@ -584,6 +584,17 @@ Asset::LoadResult BinaryAsset::loadAsset() ASSERT(Storage && _header.ID.IsValid() && _header.TypeName.HasChars()); auto lock = Storage->Lock(); + auto chunksToPreload = getChunksToPreload(); + if (chunksToPreload != 0) + { + // Ensure that any chunks that were requested before are loaded in memory (in case streaming flushed them out after timeout) + for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) + { + const auto chunk = _header.Chunks[i]; + if (GET_CHUNK_FLAG(i) & chunksToPreload && chunk && chunk->IsMissing()) + Storage->LoadAssetChunk(chunk); + } + } const LoadResult result = load(); #if !BUILD_RELEASE if (result == LoadResult::MissingDataChunk) From 3ba6867cd21a5e32ea6c03b43d89e9a54a7ff88a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Mar 2024 17:30:54 +0100 Subject: [PATCH 34/68] Minor fixes --- Source/Editor/Windows/SceneTreeWindow.cs | 1 - Source/Engine/UI/GUI/ContainerControl.cs | 2 +- Source/Engine/UI/GUI/Control.Bounds.cs | 3 +++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index f47cd4243..9231ce970 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -221,7 +221,6 @@ namespace FlaxEditor.Windows { if (!Editor.StateMachine.CurrentState.CanEditScene) return; - ShowContextMenu(node, location); } diff --git a/Source/Engine/UI/GUI/ContainerControl.cs b/Source/Engine/UI/GUI/ContainerControl.cs index 917d200da..c53307c65 100644 --- a/Source/Engine/UI/GUI/ContainerControl.cs +++ b/Source/Engine/UI/GUI/ContainerControl.cs @@ -356,7 +356,7 @@ namespace FlaxEngine.GUI for (int i = _children.Count - 1; i >= 0; i--) { var child = _children[i]; - if (IntersectsChildContent(child, point, out var childLocation)) + if (child.Visible && IntersectsChildContent(child, point, out var childLocation)) { var containerControl = child as ContainerControl; var childAtRecursive = containerControl?.GetChildAtRecursive(childLocation); diff --git a/Source/Engine/UI/GUI/Control.Bounds.cs b/Source/Engine/UI/GUI/Control.Bounds.cs index d76f82308..ec96f89f2 100644 --- a/Source/Engine/UI/GUI/Control.Bounds.cs +++ b/Source/Engine/UI/GUI/Control.Bounds.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using System.ComponentModel; namespace FlaxEngine.GUI { @@ -382,6 +383,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point. /// + [DefaultValue(0.0f)] [ExpandGroups, EditorDisplay("Transform"), EditorOrder(1040), Tooltip("The shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point.")] public Float2 Shear { @@ -398,6 +400,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the rotation angle (in degrees). Control is rotated around it's pivot point (middle of the control by default). /// + [DefaultValue(0.0f)] [ExpandGroups, EditorDisplay("Transform"), EditorOrder(1050), Tooltip("The control rotation angle (in degrees). Control is rotated around it's pivot point (middle of the control by default).")] public float Rotation { From 7ec490443eb641e9355f59792428aeab5a7aa26d Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 15 Mar 2024 09:25:19 -0500 Subject: [PATCH 35/68] Fix wrong default value type. --- Source/Engine/UI/GUI/Control.Bounds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Control.Bounds.cs b/Source/Engine/UI/GUI/Control.Bounds.cs index ec96f89f2..cbb3ba80b 100644 --- a/Source/Engine/UI/GUI/Control.Bounds.cs +++ b/Source/Engine/UI/GUI/Control.Bounds.cs @@ -383,7 +383,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point. /// - [DefaultValue(0.0f)] + [DefaultValue(typeof(Float2), "0,0")] [ExpandGroups, EditorDisplay("Transform"), EditorOrder(1040), Tooltip("The shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point.")] public Float2 Shear { From 7652feabfadbbda3f0b41b4a6c9f8e2edd72b4a4 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 15 Mar 2024 15:15:40 -0500 Subject: [PATCH 36/68] Clamp location of multiblend data. --- Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 2747e1023..1178f54de 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -617,8 +617,9 @@ namespace FlaxEditor.Surface.Archetypes public override void SetLocation(int index, Float2 location) { var dataA = (Float4)_node.Values[4 + index * 2]; + var ranges = (Float4)_node.Values[0]; - dataA.X = location.X; + dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y); _node.Values[4 + index * 2] = dataA; _node.Surface.MarkAsEdited(); @@ -750,9 +751,10 @@ namespace FlaxEditor.Surface.Archetypes public override void SetLocation(int index, Float2 location) { var dataA = (Float4)_node.Values[4 + index * 2]; + var ranges = (Float4)_node.Values[0]; - dataA.X = location.X; - dataA.Y = location.Y; + dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y); + dataA.Y = Mathf.Clamp(location.Y, ranges.Z, ranges.W); _node.Values[4 + index * 2] = dataA; _node.Surface.MarkAsEdited(); From f81e89d7d4ecbb31b8f736c41f959011ac6fe1fe Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 16 Mar 2024 16:44:57 +0100 Subject: [PATCH 37/68] Refactor timeline grid drawing to share code between Editor UI elements --- Source/Editor/GUI/CurveEditor.cs | 91 +++++--------------- Source/Editor/GUI/Timeline/GUI/Background.cs | 58 +++---------- Source/Editor/Utilities/Utils.cs | 57 ++++++++++++ 3 files changed, 91 insertions(+), 115 deletions(-) diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 141b3aa60..bc96cf3bc 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -431,7 +431,6 @@ namespace FlaxEditor.GUI /// protected CurveEditor() { - _tickStrengths = new float[TickSteps.Length]; Accessor.GetDefaultValue(out DefaultValue); var style = Style.Current; @@ -780,75 +779,31 @@ namespace FlaxEditor.GUI return _mainPanel.PointToParent(point); } - private void DrawAxis(Float2 axis, ref Rectangle viewRect, float min, float max, float pixelRange) + private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange) { - int minDistanceBetweenTicks = 20; - int maxDistanceBetweenTicks = 60; - var range = max - min; - - // Find the strength for each modulo number tick marker - int smallestTick = 0; - int biggestTick = TickSteps.Length - 1; - for (int i = TickSteps.Length - 1; i >= 0; i--) + Utilities.Utils.DrawCurveTicks((float tick, float strength) => { - // Calculate how far apart these modulo tick steps are spaced - float tickSpacing = TickSteps[i] * pixelRange / range; + var p = PointFromKeyframes(axis * tick, ref viewRect); - // Calculate the strength of the tick markers based on the spacing - _tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks)); + // Draw line + var lineRect = new Rectangle + ( + viewRect.Location + (p - 0.5f) * axis, + Float2.Lerp(viewRect.Size, Float2.One, axis) + ); + Render2D.FillRectangle(lineRect, _linesColor.AlphaMultiplied(strength)); - // Beyond threshold the ticks don't get any bigger or fatter - if (_tickStrengths[i] >= 1) - biggestTick = i; - - // Do not show small tick markers - if (tickSpacing <= minDistanceBetweenTicks) - { - smallestTick = i; - break; - } - } - - // Draw all tick levels - int tickLevels = biggestTick - smallestTick + 1; - for (int level = 0; level < tickLevels; level++) - { - float strength = _tickStrengths[smallestTick + level]; - if (strength <= Mathf.Epsilon) - continue; - - // Draw all ticks - int l = Mathf.Clamp(smallestTick + level, 0, TickSteps.Length - 1); - int startTick = Mathf.FloorToInt(min / TickSteps[l]); - int endTick = Mathf.CeilToInt(max / TickSteps[l]); - for (int i = startTick; i <= endTick; i++) - { - if (l < biggestTick && (i % Mathf.RoundToInt(TickSteps[l + 1] / TickSteps[l]) == 0)) - continue; - - var tick = i * TickSteps[l]; - var p = PointFromKeyframes(axis * tick, ref viewRect); - - // Draw line - var lineRect = new Rectangle - ( - viewRect.Location + (p - 0.5f) * axis, - Float2.Lerp(viewRect.Size, Float2.One, axis) - ); - Render2D.FillRectangle(lineRect, _linesColor.AlphaMultiplied(strength)); - - // Draw label - string label = tick.ToString(CultureInfo.InvariantCulture); - var labelRect = new Rectangle - ( - viewRect.X + 4.0f + (p.X * axis.X), - viewRect.Y - LabelsSize + (p.Y * axis.Y) + (viewRect.Size.Y * axis.X), - 50, - LabelsSize - ); - Render2D.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); - } - } + // Draw label + string label = tick.ToString(CultureInfo.InvariantCulture); + var labelRect = new Rectangle + ( + viewRect.X + 4.0f + (p.X * axis.X), + viewRect.Y - LabelsSize + (p.Y * axis.Y) + (viewRect.Size.Y * axis.X), + 50, + LabelsSize + ); + Render2D.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); + }, TickSteps, ref _tickStrengths, min, max, pixelRange); } /// @@ -890,9 +845,9 @@ namespace FlaxEditor.GUI Render2D.PushClip(ref viewRect); if ((ShowAxes & UseMode.Vertical) == UseMode.Vertical) - DrawAxis(Float2.UnitX, ref viewRect, min.X, max.X, pixelRange.X); + DrawAxis(Float2.UnitX, viewRect, min.X, max.X, pixelRange.X); if ((ShowAxes & UseMode.Horizontal) == UseMode.Horizontal) - DrawAxis(Float2.UnitY, ref viewRect, min.Y, max.Y, pixelRange.Y); + DrawAxis(Float2.UnitY, viewRect, min.Y, max.Y, pixelRange.Y); Render2D.PopClip(); } diff --git a/Source/Editor/GUI/Timeline/GUI/Background.cs b/Source/Editor/GUI/Timeline/GUI/Background.cs index 4ec634a0b..7bda8f4c0 100644 --- a/Source/Editor/GUI/Timeline/GUI/Background.cs +++ b/Source/Editor/GUI/Timeline/GUI/Background.cs @@ -28,7 +28,6 @@ namespace FlaxEditor.GUI.Timeline.GUI { _timeline = timeline; _tickSteps = Utilities.Utils.CurveTickSteps; - _tickStrengths = new float[_tickSteps.Length]; } private void UpdateSelectionRectangle() @@ -173,55 +172,20 @@ namespace FlaxEditor.GUI.Timeline.GUI var rightFrame = Mathf.Ceil((right - Timeline.StartOffset) / zoom) * _timeline.FramesPerSecond; var min = leftFrame; var max = rightFrame; - int smallestTick = 0; - int biggestTick = _tickSteps.Length - 1; - for (int i = _tickSteps.Length - 1; i >= 0; i--) - { - // Calculate how far apart these modulo tick steps are spaced - float tickSpacing = _tickSteps[i] * _timeline.Zoom; - - // Calculate the strength of the tick markers based on the spacing - _tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks)); - - // Beyond threshold the ticks don't get any bigger or fatter - if (_tickStrengths[i] >= 1) - biggestTick = i; - - // Do not show small tick markers - if (tickSpacing <= minDistanceBetweenTicks) - { - smallestTick = i; - break; - } - } - int tickLevels = biggestTick - smallestTick + 1; // Draw vertical lines for time axis - for (int level = 0; level < tickLevels; level++) + var pixelsInRange = _timeline.Zoom; + var pixelRange = pixelsInRange * (max - min); + var tickRange = Utilities.Utils.DrawCurveTicks((float tick, float strength) => { - float strength = _tickStrengths[smallestTick + level]; - if (strength <= Mathf.Epsilon) - continue; - - // Draw all ticks - int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1); - var lStep = _tickSteps[l]; - var lNextStep = _tickSteps[l + 1]; - int startTick = Mathf.FloorToInt(min / lStep); - int endTick = Mathf.CeilToInt(max / lStep); - Color lineColor = style.ForegroundDisabled.RGBMultiplied(0.7f).AlphaMultiplied(strength); - for (int i = startTick; i <= endTick; i++) - { - if (l < biggestTick && (i % Mathf.RoundToInt(lNextStep / lStep) == 0)) - continue; - var tick = i * lStep; - var time = tick / _timeline.FramesPerSecond; - var x = time * zoom + Timeline.StartOffset; - - // Draw line - Render2D.FillRectangle(new Rectangle(x - 0.5f, 0, 1.0f, height), lineColor); - } - } + var time = tick / _timeline.FramesPerSecond; + var x = time * zoom + Timeline.StartOffset; + var lineColor = style.ForegroundDisabled.RGBMultiplied(0.7f).AlphaMultiplied(strength); + Render2D.FillRectangle(new Rectangle(x - 0.5f, 0, 1.0f, height), lineColor); + }, _tickSteps, ref _tickStrengths, min, max, pixelRange, minDistanceBetweenTicks, maxDistanceBetweenTicks); + var smallestTick = tickRange.X; + var biggestTick = tickRange.Y; + var tickLevels = biggestTick - smallestTick + 1; // Draw selection rectangle if (_isSelecting) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index b403c1489..576c30f89 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -243,6 +243,63 @@ namespace FlaxEditor.Utilities 500000, 1000000, 5000000, 10000000, 100000000 }; + internal delegate void DrawCurveTick(float tick, float strength); + + internal static Int2 DrawCurveTicks(DrawCurveTick drawTick, float[] tickSteps, ref float[] tickStrengths, float min, float max, float pixelRange, float minDistanceBetweenTicks = 20, float maxDistanceBetweenTicks = 60) + { + if (tickStrengths == null || tickStrengths.Length != tickSteps.Length) + tickStrengths = new float[tickSteps.Length]; + + // Find the strength for each modulo number tick marker + var pixelsInRange = pixelRange / (max - min); + var smallestTick = 0; + var biggestTick = tickSteps.Length - 1; + for (int i = tickSteps.Length - 1; i >= 0; i--) + { + // Calculate how far apart these modulo tick steps are spaced + float tickSpacing = tickSteps[i] * pixelsInRange; + + // Calculate the strength of the tick markers based on the spacing + tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks)); + + // Beyond threshold the ticks don't get any bigger or fatter + if (tickStrengths[i] >= 1) + biggestTick = i; + + // Do not show small tick markers + if (tickSpacing <= minDistanceBetweenTicks) + { + smallestTick = i; + break; + } + } + var tickLevels = biggestTick - smallestTick + 1; + + // Draw all tick levels + for (int level = 0; level < tickLevels; level++) + { + float strength = tickStrengths[smallestTick + level]; + if (strength <= Mathf.Epsilon) + continue; + + // Draw all ticks + int l = Mathf.Clamp(smallestTick + level, 0, tickSteps.Length - 1); + var lStep = tickSteps[l]; + var lNextStep = tickSteps[l + 1]; + int startTick = Mathf.FloorToInt(min / lStep); + int endTick = Mathf.CeilToInt(max / lStep); + for (int i = startTick; i <= endTick; i++) + { + if (l < biggestTick && (i % Mathf.RoundToInt(lNextStep / lStep) == 0)) + continue; + var tick = i * lStep; + drawTick(tick, strength); + } + } + + return new Int2(smallestTick, biggestTick); + } + /// /// Determines whether the specified path string contains any invalid character. /// From 8d149b94f18fb90f70665e0b9cd35a9e9ff3685c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 16 Mar 2024 16:46:10 +0100 Subject: [PATCH 38/68] Refactor editor viepwort camera to unify focusing on selected objects --- Source/Editor/Viewport/Cameras/FPSCamera.cs | 84 +----------------- .../Editor/Viewport/Cameras/ViewportCamera.cs | 87 +++++++++++++++++++ .../Viewport/MainEditorGizmoViewport.cs | 9 +- .../Editor/Viewport/PrefabWindowViewport.cs | 11 +-- 4 files changed, 92 insertions(+), 99 deletions(-) diff --git a/Source/Editor/Viewport/Cameras/FPSCamera.cs b/Source/Editor/Viewport/Cameras/FPSCamera.cs index 83fc49cd6..45770b007 100644 --- a/Source/Editor/Viewport/Cameras/FPSCamera.cs +++ b/Source/Editor/Viewport/Cameras/FPSCamera.cs @@ -6,9 +6,7 @@ using Real = System.Double; using Real = System.Single; #endif -using System.Collections.Generic; using FlaxEditor.Gizmo; -using FlaxEditor.SceneGraph; using FlaxEngine; namespace FlaxEditor.Viewport.Cameras @@ -85,86 +83,8 @@ namespace FlaxEditor.Viewport.Cameras _moveStartTime = Time.UnscaledGameTime; } - /// - /// Moves the viewport to visualize the actor. - /// - /// The actor to preview. - public void ShowActor(Actor actor) - { - Editor.GetActorEditorSphere(actor, out BoundingSphere sphere); - ShowSphere(ref sphere); - } - - /// - /// Moves the viewport to visualize selected actors. - /// - /// The actors to show. - /// The used orientation. - public void ShowActor(Actor actor, ref Quaternion orientation) - { - Editor.GetActorEditorSphere(actor, out BoundingSphere sphere); - ShowSphere(ref sphere, ref orientation); - } - - /// - /// Moves the viewport to visualize selected actors. - /// - /// The actors to show. - public void ShowActors(List selection) - { - if (selection.Count == 0) - return; - - BoundingSphere mergesSphere = BoundingSphere.Empty; - for (int i = 0; i < selection.Count; i++) - { - selection[i].GetEditorSphere(out var sphere); - BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere); - } - - if (mergesSphere == BoundingSphere.Empty) - return; - ShowSphere(ref mergesSphere); - } - - /// - /// Moves the viewport to visualize selected actors. - /// - /// The actors to show. - /// The used orientation. - public void ShowActors(List selection, ref Quaternion orientation) - { - if (selection.Count == 0) - return; - - BoundingSphere mergesSphere = BoundingSphere.Empty; - for (int i = 0; i < selection.Count; i++) - { - selection[i].GetEditorSphere(out var sphere); - BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere); - } - - if (mergesSphere == BoundingSphere.Empty) - return; - ShowSphere(ref mergesSphere, ref orientation); - } - - /// - /// Moves the camera to visualize given world area defined by the sphere. - /// - /// The sphere. - public void ShowSphere(ref BoundingSphere sphere) - { - var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f); - ShowSphere(ref sphere, ref q); - } - - /// - /// Moves the camera to visualize given world area defined by the sphere. - /// - /// The sphere. - /// The camera orientation. - public void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation) + /// + public override void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation) { Vector3 position; if (Viewport.UseOrthographicProjection) diff --git a/Source/Editor/Viewport/Cameras/ViewportCamera.cs b/Source/Editor/Viewport/Cameras/ViewportCamera.cs index 5e25a29bb..f8019a481 100644 --- a/Source/Editor/Viewport/Cameras/ViewportCamera.cs +++ b/Source/Editor/Viewport/Cameras/ViewportCamera.cs @@ -6,6 +6,9 @@ using Real = System.Double; using Real = System.Single; #endif +using System.Collections.Generic; +using FlaxEditor.Gizmo; +using FlaxEditor.SceneGraph; using FlaxEngine; namespace FlaxEditor.Viewport.Cameras @@ -33,6 +36,90 @@ namespace FlaxEditor.Viewport.Cameras /// public virtual bool UseMovementSpeed => true; + /// + /// Focuses the viewport on the current selection of the gizmo. + /// + /// The gizmo collection (from viewport). + /// The target view orientation. + public virtual void FocusSelection(GizmosCollection gizmos, ref Quaternion orientation) + { + var transformGizmo = gizmos.Get(); + if (transformGizmo == null || transformGizmo.SelectedParents.Count == 0) + return; + if (gizmos.Active != null) + { + var gizmoBounds = gizmos.Active.FocusBounds; + if (gizmoBounds != BoundingSphere.Empty) + { + ShowSphere(ref gizmoBounds, ref orientation); + return; + } + } + ShowActors(transformGizmo.SelectedParents, ref orientation); + } + + /// + /// Moves the viewport to visualize the actor. + /// + /// The actor to preview. + public virtual void ShowActor(Actor actor) + { + Editor.GetActorEditorSphere(actor, out BoundingSphere sphere); + ShowSphere(ref sphere); + } + + /// + /// Moves the viewport to visualize selected actors. + /// + /// The actors to show. + public void ShowActors(List selection) + { + var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f); + ShowActors(selection, ref q); + } + + /// + /// Moves the viewport to visualize selected actors. + /// + /// The actors to show. + /// The used orientation. + public virtual void ShowActors(List selection, ref Quaternion orientation) + { + if (selection.Count == 0) + return; + + BoundingSphere mergesSphere = BoundingSphere.Empty; + for (int i = 0; i < selection.Count; i++) + { + selection[i].GetEditorSphere(out var sphere); + BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere); + } + + if (mergesSphere == BoundingSphere.Empty) + return; + ShowSphere(ref mergesSphere, ref orientation); + } + + /// + /// Moves the camera to visualize given world area defined by the sphere. + /// + /// The sphere. + public void ShowSphere(ref BoundingSphere sphere) + { + var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f); + ShowSphere(ref sphere, ref q); + } + + /// + /// Moves the camera to visualize given world area defined by the sphere. + /// + /// The sphere. + /// The camera orientation. + public virtual void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation) + { + SetArcBallView(orientation, sphere.Center, sphere.Radius); + } + /// /// Sets view orientation and position to match the arc ball camera style view for the given target object bounds. /// diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 1e63888bf..6d8b572a4 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -770,14 +770,7 @@ namespace FlaxEditor.Viewport /// The target view orientation. public void FocusSelection(ref Quaternion orientation) { - if (TransformGizmo.SelectedParents.Count == 0) - return; - - var gizmoBounds = Gizmos.Active.FocusBounds; - if (gizmoBounds != BoundingSphere.Empty) - ((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation); - else - ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation); + ViewportCamera.FocusSelection(Gizmos, ref orientation); } /// diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index f66d4e861..4166d6163 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -309,7 +309,7 @@ namespace FlaxEditor.Viewport public void ShowSelectedActors() { var orient = ViewOrientation; - ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orient); + ViewportCamera.ShowActors(TransformGizmo.SelectedParents, ref orient); } /// @@ -721,14 +721,7 @@ namespace FlaxEditor.Viewport /// The target view orientation. public void FocusSelection(ref Quaternion orientation) { - if (TransformGizmo.SelectedParents.Count == 0) - return; - - var gizmoBounds = Gizmos.Active.FocusBounds; - if (gizmoBounds != BoundingSphere.Empty) - ((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation); - else - ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation); + ViewportCamera.FocusSelection(Gizmos, ref orientation); } /// From c6aa18c47cff37e1f10361c93908ebbd68e9ece4 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 16 Mar 2024 13:20:18 -0500 Subject: [PATCH 39/68] Remove import UI on Animation not created by an import. --- Source/Editor/Windows/Assets/AnimationWindow.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs index ae87826b0..2411daf86 100644 --- a/Source/Editor/Windows/Assets/AnimationWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationWindow.cs @@ -186,6 +186,10 @@ namespace FlaxEditor.Windows.Assets base.Initialize(layout); + // Ignore import settings GUI if the type is not animation. This removes the import UI if the animation asset was not created using an import. + if (proxy.ImportSettings.Settings.Type != FlaxEngine.Tools.ModelTool.ModelType.Animation) + return; + // Import Settings { var group = layout.Group("Import Settings"); From c60244878d33cd719eaa342bcea83d0bb6500abc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 16 Mar 2024 22:00:40 +0100 Subject: [PATCH 40/68] Add **UI Control gizmo for editing UIs** --- Source/Editor/Gizmo/IGizmoOwner.cs | 5 + Source/Editor/Gizmo/TransformGizmo.cs | 5 + Source/Editor/Gizmo/UIEditorGizmo.cs | 724 ++++++++++++++++++ Source/Editor/Surface/VisjectSurface.Draw.cs | 10 +- Source/Editor/Viewport/EditorGizmoViewport.cs | 3 + Source/Editor/Viewport/EditorViewport.cs | 5 +- .../Viewport/MainEditorGizmoViewport.cs | 18 +- .../Editor/Viewport/PrefabWindowViewport.cs | 114 ++- .../Editor/Viewport/Previews/AssetPreview.cs | 2 +- .../Editor/Viewport/Previews/PrefabPreview.cs | 24 +- .../Windows/Assets/PrefabWindow.Hierarchy.cs | 3 +- Source/Editor/Windows/Assets/PrefabWindow.cs | 5 +- Source/Editor/Windows/GameWindow.cs | 169 +--- .../Windows/SceneTreeWindow.ContextMenu.cs | 4 +- Source/Engine/UI/GUI/Control.Bounds.cs | 2 +- 15 files changed, 871 insertions(+), 222 deletions(-) create mode 100644 Source/Editor/Gizmo/UIEditorGizmo.cs diff --git a/Source/Editor/Gizmo/IGizmoOwner.cs b/Source/Editor/Gizmo/IGizmoOwner.cs index 7237d724b..0a5c520a7 100644 --- a/Source/Editor/Gizmo/IGizmoOwner.cs +++ b/Source/Editor/Gizmo/IGizmoOwner.cs @@ -117,5 +117,10 @@ namespace FlaxEditor.Gizmo /// /// The new actor to spawn. void Spawn(Actor actor); + + /// + /// Opens the context menu at the current mouse location (using current selection). + /// + void OpenContextMenu(); } } diff --git a/Source/Editor/Gizmo/TransformGizmo.cs b/Source/Editor/Gizmo/TransformGizmo.cs index 64f969990..cd5e16e92 100644 --- a/Source/Editor/Gizmo/TransformGizmo.cs +++ b/Source/Editor/Gizmo/TransformGizmo.cs @@ -42,6 +42,11 @@ namespace FlaxEditor.Gizmo /// public Action Duplicate; + /// + /// Gets the array of selected objects. + /// + public List Selection => _selection; + /// /// Gets the array of selected parent objects (as actors). /// diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs new file mode 100644 index 000000000..4fd059202 --- /dev/null +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -0,0 +1,724 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +using System.Collections.Generic; +using System.Linq; +using FlaxEditor.Gizmo; +using FlaxEditor.SceneGraph; +using FlaxEditor.SceneGraph.Actors; +using FlaxEditor.Viewport.Cameras; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor +{ + /// + /// UI editor camera. + /// + [HideInEditor] + internal sealed class UIEditorCamera : ViewportCamera + { + public UIEditorRoot UIEditor; + + public void ShowActors(IEnumerable actors) + { + // Calculate bounds of all selected objects + var areaRect = Rectangle.Empty; + var root = UIEditor.UIRoot; + foreach (var actor in actors) + { + Rectangle bounds; + if (actor is UIControl uiControl && uiControl.HasControl && uiControl.IsActive) + { + var control = uiControl.Control; + bounds = control.EditorBounds; + + var ul = control.PointToParent(root, bounds.UpperLeft); + var ur = control.PointToParent(root, bounds.UpperRight); + var bl = control.PointToParent(root, bounds.BottomLeft); + var br = control.PointToParent(root, bounds.BottomRight); + + var min = Float2.Min(Float2.Min(ul, ur), Float2.Min(bl, br)); + var max = Float2.Max(Float2.Max(ul, ur), Float2.Max(bl, br)); + bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero)); + } + else if (actor is UICanvas uiCanvas && uiCanvas.IsActive && uiCanvas.GUI.Parent == root) + { + bounds = uiCanvas.GUI.Bounds; + } + else + continue; + + if (areaRect == Rectangle.Empty) + areaRect = bounds; + else + areaRect = Rectangle.Union(areaRect, bounds); + } + if (areaRect == Rectangle.Empty) + return; + + // Add margin + areaRect = areaRect.MakeExpanded(100.0f); + + // Show bounds + UIEditor.ViewScale = (UIEditor.Size / areaRect.Size).MinValue * 0.95f; + UIEditor.ViewCenterPosition = areaRect.Center; + } + + public override void FocusSelection(GizmosCollection gizmos, ref Quaternion orientation) + { + ShowActors(gizmos.Get().Selection, ref orientation); + } + + public override void ShowActor(Actor actor) + { + ShowActors(new[] { actor }); + } + + public override void ShowActors(List selection, ref Quaternion orientation) + { + ShowActors(selection.ConvertAll(x => (Actor)x.EditableObject)); + } + + public override void UpdateView(float dt, ref Vector3 moveDelta, ref Float2 mouseDelta, out bool centerMouse) + { + centerMouse = false; + } + } + + /// + /// Root control for UI Controls presentation in the game/prefab viewport. + /// + [HideInEditor] + internal class UIEditorRoot : InputsPassThrough + { + /// + /// View for the UI structure to be linked in for camera zoom and panning operations. + /// + private sealed class View : ContainerControl + { + public View(UIEditorRoot parent) + { + AutoFocus = false; + ClipChildren = false; + CullChildren = false; + Pivot = Float2.Zero; + Size = new Float2(1920, 1080); + Parent = parent; + } + + public override bool RayCast(ref Float2 location, out Control hit) + { + // Ignore self + return RayCastChildren(ref location, out hit); + } + + public override bool IntersectsContent(ref Float2 locationParent, out Float2 location) + { + location = PointFromParent(ref locationParent); + return true; + } + + public override void DrawSelf() + { + var uiRoot = (UIEditorRoot)Parent; + if (!uiRoot.EnableBackground) + return; + + // Draw canvas area + var bounds = new Rectangle(Float2.Zero, Size); + Render2D.FillRectangle(bounds, new Color(0, 0, 0, 0.2f)); + } + } + + private bool _mouseMovesControl, _mouseMovesView; + private Float2 _mouseMovesPos, _moveSnapDelta; + private float _mouseMoveSum; + private UndoMultiBlock _undoBlock; + private View _view; + private float[] _gridTickSteps = Utilities.Utils.CurveTickSteps, _gridTickStrengths; + + /// + /// True if enable displaying UI editing background and grid elements. + /// + public virtual bool EnableBackground => false; + + /// + /// True if enable selecting controls with mouse button. + /// + public virtual bool EnableSelecting => false; + + /// + /// True if enable panning and zooming the view. + /// + public bool EnableCamera => _view != null; + + /// + /// Transform gizmo to use sync with (selection, snapping, transformation settings). + /// + public virtual TransformGizmo TransformGizmo => null; + + /// + /// The root control for controls to be linked in. + /// + public readonly ContainerControl UIRoot; + + internal Float2 ViewPosition + { + get => _view.Location / -ViewScale; + set => _view.Location = value * -ViewScale; + } + + internal Float2 ViewCenterPosition + { + get => (_view.Location - Size * 0.5f) / -ViewScale; + set => _view.Location = Size * 0.5f + value * -ViewScale; + } + + internal float ViewScale + { + get => _view.Scale.X; + set + { + value = Mathf.Clamp(value, 0.1f, 4.0f); + _view.Scale = new Float2(value); + } + } + + public UIEditorRoot(bool enableCamera = false) + { + AnchorPreset = AnchorPresets.StretchAll; + Offsets = Margin.Zero; + AutoFocus = false; + UIRoot = this; + CullChildren = false; + ClipChildren = true; + if (enableCamera) + { + _view = new View(this); + UIRoot = _view; + } + } + + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (base.OnMouseDown(location, button)) + return true; + + var transformGizmo = TransformGizmo; + var owner = transformGizmo?.Owner; + if (EnableSelecting && owner != null && !_mouseMovesControl && button == MouseButton.Left) + { + // Raycast the control under the mouse + var mousePos = PointFromWindow(RootWindow.MousePosition); + if (RayCastControl(ref mousePos, out var hitControl)) + { + var uiControlNode = FindUIControlNode(hitControl); + if (uiControlNode != null) + { + // Select node (with additive mode) + var selection = new List(); + if (Root.GetKey(KeyboardKeys.Control)) + { + // Add/remove from selection + selection.AddRange(transformGizmo.Selection); + if (transformGizmo.Selection.Contains(uiControlNode)) + selection.Remove(uiControlNode); + else + selection.Add(uiControlNode); + } + else + { + // Select + selection.Add(uiControlNode); + } + owner.Select(selection); + + // Initialize control movement + _mouseMovesControl = true; + _mouseMovesPos = location; + _mouseMoveSum = 0.0f; + _moveSnapDelta = Float2.Zero; + Focus(); + StartMouseCapture(); + return true; + } + } + } + if (EnableCamera && (button == MouseButton.Right || button == MouseButton.Middle)) + { + // Initialize surface movement + _mouseMovesView = true; + _mouseMovesPos = location; + _mouseMoveSum = 0.0f; + Focus(); + StartMouseCapture(); + return true; + } + + return false; + } + + public override void OnMouseMove(Float2 location) + { + base.OnMouseMove(location); + + var transformGizmo = TransformGizmo; + if (_mouseMovesControl && transformGizmo != null) + { + // Calculate transform delta + var delta = location - _mouseMovesPos; + if (transformGizmo.TranslationSnapEnable || transformGizmo.Owner.UseSnapping) + { + _moveSnapDelta += delta; + delta = Float2.SnapToGrid(_moveSnapDelta, new Float2(transformGizmo.TranslationSnapValue * ViewScale)); + _moveSnapDelta -= delta; + } + + // Move selected controls + if (delta.LengthSquared > 0.0f) + { + StartUndo(); + var moved = false; + var moveLocation = _mouseMovesPos + delta; + var selection = transformGizmo.Selection; + for (var i = 0; i < selection.Count; i++) + { + if (IsValidControl(selection[i], out var uiControl)) + { + // Move control (handle any control transformations by moving in editor's local-space) + var control = uiControl.Control; + var localLocation = control.LocalLocation; + var pointOrigin = control.Parent ?? control; + var startPos = pointOrigin.PointFromParent(this, _mouseMovesPos); + var endPos = pointOrigin.PointFromParent(this, moveLocation); + var uiControlDelta = endPos - startPos; + control.LocalLocation = localLocation + uiControlDelta; + + // Don't move if layout doesn't allow it + if (control.Parent != null) + control.Parent.PerformLayout(); + else + control.PerformLayout(); + + // Check if control was moved (parent container could block it) + if (localLocation != control.LocalLocation) + moved = true; + } + } + _mouseMovesPos = location; + _mouseMoveSum += delta.Length; + if (moved) + Cursor = CursorType.SizeAll; + } + } + if (_mouseMovesView) + { + // Move view + var delta = location - _mouseMovesPos; + if (delta.LengthSquared > 4.0f) + { + _mouseMovesPos = location; + _mouseMoveSum += delta.Length; + _view.Location += delta; + Cursor = CursorType.SizeAll; + } + } + } + + public override bool OnMouseUp(Float2 location, MouseButton button) + { + EndMovingControls(); + if (_mouseMovesView) + { + EndMovingView(); + if (button == MouseButton.Right && _mouseMoveSum < 2.0f) + TransformGizmo.Owner.OpenContextMenu(); + } + + return base.OnMouseUp(location, button); + } + + public override void OnMouseLeave() + { + EndMovingControls(); + EndMovingView(); + + base.OnMouseLeave(); + } + + public override void OnLostFocus() + { + EndMovingControls(); + EndMovingView(); + + base.OnLostFocus(); + } + + public override bool OnMouseWheel(Float2 location, float delta) + { + if (base.OnMouseWheel(location, delta)) + return true; + + if (EnableCamera && !_mouseMovesControl) + { + // Zoom view + var nextViewScale = ViewScale + delta * 0.1f; + if (delta > 0 && !_mouseMovesControl) + { + // Scale towards mouse when zooming in + var nextCenterPosition = ViewPosition + location / ViewScale; + ViewScale = nextViewScale; + ViewPosition = nextCenterPosition - (location / ViewScale); + } + else + { + // Scale while keeping center position when zooming out or when dragging view + var viewCenter = ViewCenterPosition; + ViewScale = nextViewScale; + ViewCenterPosition = viewCenter; + } + + return true; + } + + return false; + } + + public override void Draw() + { + if (EnableBackground) + { + // Draw background + Surface.VisjectSurface.DrawBackgroundDefault(Editor.Instance.UI.VisjectSurfaceBackground, Width, Height); + + // Draw grid + var viewRect = GetClientArea(); + var upperLeft = _view.PointFromParent(viewRect.Location); + var bottomRight = _view.PointFromParent(viewRect.Size); + var min = Float2.Min(upperLeft, bottomRight); + var max = Float2.Max(upperLeft, bottomRight); + var pixelRange = (max - min) * ViewScale; + Render2D.PushClip(ref viewRect); + DrawAxis(Float2.UnitX, viewRect, min.X, max.X, pixelRange.X); + DrawAxis(Float2.UnitY, viewRect, min.Y, max.Y, pixelRange.Y); + Render2D.PopClip(); + } + + base.Draw(); + + bool drawAnySelectedControl = false; + var transformGizmo = TransformGizmo; + if (transformGizmo != null) + { + // Selected UI controls outline + var selection = transformGizmo.Selection; + for (var i = 0; i < selection.Count; i++) + { + if (IsValidControl(selection[i], out var controlActor)) + { + DrawControlBounds(controlActor.Control, true, ref drawAnySelectedControl); + // TODO: draw anchors + } + } + } + if (EnableSelecting && !_mouseMovesControl && IsMouseOver) + { + // Highlight control under mouse for easier selecting (except if already selected) + var mousePos = PointFromWindow(RootWindow.MousePosition); + if (RayCastControl(ref mousePos, out var hitControl) && + (transformGizmo == null || !transformGizmo.Selection.Any(x => x.EditableObject is UIControl controlActor && controlActor.Control == hitControl))) + { + DrawControlBounds(hitControl, false, ref drawAnySelectedControl); + } + } + if (drawAnySelectedControl) + Render2D.PopTransform(); + + if (EnableBackground) + { + // Draw border + if (ContainsFocus) + { + Render2D.DrawRectangle(new Rectangle(1, 1, Width - 2, Height - 2), Editor.IsPlayMode ? Color.OrangeRed : Style.Current.BackgroundSelected); + } + } + } + + public override void OnDestroy() + { + if (IsDisposing) + return; + EndMovingControls(); + EndMovingView(); + + base.OnDestroy(); + } + + private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange) + { + var style = Style.Current; + var linesColor = style.ForegroundDisabled.RGBMultiplied(0.5f); + var labelsColor = style.ForegroundDisabled; + var labelsSize = 10.0f; + Utilities.Utils.DrawCurveTicks((float tick, float strength) => + { + var p = _view.PointToParent(axis * tick); + + // Draw line + var lineRect = new Rectangle + ( + viewRect.Location + (p - 0.5f) * axis, + Float2.Lerp(viewRect.Size, Float2.One, axis) + ); + Render2D.FillRectangle(lineRect, linesColor.AlphaMultiplied(strength)); + + // Draw label + string label = tick.ToString(System.Globalization.CultureInfo.InvariantCulture); + var labelRect = new Rectangle + ( + viewRect.X + 4.0f + (p.X * axis.X), + viewRect.Y - labelsSize + (p.Y * axis.Y) + (viewRect.Size.Y * axis.X), + 50, + labelsSize + ); + Render2D.DrawText(style.FontSmall, label, labelRect, labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); + }, _gridTickSteps, ref _gridTickStrengths, min, max, pixelRange); + } + + private void DrawControlBounds(Control control, bool selection, ref bool drawAnySelectedControl) + { + if (!drawAnySelectedControl) + { + drawAnySelectedControl = true; + Render2D.PushTransform(ref _cachedTransform); + } + var options = Editor.Instance.Options.Options.Visual; + var bounds = control.EditorBounds; + var ul = control.PointToParent(this, bounds.UpperLeft); + var ur = control.PointToParent(this, bounds.UpperRight); + var bl = control.PointToParent(this, bounds.BottomLeft); + var br = control.PointToParent(this, bounds.BottomRight); + var color = selection ? options.SelectionOutlineColor0 : Style.Current.SelectionBorder; +#if false + // AABB + var min = Float2.Min(Float2.Min(ul, ur), Float2.Min(bl, br)); + var max = Float2.Max(Float2.Max(ul, ur), Float2.Max(bl, br)); + bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero)); + Render2D.DrawRectangle(bounds, color, options.UISelectionOutlineSize); +#else + // OBB + Render2D.DrawLine(ul, ur, color, options.UISelectionOutlineSize); + Render2D.DrawLine(ur, br, color, options.UISelectionOutlineSize); + Render2D.DrawLine(br, bl, color, options.UISelectionOutlineSize); + Render2D.DrawLine(bl, ul, color, options.UISelectionOutlineSize); +#endif + } + + private bool IsValidControl(SceneGraphNode node, out UIControl uiControl) + { + uiControl = null; + if (node.EditableObject is UIControl controlActor) + uiControl = controlActor; + return uiControl != null && + uiControl.Control != null && + uiControl.Control.VisibleInHierarchy && + uiControl.Control.RootWindow != null; + } + + private bool RayCastControl(ref Float2 location, out Control hit) + { +#if false + // Raycast only controls with content (eg. skips transparent panels) + return RayCastChildren(ref location, out hit); +#else + // Find any control under mouse (hierarchical) + hit = GetChildAtRecursive(location); + if (hit is View) + hit = null; + return hit != null; +#endif + } + + private UIControlNode FindUIControlNode(Control control) + { + return FindUIControlNode(TransformGizmo.Owner.SceneGraphRoot, control); + } + + private UIControlNode FindUIControlNode(SceneGraphNode node, Control control) + { + var result = node as UIControlNode; + if (result != null && ((UIControl)result.Actor).Control == control) + return result; + foreach (var e in node.ChildNodes) + { + result = FindUIControlNode(e, control); + if (result != null) + return result; + } + return null; + } + + private void StartUndo() + { + var undo = TransformGizmo?.Owner?.Undo; + if (undo == null || _undoBlock != null) + return; + _undoBlock = new UndoMultiBlock(undo, TransformGizmo.Selection.ConvertAll(x => x.EditableObject), "Edit control"); + } + + private void EndUndo() + { + if (_undoBlock == null) + return; + _undoBlock.Dispose(); + _undoBlock = null; + } + + private void EndMovingControls() + { + if (!_mouseMovesControl) + return; + _mouseMovesControl = false; + EndMouseCapture(); + Cursor = CursorType.Default; + EndUndo(); + } + + private void EndMovingView() + { + if (!_mouseMovesView) + return; + _mouseMovesView = false; + EndMouseCapture(); + Cursor = CursorType.Default; + } + } + + /// + /// Control that can optionally disable inputs to the children. + /// + [HideInEditor] + internal class InputsPassThrough : ContainerControl + { + private bool _isMouseOver; + + /// + /// True if enable input events passing to the UI. + /// + public virtual bool EnableInputs => true; + + public override bool RayCast(ref Float2 location, out Control hit) + { + return RayCastChildren(ref location, out hit); + } + + public override bool ContainsPoint(ref Float2 location, bool precise = false) + { + if (precise) + return false; + return base.ContainsPoint(ref location, precise); + } + + public override bool OnCharInput(char c) + { + if (!EnableInputs) + return false; + return base.OnCharInput(c); + } + + public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) + { + if (!EnableInputs) + return DragDropEffect.None; + return base.OnDragDrop(ref location, data); + } + + public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) + { + if (!EnableInputs) + return DragDropEffect.None; + return base.OnDragEnter(ref location, data); + } + + public override void OnDragLeave() + { + if (!EnableInputs) + return; + base.OnDragLeave(); + } + + public override DragDropEffect OnDragMove(ref Float2 location, DragData data) + { + if (!EnableInputs) + return DragDropEffect.None; + return base.OnDragMove(ref location, data); + } + + public override bool OnKeyDown(KeyboardKeys key) + { + if (!EnableInputs) + return false; + return base.OnKeyDown(key); + } + + public override void OnKeyUp(KeyboardKeys key) + { + if (!EnableInputs) + return; + base.OnKeyUp(key); + } + + public override bool OnMouseDoubleClick(Float2 location, MouseButton button) + { + if (!EnableInputs) + return false; + return base.OnMouseDoubleClick(location, button); + } + + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (!EnableInputs) + return false; + return base.OnMouseDown(location, button); + } + + public override bool IsMouseOver => _isMouseOver; + + public override void OnMouseEnter(Float2 location) + { + _isMouseOver = true; + if (!EnableInputs) + return; + base.OnMouseEnter(location); + } + + public override void OnMouseLeave() + { + _isMouseOver = false; + if (!EnableInputs) + return; + base.OnMouseLeave(); + } + + public override void OnMouseMove(Float2 location) + { + if (!EnableInputs) + return; + base.OnMouseMove(location); + } + + public override bool OnMouseUp(Float2 location, MouseButton button) + { + if (!EnableInputs) + return false; + return base.OnMouseUp(location, button); + } + + public override bool OnMouseWheel(Float2 location, float delta) + { + if (!EnableInputs) + return false; + return base.OnMouseWheel(location, delta); + } + } +} diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index 13da1fb97..59a56e3f3 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -64,7 +64,11 @@ namespace FlaxEditor.Surface /// protected virtual void DrawBackground() { - var background = Style.Background; + DrawBackgroundDefault(Style.Background, Width, Height); + } + + internal static void DrawBackgroundDefault(Texture background, float width, float height) + { if (background && background.ResidentMipLevels > 0) { var bSize = background.Size; @@ -77,8 +81,8 @@ namespace FlaxEditor.Surface if (pos.Y > 0) pos.Y -= bh; - int maxI = Mathf.CeilToInt(Width / bw + 1.0f); - int maxJ = Mathf.CeilToInt(Height / bh + 1.0f); + int maxI = Mathf.CeilToInt(width / bw + 1.0f); + int maxJ = Mathf.CeilToInt(height / bh + 1.0f); for (int i = 0; i < maxI; i++) { diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index f1c4fed70..fec1154a5 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -93,6 +93,9 @@ namespace FlaxEditor.Viewport /// public abstract void Spawn(Actor actor); + /// + public abstract void OpenContextMenu(); + /// protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 785f0e651..59fad6b11 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -10,7 +10,6 @@ using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Widgets; using FlaxEngine; using FlaxEngine.GUI; -using Newtonsoft.Json; using JsonSerializer = FlaxEngine.Json.JsonSerializer; namespace FlaxEditor.Viewport @@ -154,6 +153,7 @@ namespace FlaxEditor.Viewport // Input + internal bool _disableInputUpdate; private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown; private int _deltaFilteringStep; private Float2 _startPos; @@ -1496,6 +1496,9 @@ namespace FlaxEditor.Viewport { base.Update(deltaTime); + if (_disableInputUpdate) + return; + // Update camera bool useMovementSpeed = false; if (_camera != null) diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 6d8b572a4..c9bb4c88a 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; @@ -223,7 +222,7 @@ namespace FlaxEditor.Viewport TransformGizmo = new TransformGizmo(this); TransformGizmo.ApplyTransformation += ApplyTransform; TransformGizmo.ModeChanged += OnGizmoModeChanged; - TransformGizmo.Duplicate += Editor.Instance.SceneEditing.Duplicate; + TransformGizmo.Duplicate += _editor.SceneEditing.Duplicate; Gizmos.Active = TransformGizmo; // Add grid @@ -479,7 +478,7 @@ namespace FlaxEditor.Viewport }; // Spawn - Editor.Instance.SceneEditing.Spawn(actor, parent); + _editor.SceneEditing.Spawn(actor, parent); } private void OnBegin(RenderTask task, GPUContext context) @@ -712,7 +711,7 @@ namespace FlaxEditor.Viewport Vector3 gizmoPosition = TransformGizmo.Position; // Rotate selected objects - bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode; + bool isPlayMode = _editor.StateMachine.IsPlayMode; TransformGizmo.StartTransforming(); for (int i = 0; i < selection.Count; i++) { @@ -787,7 +786,7 @@ namespace FlaxEditor.Viewport Vector3 gizmoPosition = TransformGizmo.Position; // Transform selected objects - bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode; + bool isPlayMode = _editor.StateMachine.IsPlayMode; for (int i = 0; i < selection.Count; i++) { var obj = selection[i]; @@ -929,7 +928,14 @@ namespace FlaxEditor.Viewport { var parent = actor.Parent ?? Level.GetScene(0); actor.Name = Utilities.Utils.IncrementNameNumber(actor.Name, x => parent.GetChild(x) == null); - Editor.Instance.SceneEditing.Spawn(actor); + _editor.SceneEditing.Spawn(actor); + } + + /// + public override void OpenContextMenu() + { + var mouse = PointFromWindow(Root.MousePosition); + _editor.Windows.SceneWin.ShowContextMenu(this, mouse); } /// diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 4166d6163..31f27f708 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -29,7 +29,6 @@ namespace FlaxEditor.Viewport { public PrefabWindowViewport Viewport; - /// public override bool CanRender() { return (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Enabled; @@ -41,6 +40,24 @@ namespace FlaxEditor.Viewport } } + [HideInEditor] + private sealed class PrefabUIEditorRoot : UIEditorRoot + { + private readonly PrefabWindowViewport _viewport; + + public PrefabUIEditorRoot(PrefabWindowViewport viewport) + : base(true) + { + _viewport = viewport; + Parent = viewport; + } + + public override bool EnableInputs => false; + public override bool EnableSelecting => true; + public override bool EnableBackground => _viewport._hasUILinkedCached; + public override TransformGizmo TransformGizmo => _viewport.TransformGizmo; + } + private readonly PrefabWindow _window; private UpdateDelegate _update; @@ -56,6 +73,9 @@ namespace FlaxEditor.Viewport private PrefabSpritesRenderer _spritesRenderer; private IntPtr _tempDebugDrawContext; + private bool _hasUILinkedCached; + private PrefabUIEditorRoot _uiRoot; + /// /// Drag and drop handlers /// @@ -111,6 +131,11 @@ namespace FlaxEditor.Viewport TransformGizmo.Duplicate += _window.Duplicate; Gizmos.Active = TransformGizmo; + // Use custom root for UI controls + _uiRoot = new PrefabUIEditorRoot(this); + _uiRoot.IndexInParent = 0; // Move viewport down below other widgets in the viewport + _uiParentLink = _uiRoot.UIRoot; + // Transform space widget var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); var transformSpaceToggle = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Globe32, null, true) @@ -237,8 +262,54 @@ namespace FlaxEditor.Viewport SetUpdate(ref _update, OnUpdate); } + /// + /// Updates the viewport's gizmos, especially to toggle between 3D and UI editing modes. + /// + internal void UpdateGizmoMode() + { + // Skip if gizmo mode was unmodified + if (_hasUILinked == _hasUILinkedCached) + return; + _hasUILinkedCached = _hasUILinked; + + if (_hasUILinked) + { + // UI widget + Gizmos.Active = null; + ViewportCamera = new UIEditorCamera { UIEditor = _uiRoot }; + + // Hide 3D visuals + ShowEditorPrimitives = false; + ShowDefaultSceneActors = false; + ShowDebugDraw = false; + + // Show whole UI on startup + ViewportCamera.ShowActor(Instance); + } + else + { + // Generic prefab + Gizmos.Active = TransformGizmo; + ViewportCamera = new FPSCamera(); + } + + // Update default components usage + bool defaultFeatures = !_hasUILinked; + _disableInputUpdate = _hasUILinked; + _spritesRenderer.Enabled = defaultFeatures; + SelectionOutline.Enabled = defaultFeatures; + _showDefaultSceneButton.Visible = defaultFeatures; + _cameraWidget.Visible = defaultFeatures; + _cameraButton.Visible = defaultFeatures; + _orthographicModeButton.Visible = defaultFeatures; + Task.Enabled = defaultFeatures; + UseAutomaticTaskManagement = defaultFeatures; + TintColor = defaultFeatures ? Color.White : Color.Transparent; + } + private void OnUpdate(float deltaTime) { + UpdateGizmoMode(); for (int i = 0; i < Gizmos.Count; i++) { Gizmos[i].Update(deltaTime); @@ -369,6 +440,13 @@ namespace FlaxEditor.Viewport _window.Spawn(actor); } + /// + public void OpenContextMenu() + { + var mouse = PointFromWindow(Root.MousePosition); + _window.ShowContextMenu(this, ref mouse); + } + /// protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false; @@ -545,40 +623,6 @@ namespace FlaxEditor.Viewport } } - /// - public override void Draw() - { - base.Draw(); - - // Selected UI controls outline - bool drawAnySelectedControl = false; - // TODO: optimize this (eg. cache list of selected UIControl's when selection gets changed) - for (var i = 0; i < _window.Selection.Count; i++) - { - if (_window.Selection[i]?.EditableObject is UIControl controlActor && controlActor && controlActor.Control != null && controlActor.Control.VisibleInHierarchy && controlActor.Control.RootWindow != null) - { - if (!drawAnySelectedControl) - { - drawAnySelectedControl = true; - Render2D.PushTransform(ref _cachedTransform); - } - var control = controlActor.Control; - var bounds = control.EditorBounds; - var p1 = control.PointToParent(this, bounds.UpperLeft); - var p2 = control.PointToParent(this, bounds.UpperRight); - var p3 = control.PointToParent(this, bounds.BottomLeft); - var p4 = control.PointToParent(this, bounds.BottomRight); - var min = Float2.Min(Float2.Min(p1, p2), Float2.Min(p3, p4)); - var max = Float2.Max(Float2.Max(p1, p2), Float2.Max(p3, p4)); - bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero)); - var options = Editor.Instance.Options.Options.Visual; - Render2D.DrawRectangle(bounds, options.SelectionOutlineColor0, options.UISelectionOutlineSize); - } - } - if (drawAnySelectedControl) - Render2D.PopTransform(); - } - /// protected override void OnLeftMouseButtonUp() { diff --git a/Source/Editor/Viewport/Previews/AssetPreview.cs b/Source/Editor/Viewport/Previews/AssetPreview.cs index ee9ee3195..c460a24de 100644 --- a/Source/Editor/Viewport/Previews/AssetPreview.cs +++ b/Source/Editor/Viewport/Previews/AssetPreview.cs @@ -21,7 +21,7 @@ namespace FlaxEditor.Viewport.Previews /// public abstract class AssetPreview : EditorViewport, IEditorPrimitivesOwner { - private ContextMenuButton _showDefaultSceneButton; + internal ContextMenuButton _showDefaultSceneButton; private IntPtr _debugDrawContext; private bool _debugDrawEnable; private bool _editorPrimitivesEnable; diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index a17850557..2c7b60a1f 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -2,6 +2,7 @@ using System; using FlaxEngine; +using FlaxEngine.GUI; using Object = FlaxEngine.Object; namespace FlaxEditor.Viewport.Previews @@ -14,7 +15,9 @@ namespace FlaxEditor.Viewport.Previews { private Prefab _prefab; private Actor _instance; - internal UIControl _uiControlLinked; + private UIControl _uiControlLinked; + internal bool _hasUILinked; + internal ContainerControl _uiParentLink; /// /// Gets or sets the prefab asset to preview. @@ -72,7 +75,7 @@ namespace FlaxEditor.Viewport.Previews // Unlink UI control if (_uiControlLinked) { - if (_uiControlLinked.Control?.Parent == this) + if (_uiControlLinked.Control?.Parent == _uiParentLink) _uiControlLinked.Control.Parent = null; _uiControlLinked = null; } @@ -82,6 +85,7 @@ namespace FlaxEditor.Viewport.Previews } _instance = value; + _hasUILinked = false; if (_instance) { @@ -103,20 +107,24 @@ namespace FlaxEditor.Viewport.Previews uiControl.Control != null && uiControl.Control.Parent == null) { - uiControl.Control.Parent = this; + uiControl.Control.Parent = _uiParentLink; _uiControlLinked = uiControl; + _hasUILinked = true; } } private void LinkCanvas(Actor actor) { if (actor is UICanvas uiCanvas) - uiCanvas.EditorOverride(Task, this); + { + uiCanvas.EditorOverride(Task, _uiParentLink); + if (uiCanvas.GUI.Parent == _uiParentLink) + _hasUILinked = true; + } + var children = actor.ChildrenCount; for (int i = 0; i < children; i++) - { LinkCanvas(actor.GetChild(i)); - } } /// @@ -126,6 +134,8 @@ namespace FlaxEditor.Viewport.Previews public PrefabPreview(bool useWidgets) : base(useWidgets) { + // Link to itself by default + _uiParentLink = this; } /// @@ -142,8 +152,6 @@ namespace FlaxEditor.Viewport.Previews /// public override void OnDestroy() { - if (IsDisposing) - return; Prefab = null; base.OnDestroy(); diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index d7efb1260..21e037fbc 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -360,10 +360,9 @@ namespace FlaxEditor.Windows.Assets /// /// The parent control. /// The location (within a given control). - private void ShowContextMenu(Control parent, ref Float2 location) + internal void ShowContextMenu(Control parent, ref Float2 location) { var contextMenu = CreateContextMenu(); - contextMenu.Show(parent, location); } diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index eb5070cb1..b788821cf 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -344,6 +344,7 @@ namespace FlaxEditor.Windows.Assets private void OnPrefabOpened() { _viewport.Prefab = _asset; + _viewport.UpdateGizmoMode(); Graph.MainActor = _viewport.Instance; Selection.Clear(); Select(Graph.Main); @@ -359,7 +360,7 @@ namespace FlaxEditor.Windows.Assets try { - Editor.Scene.OnSaveStart(_viewport); + Editor.Scene.OnSaveStart(_viewport._uiParentLink); // Simply update changes Editor.Prefabs.ApplyAll(_viewport.Instance); @@ -379,7 +380,7 @@ namespace FlaxEditor.Windows.Assets } finally { - Editor.Scene.OnSaveEnd(_viewport); + Editor.Scene.OnSaveEnd(_viewport._uiParentLink); } } diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index f1ea43d9b..71addb3ed 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Xml; +using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEditor.Options; @@ -194,133 +195,14 @@ namespace FlaxEditor.Windows public bool Active; } - private class GameRoot : ContainerControl + /// + /// Root control for game UI preview in Editor. Supports basic UI editing via . + /// + private class GameRoot : UIEditorRoot { - public bool EnableEvents => !Time.GamePaused; - - public override bool RayCast(ref Float2 location, out Control hit) - { - return RayCastChildren(ref location, out hit); - } - - public override bool ContainsPoint(ref Float2 location, bool precise = false) - { - if (precise) - return false; - return base.ContainsPoint(ref location, precise); - } - - public override bool OnCharInput(char c) - { - if (!EnableEvents) - return false; - - return base.OnCharInput(c); - } - - public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) - { - if (!EnableEvents) - return DragDropEffect.None; - - return base.OnDragDrop(ref location, data); - } - - public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) - { - if (!EnableEvents) - return DragDropEffect.None; - - return base.OnDragEnter(ref location, data); - } - - public override void OnDragLeave() - { - if (!EnableEvents) - return; - - base.OnDragLeave(); - } - - public override DragDropEffect OnDragMove(ref Float2 location, DragData data) - { - if (!EnableEvents) - return DragDropEffect.None; - - return base.OnDragMove(ref location, data); - } - - public override bool OnKeyDown(KeyboardKeys key) - { - if (!EnableEvents) - return false; - - return base.OnKeyDown(key); - } - - public override void OnKeyUp(KeyboardKeys key) - { - if (!EnableEvents) - return; - - base.OnKeyUp(key); - } - - public override bool OnMouseDoubleClick(Float2 location, MouseButton button) - { - if (!EnableEvents) - return false; - - return base.OnMouseDoubleClick(location, button); - } - - public override bool OnMouseDown(Float2 location, MouseButton button) - { - if (!EnableEvents) - return false; - - return base.OnMouseDown(location, button); - } - - public override void OnMouseEnter(Float2 location) - { - if (!EnableEvents) - return; - - base.OnMouseEnter(location); - } - - public override void OnMouseLeave() - { - if (!EnableEvents) - return; - - base.OnMouseLeave(); - } - - public override void OnMouseMove(Float2 location) - { - if (!EnableEvents) - return; - - base.OnMouseMove(location); - } - - public override bool OnMouseUp(Float2 location, MouseButton button) - { - if (!EnableEvents) - return false; - - return base.OnMouseUp(location, button); - } - - public override bool OnMouseWheel(Float2 location, float delta) - { - if (!EnableEvents) - return false; - - return base.OnMouseWheel(location, delta); - } + public override bool EnableInputs => !Time.GamePaused; + public override bool EnableSelecting => !Editor.IsPlayMode || Time.GamePaused; + public override TransformGizmo TransformGizmo => Editor.Instance.MainTransformGizmo; } /// @@ -348,13 +230,9 @@ namespace FlaxEditor.Windows // Override the game GUI root _guiRoot = new GameRoot { - AnchorPreset = AnchorPresets.StretchAll, - Offsets = Margin.Zero, - //Visible = false, - AutoFocus = false, Parent = _viewport }; - RootControl.GameRoot = _guiRoot; + RootControl.GameRoot = _guiRoot.UIRoot; SizeChanged += control => { ResizeViewport(); }; @@ -916,35 +794,6 @@ namespace FlaxEditor.Windows Render2D.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } - // Selected UI controls outline - bool drawAnySelectedControl = false; - // TODO: optimize this (eg. cache list of selected UIControl's when selection gets changed) - var selection = Editor.SceneEditing.Selection; - for (var i = 0; i < selection.Count; i++) - { - if (selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null && controlActor.Control.VisibleInHierarchy && controlActor.Control.RootWindow != null) - { - if (!drawAnySelectedControl) - { - drawAnySelectedControl = true; - Render2D.PushTransform(ref _viewport._cachedTransform); - } - var options = Editor.Options.Options.Visual; - var control = controlActor.Control; - var bounds = control.EditorBounds; - var p1 = control.PointToParent(_viewport, bounds.UpperLeft); - var p2 = control.PointToParent(_viewport, bounds.UpperRight); - var p3 = control.PointToParent(_viewport, bounds.BottomLeft); - var p4 = control.PointToParent(_viewport, bounds.BottomRight); - var min = Float2.Min(Float2.Min(p1, p2), Float2.Min(p3, p4)); - var max = Float2.Max(Float2.Max(p1, p2), Float2.Max(p3, p4)); - bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero)); - Render2D.DrawRectangle(bounds, options.SelectionOutlineColor0, options.UISelectionOutlineSize); - } - } - if (drawAnySelectedControl) - Render2D.PopTransform(); - // Play mode hints and overlay if (Editor.StateMachine.IsPlayMode) { diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index 4ff4752ac..f4a49195f 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; -using System.Collections.Generic; using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.SceneGraph; @@ -258,10 +257,9 @@ namespace FlaxEditor.Windows /// /// The parent control. /// The location (within a given control). - private void ShowContextMenu(Control parent, Float2 location) + internal void ShowContextMenu(Control parent, Float2 location) { var contextMenu = CreateContextMenu(); - contextMenu.Show(parent, location); } diff --git a/Source/Engine/UI/GUI/Control.Bounds.cs b/Source/Engine/UI/GUI/Control.Bounds.cs index ec96f89f2..cbb3ba80b 100644 --- a/Source/Engine/UI/GUI/Control.Bounds.cs +++ b/Source/Engine/UI/GUI/Control.Bounds.cs @@ -383,7 +383,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point. /// - [DefaultValue(0.0f)] + [DefaultValue(typeof(Float2), "0,0")] [ExpandGroups, EditorDisplay("Transform"), EditorOrder(1040), Tooltip("The shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point.")] public Float2 Shear { From 700ed25d3db11e0d64a8b660fc787d9658c8d7ce Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 17 Mar 2024 13:46:39 +0100 Subject: [PATCH 41/68] Add resizing UI Control via widgets --- Source/Editor/Gizmo/UIEditorGizmo.cs | 159 +++++++++++++++++++++++++-- 1 file changed, 149 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index 4fd059202..084a64080 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -130,12 +130,25 @@ namespace FlaxEditor } } - private bool _mouseMovesControl, _mouseMovesView; + /// + /// Cached placement of the widget used to size/edit control + /// + private struct Widget + { + public UIControl UIControl; + public Rectangle Bounds; + public Float2 ResizeAxis; + public CursorType Cursor; + } + + private bool _mouseMovesControl, _mouseMovesView, _mouseMovesWidget; private Float2 _mouseMovesPos, _moveSnapDelta; private float _mouseMoveSum; private UndoMultiBlock _undoBlock; private View _view; private float[] _gridTickSteps = Utilities.Utils.CurveTickSteps, _gridTickStrengths; + private List _widgets; + private Widget _activeWidget; /// /// True if enable displaying UI editing background and grid elements. @@ -206,6 +219,24 @@ namespace FlaxEditor var transformGizmo = TransformGizmo; var owner = transformGizmo?.Owner; + if (_widgets != null && _widgets.Count != 0 && button == MouseButton.Left) + { + foreach (var widget in _widgets) + { + if (widget.Bounds.Contains(ref location)) + { + // Initialize widget movement + _activeWidget = widget; + _mouseMovesWidget = true; + _mouseMovesPos = location; + Cursor = widget.Cursor; + StartUndo(); + Focus(); + StartMouseCapture(); + return true; + } + } + } if (EnableSelecting && owner != null && !_mouseMovesControl && button == MouseButton.Left) { // Raycast the control under the mouse @@ -288,10 +319,7 @@ namespace FlaxEditor // Move control (handle any control transformations by moving in editor's local-space) var control = uiControl.Control; var localLocation = control.LocalLocation; - var pointOrigin = control.Parent ?? control; - var startPos = pointOrigin.PointFromParent(this, _mouseMovesPos); - var endPos = pointOrigin.PointFromParent(this, moveLocation); - var uiControlDelta = endPos - startPos; + var uiControlDelta = GetControlDelta(control, ref _mouseMovesPos, ref moveLocation); control.LocalLocation = localLocation + uiControlDelta; // Don't move if layout doesn't allow it @@ -311,6 +339,31 @@ namespace FlaxEditor Cursor = CursorType.SizeAll; } } + if (_mouseMovesWidget && _activeWidget.UIControl) + { + // Calculate transform delta + var resizeAxisAbs = _activeWidget.ResizeAxis.Absolute; + var resizeAxisPos = Float2.Clamp(_activeWidget.ResizeAxis, Float2.Zero, Float2.One); + var resizeAxisNeg = Float2.Clamp(-_activeWidget.ResizeAxis, Float2.Zero, Float2.One); + var delta = location - _mouseMovesPos; + // TODO: scale/size snapping? + delta *= resizeAxisAbs; + + // Resize control via widget + var moveLocation = _mouseMovesPos + delta; + var control = _activeWidget.UIControl.Control; + var uiControlDelta = GetControlDelta(control, ref _mouseMovesPos, ref moveLocation); + control.LocalLocation += uiControlDelta * resizeAxisNeg; + control.Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; + + // Don't move if layout doesn't allow it + if (control.Parent != null) + control.Parent.PerformLayout(); + else + control.PerformLayout(); + + _mouseMovesPos = location; + } if (_mouseMovesView) { // Move view @@ -328,6 +381,7 @@ namespace FlaxEditor public override bool OnMouseUp(Float2 location, MouseButton button) { EndMovingControls(); + EndMovingWidget(); if (_mouseMovesView) { EndMovingView(); @@ -342,6 +396,7 @@ namespace FlaxEditor { EndMovingControls(); EndMovingView(); + EndMovingWidget(); base.OnMouseLeave(); } @@ -350,6 +405,7 @@ namespace FlaxEditor { EndMovingControls(); EndMovingView(); + EndMovingWidget(); base.OnLostFocus(); } @@ -406,8 +462,15 @@ namespace FlaxEditor base.Draw(); + if (!_mouseMovesWidget) + { + // Clear widgets to collect them during drawing + _widgets?.Clear(); + } + bool drawAnySelectedControl = false; var transformGizmo = TransformGizmo; + var mousePos = PointFromWindow(RootWindow.MousePosition); if (transformGizmo != null) { // Selected UI controls outline @@ -416,19 +479,17 @@ namespace FlaxEditor { if (IsValidControl(selection[i], out var controlActor)) { - DrawControlBounds(controlActor.Control, true, ref drawAnySelectedControl); - // TODO: draw anchors + DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, true); } } } if (EnableSelecting && !_mouseMovesControl && IsMouseOver) { // Highlight control under mouse for easier selecting (except if already selected) - var mousePos = PointFromWindow(RootWindow.MousePosition); if (RayCastControl(ref mousePos, out var hitControl) && (transformGizmo == null || !transformGizmo.Selection.Any(x => x.EditableObject is UIControl controlActor && controlActor.Control == hitControl))) { - DrawControlBounds(hitControl, false, ref drawAnySelectedControl); + DrawControl(null, hitControl, false, ref mousePos, ref drawAnySelectedControl); } } if (drawAnySelectedControl) @@ -450,10 +511,19 @@ namespace FlaxEditor return; EndMovingControls(); EndMovingView(); + EndMovingWidget(); base.OnDestroy(); } + private Float2 GetControlDelta(Control control, ref Float2 start, ref Float2 end) + { + var pointOrigin = control.Parent ?? control; + var startPos = pointOrigin.PointFromParent(this, start); + var endPos = pointOrigin.PointFromParent(this, end); + return endPos - startPos; + } + private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange) { var style = Style.Current; @@ -485,7 +555,7 @@ namespace FlaxEditor }, _gridTickSteps, ref _gridTickStrengths, min, max, pixelRange); } - private void DrawControlBounds(Control control, bool selection, ref bool drawAnySelectedControl) + private void DrawControl(UIControl uiControl, Control control, bool selection, ref Float2 mousePos, ref bool drawAnySelectedControl, bool withWidgets = false) { if (!drawAnySelectedControl) { @@ -493,6 +563,8 @@ namespace FlaxEditor Render2D.PushTransform(ref _cachedTransform); } var options = Editor.Instance.Options.Options.Visual; + + // Draw bounds var bounds = control.EditorBounds; var ul = control.PointToParent(this, bounds.UpperLeft); var ur = control.PointToParent(this, bounds.UpperRight); @@ -512,6 +584,62 @@ namespace FlaxEditor Render2D.DrawLine(br, bl, color, options.UISelectionOutlineSize); Render2D.DrawLine(bl, ul, color, options.UISelectionOutlineSize); #endif + if (withWidgets) + { + // Draw sizing widgets + if (_widgets == null) + _widgets = new List(); + var widgetSize = 8.0f; + var viewScale = ViewScale; + if (viewScale < 0.7f) + widgetSize *= viewScale; + var controlSize = control.Size.Absolute.MinValue / 50.0f; + if (controlSize < 1.0f) + widgetSize *= Mathf.Clamp(controlSize + 0.1f, 0.1f, 1.0f); + var cornerSize = new Float2(widgetSize); + DrawControlWidget(uiControl, ref ul, ref mousePos, ref cornerSize, new Float2(-1, -1), CursorType.SizeNWSE); + DrawControlWidget(uiControl, ref ur, ref mousePos, ref cornerSize, new Float2(1, -1), CursorType.SizeNESW); + DrawControlWidget(uiControl, ref bl, ref mousePos, ref cornerSize, new Float2(-1, 1), CursorType.SizeNESW); + DrawControlWidget(uiControl, ref br, ref mousePos, ref cornerSize, new Float2(1, 1), CursorType.SizeNWSE); + var edgeSizeV = new Float2(widgetSize * 2, widgetSize); + var edgeSizeH = new Float2(edgeSizeV.Y, edgeSizeV.X); + Float2.Lerp(ref ul, ref bl, 0.5f, out var el); + Float2.Lerp(ref ur, ref br, 0.5f, out var er); + Float2.Lerp(ref ul, ref ur, 0.5f, out var eu); + Float2.Lerp(ref bl, ref br, 0.5f, out var eb); + DrawControlWidget(uiControl, ref el, ref mousePos, ref edgeSizeH, new Float2(-1, 0), CursorType.SizeWE); + DrawControlWidget(uiControl, ref er, ref mousePos, ref edgeSizeH, new Float2(1, 0), CursorType.SizeWE); + DrawControlWidget(uiControl, ref eu, ref mousePos, ref edgeSizeV, new Float2(0, -1), CursorType.SizeNS); + DrawControlWidget(uiControl, ref eb, ref mousePos, ref edgeSizeV, new Float2(0, 1), CursorType.SizeNS); + + // TODO: draw anchors + } + } + + private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size, Float2 resizeAxis, CursorType cursor) + { + var style = Style.Current; + var rect = new Rectangle(pos - size * 0.5f, size); + if (rect.Contains(ref mousePos)) + { + Render2D.FillRectangle(rect, style.Foreground); + } + else + { + Render2D.FillRectangle(rect, style.ForegroundGrey); + Render2D.DrawRectangle(rect, style.Foreground); + } + if (!_mouseMovesWidget && uiControl != null) + { + // Collect widget + _widgets.Add(new Widget + { + UIControl = uiControl, + Bounds = rect, + ResizeAxis = resizeAxis, + Cursor = cursor, + }); + } } private bool IsValidControl(SceneGraphNode node, out UIControl uiControl) @@ -592,6 +720,17 @@ namespace FlaxEditor EndMouseCapture(); Cursor = CursorType.Default; } + + private void EndMovingWidget() + { + if (!_mouseMovesWidget) + return; + _mouseMovesWidget = false; + _activeWidget = new Widget(); + EndMouseCapture(); + Cursor = CursorType.Default; + EndUndo(); + } } /// From b847c2d056482c372beed491d16995ec2932c5f6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 17 Mar 2024 18:27:59 +0100 Subject: [PATCH 42/68] Fixes --- Source/Editor/Gizmo/UIEditorGizmo.cs | 6 ++++-- Source/Editor/Options/InputBinding.cs | 4 ++-- Source/Editor/Viewport/EditorGizmoViewport.cs | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index 084a64080..c7d37d78d 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -189,9 +189,11 @@ namespace FlaxEditor internal float ViewScale { - get => _view.Scale.X; + get => _view?.Scale.X ?? 1; set { + if (_view == null) + return; value = Mathf.Clamp(value, 0.1f, 4.0f); _view.Scale = new Float2(value); } @@ -483,7 +485,7 @@ namespace FlaxEditor } } } - if (EnableSelecting && !_mouseMovesControl && IsMouseOver) + if (EnableSelecting && !_mouseMovesControl && !_mouseMovesWidget && IsMouseOver) { // Highlight control under mouse for easier selecting (except if already selected) if (RayCastControl(ref mousePos, out var hitControl) && diff --git a/Source/Editor/Options/InputBinding.cs b/Source/Editor/Options/InputBinding.cs index 47147eaa8..59954dec5 100644 --- a/Source/Editor/Options/InputBinding.cs +++ b/Source/Editor/Options/InputBinding.cs @@ -201,8 +201,8 @@ namespace FlaxEditor.Options /// True if input has been processed, otherwise false. public bool Process(Control control) { - var root = control.Root; - return root.GetKey(Key) && ProcessModifiers(control); + var root = control?.Root; + return root != null && root.GetKey(Key) && ProcessModifiers(control); } /// diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index fec1154a5..25dcd76e7 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -76,10 +76,10 @@ namespace FlaxEditor.Viewport public Float2 MouseDelta => _mouseDelta * 1000; /// - public bool UseSnapping => Root.GetKey(KeyboardKeys.Control); + public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false; /// - public bool UseDuplicate => Root.GetKey(KeyboardKeys.Shift); + public bool UseDuplicate => Root?.GetKey(KeyboardKeys.Shift) ?? false; /// public Undo Undo { get; } From 16aef0f705c5c7d6db017689f5d113bdbee14502 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 17 Mar 2024 17:11:36 -0500 Subject: [PATCH 43/68] Add deselecting by clicking no control. Add cursor changes when over widgets. --- Source/Editor/Gizmo/UIEditorGizmo.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index c7d37d78d..1ff7e687e 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -276,6 +276,11 @@ namespace FlaxEditor return true; } } + // Allow deselecting if user clicks on nothing + else + { + owner.Select(null); + } } if (EnableCamera && (button == MouseButton.Right || button == MouseButton.Middle)) { @@ -295,6 +300,24 @@ namespace FlaxEditor { base.OnMouseMove(location); + // Change cursor if mouse is over active control widget + bool cursorChanged = false; + if (_widgets != null && _widgets.Count != 0 && !_mouseMovesControl && !_mouseMovesWidget && !_mouseMovesView) + { + foreach (var widget in _widgets) + { + if (widget.Bounds.Contains(ref location)) + { + Cursor = widget.Cursor; + cursorChanged = true; + } + else if (Cursor != CursorType.Default && !cursorChanged) + { + Cursor = CursorType.Default; + } + } + } + var transformGizmo = TransformGizmo; if (_mouseMovesControl && transformGizmo != null) { From 795527576b1ff810b17d00388aa2122b87c2e93d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 15:20:05 +0100 Subject: [PATCH 44/68] Add dynamic UI mode checking for Prefab Viewport --- Source/Editor/Viewport/PrefabWindowViewport.cs | 7 ++++--- Source/Editor/Viewport/Previews/PrefabPreview.cs | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 31f27f708..1bf121ce2 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -44,6 +44,7 @@ namespace FlaxEditor.Viewport private sealed class PrefabUIEditorRoot : UIEditorRoot { private readonly PrefabWindowViewport _viewport; + private bool UI => _viewport._hasUILinkedCached; public PrefabUIEditorRoot(PrefabWindowViewport viewport) : base(true) @@ -52,9 +53,9 @@ namespace FlaxEditor.Viewport Parent = viewport; } - public override bool EnableInputs => false; - public override bool EnableSelecting => true; - public override bool EnableBackground => _viewport._hasUILinkedCached; + public override bool EnableInputs => !UI; + public override bool EnableSelecting => UI; + public override bool EnableBackground => UI; public override TransformGizmo TransformGizmo => _viewport.TransformGizmo; } diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index 2c7b60a1f..7df524a72 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -98,6 +98,9 @@ namespace FlaxEditor.Viewport.Previews private void UpdateLinkage() { + // Clear flag + _hasUILinked = false; + // Link UI canvases to the preview (eg. after canvas added to the prefab) LinkCanvas(_instance); From 91298fee19ac6e15d6a8095a56c1f0d5c432c5f1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 15:32:42 +0100 Subject: [PATCH 45/68] Fixes for UI editor changes --- Source/Editor/Gizmo/UIEditorGizmo.cs | 11 +++++++---- Source/Editor/Viewport/EditorViewport.cs | 2 +- Source/Editor/Viewport/Previews/PrefabPreview.cs | 2 ++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index 1ff7e687e..5d1f27651 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -21,9 +21,12 @@ namespace FlaxEditor public void ShowActors(IEnumerable actors) { + var root = UIEditor.UIRoot; + if (root == null) + return; + // Calculate bounds of all selected objects var areaRect = Rectangle.Empty; - var root = UIEditor.UIRoot; foreach (var actor in actors) { Rectangle bounds; @@ -163,7 +166,7 @@ namespace FlaxEditor /// /// True if enable panning and zooming the view. /// - public bool EnableCamera => _view != null; + public bool EnableCamera => _view != null && EnableBackground; /// /// Transform gizmo to use sync with (selection, snapping, transformation settings). @@ -293,7 +296,7 @@ namespace FlaxEditor return true; } - return false; + return Focus(this); } public override void OnMouseMove(Float2 location) @@ -467,7 +470,7 @@ namespace FlaxEditor public override void Draw() { - if (EnableBackground) + if (EnableBackground && _view != null) { // Draw background Surface.VisjectSurface.DrawBackgroundDefault(Editor.Instance.UI.VisjectSurfaceBackground, Width, Height); diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 59fad6b11..a53f95ea8 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1537,7 +1537,7 @@ namespace FlaxEditor.Viewport } bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height)); _prevInput = _input; - var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl)); + var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot)); if (canUseInput && ContainsFocus && hit == null) _input.Gather(win.Window, useMouse); else diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index 7df524a72..16ec8f132 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -114,6 +114,8 @@ namespace FlaxEditor.Viewport.Previews _uiControlLinked = uiControl; _hasUILinked = true; } + else if (_uiControlLinked != null) + _hasUILinked = true; } private void LinkCanvas(Actor actor) From e606ccec57a6800596e0b12978fdda198b92f6a1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 15:33:01 +0100 Subject: [PATCH 46/68] Fix actor icons drawing in prefab viewport to skip disabled actors --- Source/Editor/Utilities/ViewportIconsRenderer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp index d3cd2c940..797b4edea 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp +++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp @@ -187,6 +187,8 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor* actor, Mesh::DrawInfo& draw) { + if (!actor || !actor->IsActiveInHierarchy()) + return; auto& view = renderContext.View; const BoundingFrustum frustum = view.Frustum; Matrix m1, m2, world; @@ -208,8 +210,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor draw.DrawState = &drawState; draw.Deformation = nullptr; - // Support custom icons through types, but not onces that were added through actors, - // since they cant register while in prefab view anyway + // Support custom icons through types, but not ones that were added through actors, since they cant register while in prefab view anyway if (ActorTypeToTexture.TryGet(actor->GetTypeHandle(), texture)) { // Use custom texture From ed21876bb89a50e001968ce5af80a7accb24996f Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Mon, 18 Mar 2024 17:34:21 +0200 Subject: [PATCH 47/68] Fix duplicate generation of native variant conversion helpers --- .../Bindings/BindingsGenerator.Cpp.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 681483cf9..4bf6ae35a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -24,7 +24,7 @@ namespace Flax.Build.Bindings private static readonly List CppAutoSerializeProperties = new List(); public static readonly HashSet CppIncludeFiles = new HashSet(); private static readonly List CppIncludeFilesList = new List(); - private static readonly HashSet CppVariantToTypes = new HashSet(); + private static readonly Dictionary CppVariantToTypes = new Dictionary(); private static readonly Dictionary CppVariantFromTypes = new Dictionary(); private static bool CppNonPodTypesConvertingGeneration = false; private static StringBuilder CppContentsEnd; @@ -231,13 +231,15 @@ namespace Flax.Build.Bindings throw new Exception($"Not supported type to convert from the Variant to fixed-size array '{typeInfo}[{typeInfo.ArraySize}]'."); if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null) { - CppVariantToTypes.Add(typeInfo); - return $"MoveTemp(VariantTo{GenerateCppWrapperNativeToVariantMethodName(typeInfo)}({value}))"; + var wrapperName = GenerateCppWrapperNativeToVariantMethodName(typeInfo); + CppVariantToTypes[wrapperName] = typeInfo; + return $"MoveTemp(VariantTo{wrapperName}({value}))"; } if (typeInfo.Type == "Dictionary" && typeInfo.GenericArgs != null) { - CppVariantToTypes.Add(typeInfo); - return $"MoveTemp(VariantTo{GenerateCppWrapperNativeToVariantMethodName(typeInfo)}({value}))"; + var wrapperName = GenerateCppWrapperNativeToVariantMethodName(typeInfo); + CppVariantToTypes[wrapperName] = typeInfo; + return $"MoveTemp(VariantTo{wrapperName}({value}))"; } if (typeInfo.Type == "Span" && typeInfo.GenericArgs != null) { @@ -2790,12 +2792,14 @@ namespace Flax.Build.Bindings var header = GetStringBuilder(); // Variant converting helper methods - foreach (var typeInfo in CppVariantToTypes) + foreach (var e in CppVariantToTypes) { + var wrapperName = e.Key; + var typeInfo = e.Value; var name = typeInfo.ToString(false); header.AppendLine(); header.AppendLine("namespace {"); - header.Append($"{name} VariantTo{GenerateCppWrapperNativeToVariantMethodName(typeInfo)}(const Variant& v)").AppendLine(); + header.Append($"{name} VariantTo{wrapperName}(const Variant& v)").AppendLine(); header.Append('{').AppendLine(); header.Append($" {name} result;").AppendLine(); if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null) From 3b502086f897d99cf1ade8fa66ec7b47ffdd9b12 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 17:38:05 +0100 Subject: [PATCH 48/68] Fix UI coordinate convertion in `CanvasScaler` --- Source/Engine/UI/GUI/CanvasScaler.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Engine/UI/GUI/CanvasScaler.cs b/Source/Engine/UI/GUI/CanvasScaler.cs index f0cc59b10..b36a62f27 100644 --- a/Source/Engine/UI/GUI/CanvasScaler.cs +++ b/Source/Engine/UI/GUI/CanvasScaler.cs @@ -429,6 +429,13 @@ namespace FlaxEngine.GUI return ContainsPoint(ref location); } + /// + public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation) + { + location /= _scale; + return base.IntersectsChildContent(child, location, out childSpaceLocation); + } + /// public override bool ContainsPoint(ref Float2 location, bool precise = false) { From 34f35ebaac190513804d81d1cda4e58af7be8fbd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 17:38:24 +0100 Subject: [PATCH 49/68] Fix loading shader cache when opened with different engine version --- Flax.flaxproj | 2 +- Source/Engine/Graphics/Shaders/Cache/ShaderCacheManager.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 7153e51d5..9afdfad42 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 7, "Revision": 2, - "Build": 6408 + "Build": 6409 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.", diff --git a/Source/Engine/Graphics/Shaders/Cache/ShaderCacheManager.cpp b/Source/Engine/Graphics/Shaders/Cache/ShaderCacheManager.cpp index 0e6ad8f3a..2af300f06 100644 --- a/Source/Engine/Graphics/Shaders/Cache/ShaderCacheManager.cpp +++ b/Source/Engine/Graphics/Shaders/Cache/ShaderCacheManager.cpp @@ -10,6 +10,7 @@ #include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/Materials/MaterialShader.h" #include "Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.h" +#include "FlaxEngine.Gen.h" const Char* ShaderProfileCacheDirNames[] = { @@ -179,6 +180,7 @@ bool ShaderCacheManagerService::Init() // Validate the database cache version (need to recompile all shaders on shader cache format change) struct CacheVersion { + int32 EngineVersion = -1; int32 ShaderCacheVersion = -1; int32 MaterialGraphVersion = -1; int32 ParticleGraphVersion = -1; @@ -193,7 +195,8 @@ bool ShaderCacheManagerService::Init() LOG(Warning, "Failed to read the shaders cache database version file."); } } - if (cacheVersion.ShaderCacheVersion != GPU_SHADER_CACHE_VERSION + if (cacheVersion.EngineVersion != FLAXENGINE_VERSION_BUILD + || cacheVersion.ShaderCacheVersion != GPU_SHADER_CACHE_VERSION || cacheVersion.MaterialGraphVersion != MATERIAL_GRAPH_VERSION || cacheVersion.ParticleGraphVersion != PARTICLE_GPU_GRAPH_VERSION ) @@ -209,6 +212,7 @@ bool ShaderCacheManagerService::Init() LOG(Error, "Failed to createe the shaders cache database directory."); } + cacheVersion.EngineVersion = FLAXENGINE_VERSION_BUILD; cacheVersion.ShaderCacheVersion = GPU_SHADER_CACHE_VERSION; cacheVersion.MaterialGraphVersion = MATERIAL_GRAPH_VERSION; cacheVersion.ParticleGraphVersion = PARTICLE_GPU_GRAPH_VERSION; From 12e2b8156f5d78c797cf02d104102d055d1d0bc5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 20:31:17 +0100 Subject: [PATCH 50/68] Fix UI prefab focus on canvas not being added as root --- Source/Editor/Viewport/PrefabWindowViewport.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 1bf121ce2..252981555 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; @@ -285,7 +286,11 @@ namespace FlaxEditor.Viewport ShowDebugDraw = false; // Show whole UI on startup - ViewportCamera.ShowActor(Instance); + var canvas = (CanvasRootControl)_uiParentLink.Children.FirstOrDefault(x => x is CanvasRootControl); + if (canvas != null) + ViewportCamera.ShowActor(canvas.Canvas); + else if (Instance is UIControl) + ViewportCamera.ShowActor(Instance); } else { From eb8db60ead137fe49f61142ef8e11fc6125e98c1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 21:12:54 +0100 Subject: [PATCH 51/68] Revert eye adaptation back to original value (in favor of #2023) --- Source/Engine/Graphics/PostProcessSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 3e8c83973..86c693e81 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -948,7 +948,7 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable /// The effect rendering mode used for the exposure processing. /// API_FIELD(Attributes="EditorOrder(0), PostProcessSetting((int)EyeAdaptationSettingsOverride.Mode)") - EyeAdaptationMode Mode = EyeAdaptationMode::None; + EyeAdaptationMode Mode = EyeAdaptationMode::AutomaticHistogram; /// /// The speed at which the exposure changes when the scene brightness moves from a dark area to a bright area (brightness goes up). From 24299dc5ccbbb7a76a18ec5fc78e5e34f0388e5c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 21:36:43 +0100 Subject: [PATCH 52/68] Fix game ui inputs to UI when game is paused in Editor --- Source/Editor/Windows/GameWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 71addb3ed..c804c26dc 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -200,7 +200,7 @@ namespace FlaxEditor.Windows /// private class GameRoot : UIEditorRoot { - public override bool EnableInputs => !Time.GamePaused; + public override bool EnableInputs => !Time.GamePaused && Editor.IsPlayMode; public override bool EnableSelecting => !Editor.IsPlayMode || Time.GamePaused; public override TransformGizmo TransformGizmo => Editor.Instance.MainTransformGizmo; } From 2b824ce22a4e120f745cae6b7f85589c4c0e92d9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 21:50:08 +0100 Subject: [PATCH 53/68] Fix UI Canvas double scale apply --- Source/Engine/UI/GUI/CanvasScaler.cs | 91 ---------------------------- 1 file changed, 91 deletions(-) diff --git a/Source/Engine/UI/GUI/CanvasScaler.cs b/Source/Engine/UI/GUI/CanvasScaler.cs index b36a62f27..612de3f59 100644 --- a/Source/Engine/UI/GUI/CanvasScaler.cs +++ b/Source/Engine/UI/GUI/CanvasScaler.cs @@ -469,97 +469,6 @@ namespace FlaxEngine.GUI return result; } - /// - public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) - { - location /= _scale; - return base.OnDragEnter(ref location, data); - } - - /// - public override DragDropEffect OnDragMove(ref Float2 location, DragData data) - { - location /= _scale; - return base.OnDragMove(ref location, data); - } - - /// - public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) - { - location /= _scale; - return base.OnDragDrop(ref location, data); - } - - /// - public override void OnMouseEnter(Float2 location) - { - location /= _scale; - base.OnMouseEnter(location); - } - - /// - public override void OnMouseMove(Float2 location) - { - location /= _scale; - base.OnMouseMove(location); - } - - /// - public override bool OnMouseDown(Float2 location, MouseButton button) - { - location /= _scale; - return base.OnMouseDown(location, button); - } - - /// - public override bool OnMouseUp(Float2 location, MouseButton button) - { - location /= _scale; - return base.OnMouseUp(location, button); - } - - /// - public override bool OnMouseDoubleClick(Float2 location, MouseButton button) - { - location /= _scale; - return base.OnMouseDoubleClick(location, button); - } - - /// - public override bool OnMouseWheel(Float2 location, float delta) - { - location /= _scale; - return base.OnMouseWheel(location, delta); - } - - /// - public override void OnTouchEnter(Float2 location, int pointerId) - { - location /= _scale; - base.OnTouchEnter(location, pointerId); - } - - /// - public override void OnTouchMove(Float2 location, int pointerId) - { - location /= _scale; - base.OnTouchMove(location, pointerId); - } - - /// - public override bool OnTouchDown(Float2 location, int pointerId) - { - location /= _scale; - return base.OnTouchDown(location, pointerId); - } - - /// - public override bool OnTouchUp(Float2 location, int pointerId) - { - location /= _scale; - return base.OnTouchUp(location, pointerId); - } - #endregion } } From cf44d6580df708505feb34058efadf4f0e925ad5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 18 Mar 2024 18:50:53 -0500 Subject: [PATCH 54/68] Remove widgets when EnableSelecting is false. --- Source/Editor/Gizmo/UIEditorGizmo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index 5d1f27651..ea4b75d28 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -507,7 +507,7 @@ namespace FlaxEditor { if (IsValidControl(selection[i], out var controlActor)) { - DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, true); + DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, EnableSelecting); } } } From 4b9bfc5bb4318af84eaece7768c6ae4d739d10e7 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 18 Mar 2024 18:58:59 -0500 Subject: [PATCH 55/68] Allow to still show widget in prefabs when in play mode --- Source/Editor/Gizmo/UIEditorGizmo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index ea4b75d28..e39bd2684 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -507,7 +507,7 @@ namespace FlaxEditor { if (IsValidControl(selection[i], out var controlActor)) { - DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, EnableSelecting); + DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, EnableSelecting || (!EnableSelecting && Parent?.Parent is not Windows.GameWindow)); } } } From 211da6d8e733a2f3d48b46688d0285c472807544 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 19 Mar 2024 07:17:05 -0500 Subject: [PATCH 56/68] Revert last commit. Not needed. --- Source/Editor/Gizmo/UIEditorGizmo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index e39bd2684..ea4b75d28 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -507,7 +507,7 @@ namespace FlaxEditor { if (IsValidControl(selection[i], out var controlActor)) { - DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, EnableSelecting || (!EnableSelecting && Parent?.Parent is not Windows.GameWindow)); + DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, EnableSelecting); } } } From 41637232ea41990f2f66fbf288dbd6815cf151c2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 19 Mar 2024 07:28:30 -0500 Subject: [PATCH 57/68] Add back some more game window inputs but dont run the action if the editor is in play mode. --- Source/Editor/Windows/GameWindow.cs | 50 +++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 71addb3ed..52e0e7362 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -260,6 +260,56 @@ namespace FlaxEditor.Windows Editor.Instance.Windows.ProfilerWin.Clear(); Editor.Instance.UI.AddStatusMessage($"Profiling results cleared."); }); + InputActions.Add(options => options.Save, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SaveAll(); + }); + InputActions.Add(options => options.Undo, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.PerformUndo(); + Focus(); + }); + InputActions.Add(options => options.Redo, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.PerformRedo(); + Focus(); + }); + InputActions.Add(options => options.Cut, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SceneEditing.Cut(); + }); + InputActions.Add(options => options.Copy, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SceneEditing.Copy(); + }); + InputActions.Add(options => options.Paste, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SceneEditing.Paste(); + }); + InputActions.Add(options => options.Duplicate, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SceneEditing.Duplicate(); + }); + InputActions.Add(options => options.Delete, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SceneEditing.Delete(); + }); } private void ChangeViewportRatio(ViewportScaleOptions v) From 1a68a52611c3c0e64111b1549e1833e34f151967 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 16:01:27 +0100 Subject: [PATCH 58/68] Add better grid gizmo rendering in Editor viewport #1859 --- Source/Editor/Gizmo/GridGizmo.cs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Gizmo/GridGizmo.cs b/Source/Editor/Gizmo/GridGizmo.cs index 8638e1b54..a96651472 100644 --- a/Source/Editor/Gizmo/GridGizmo.cs +++ b/Source/Editor/Gizmo/GridGizmo.cs @@ -21,7 +21,7 @@ namespace FlaxEditor.Gizmo { Order = -100; UseSingleTarget = true; - Location = PostProcessEffectLocation.BeforeForwardPass; + Location = PostProcessEffectLocation.AfterAntiAliasingPass; } ~Renderer() @@ -46,7 +46,8 @@ namespace FlaxEditor.Gizmo var plane = new Plane(Vector3.Zero, Vector3.UnitY); var dst = CollisionsHelper.DistancePlanePoint(ref plane, ref viewPos); - float space = Editor.Instance.Options.Options.Viewport.ViewportGridScale, size; + var options = Editor.Instance.Options.Options; + float space = options.Viewport.ViewportGridScale, size; if (dst <= 500.0f) { size = 8000; @@ -62,8 +63,12 @@ namespace FlaxEditor.Gizmo size = 100000; } - Color color = Color.Gray * 0.7f; + float bigLineIntensity = 0.8f; + Color bigColor = Color.Gray * bigLineIntensity; + Color color = bigColor * 0.8f; int count = (int)(size / space); + int midLine = count / 2; + int bigLinesMod = count / 8; Vector3 start = new Vector3(0, 0, size * -0.5f); Vector3 end = new Vector3(0, 0, size * 0.5f); @@ -71,7 +76,12 @@ namespace FlaxEditor.Gizmo for (int i = 0; i <= count; i++) { start.X = end.X = i * space + start.Z; - DebugDraw.DrawLine(start, end, color); + Color lineColor = color; + if (i == midLine) + lineColor = Color.Blue * bigLineIntensity; + else if (i % bigLinesMod == 0) + lineColor = bigColor; + DebugDraw.DrawLine(start, end, lineColor); } start = new Vector3(size * -0.5f, 0, 0); @@ -80,7 +90,12 @@ namespace FlaxEditor.Gizmo for (int i = 0; i <= count; i++) { start.Z = end.Z = i * space + start.X; - DebugDraw.DrawLine(start, end, color); + Color lineColor = color; + if (i == midLine) + lineColor = Color.Red * bigLineIntensity; + else if (i % bigLinesMod == 0) + lineColor = bigColor; + DebugDraw.DrawLine(start, end, lineColor); } DebugDraw.Draw(ref renderContext, input.View(), null, true); From cf3145273faa74ba5d12e604d72623f06589a3dc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 16:02:50 +0100 Subject: [PATCH 59/68] Fix TAA jitter in post-resolve passes such as editor primitives and debug gizmos --- Source/Engine/Debug/DebugDraw.cpp | 3 +- Source/Engine/Graphics/RenderView.cpp | 37 +++++++++++++++++++++ Source/Engine/Graphics/RenderView.h | 24 ++++++++++++- Source/Engine/Renderer/AntiAliasing/TAA.cpp | 3 ++ Source/Engine/Renderer/RenderList.cpp | 1 + Source/Engine/Renderer/Renderer.cpp | 2 +- 6 files changed, 67 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 46b15a6e2..b0164124c 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -739,6 +739,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe } Context->LastViewPos = view.Position; Context->LastViewProj = view.Projection; + TaaJitterRemoveContext taaJitterRemove(view); // Fallback to task buffers if (target == nullptr && renderContext.Task) @@ -766,7 +767,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe const auto cb = DebugDrawShader->GetShader()->GetCB(0); Data data; Matrix vp; - Matrix::Multiply(view.View, view.NonJitteredProjection, vp); + Matrix::Multiply(view.View, view.Projection, vp); Matrix::Transpose(vp, data.ViewProjection); data.EnableDepthTest = enableDepthTest; context->UpdateCB(cb, &data); diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp index d59a16463..f72c8f406 100644 --- a/Source/Engine/Graphics/RenderView.cpp +++ b/Source/Engine/Graphics/RenderView.cpp @@ -18,6 +18,7 @@ void RenderView::Prepare(RenderContext& renderContext) // Check if use TAA (need to modify the projection matrix) Float2 taaJitter; NonJitteredProjection = Projection; + IsTaaResolved = false; if (renderContext.List->Setup.UseTemporalAAJitter) { // Move to the next frame @@ -82,6 +83,18 @@ void RenderView::PrepareCache(const RenderContext& renderContext, float width, f MainScreenSize = mainView->ScreenSize; } +void RenderView::UpdateCachedData() +{ + Matrix::Invert(View, IV); + Matrix::Invert(Projection, IP); + Matrix viewProjection; + Matrix::Multiply(View, Projection, viewProjection); + Frustum.SetMatrix(viewProjection); + Matrix::Invert(viewProjection, IVP); + CullingFrustum = Frustum; + NonJitteredProjection = Projection; +} + void RenderView::SetUp(const Matrix& viewProjection) { // Copy data @@ -201,3 +214,27 @@ void RenderView::GetWorldMatrix(const Transform& transform, Matrix& world) const const Float3 translation = transform.Translation - Origin; Matrix::Transformation(transform.Scale, transform.Orientation, translation, world); } + +TaaJitterRemoveContext::TaaJitterRemoveContext(const RenderView& view) +{ + if (view.IsTaaResolved) + { + // Cancel-out sub-pixel jitter when drawing geometry after TAA has been resolved + _view = (RenderView*)&view; + _prevProjection = view.Projection; + _prevNonJitteredProjection = view.NonJitteredProjection; + _view->Projection = _prevNonJitteredProjection; + _view->UpdateCachedData(); + } +} + +TaaJitterRemoveContext::~TaaJitterRemoveContext() +{ + if (_view) + { + // Restore projection + _view->Projection = _prevProjection; + _view->UpdateCachedData(); + _view->NonJitteredProjection = _prevNonJitteredProjection; + } +} diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h index d73b45c53..0b37e8b28 100644 --- a/Source/Engine/Graphics/RenderView.h +++ b/Source/Engine/Graphics/RenderView.h @@ -117,6 +117,11 @@ public: /// API_FIELD() bool IsCullingDisabled = false; + /// + /// True if TAA has been resolved when rendering view and frame doesn't contain jitter anymore. Rendering geometry after this point should not use jitter anymore (eg. editor gizmos or custom geometry as overlay). + /// + API_FIELD() bool IsTaaResolved = false; + /// /// The static flags mask used to hide objects that don't have a given static flags. Eg. use StaticFlags::Lightmap to render only objects that can use lightmap. /// @@ -160,7 +165,7 @@ public: API_FIELD() DEPRECATED float ShadowModelLODDistanceFactor = 1.0f; /// - /// The Temporal Anti-Aliasing jitter frame index. + /// Temporal Anti-Aliasing jitter frame index. /// API_FIELD() int32 TaaFrameIndex = 0; @@ -261,6 +266,11 @@ public: RenderView& operator=(const RenderView& other) = default; PRAGMA_ENABLE_DEPRECATION_WARNINGS + /// + /// Updates the cached data for the view (inverse matrices, etc.). + /// + void UpdateCachedData(); + // Set up view with custom params // @param viewProjection View * Projection matrix void SetUp(const Matrix& viewProjection); @@ -344,3 +354,15 @@ public: world.M43 -= Origin.Z; } }; + +// Removes TAA jitter from the RenderView when drawing geometry after TAA has been resolved to prevent unwanted jittering. +struct TaaJitterRemoveContext +{ +private: + RenderView* _view = nullptr; + Matrix _prevProjection, _prevNonJitteredProjection; + +public: + TaaJitterRemoveContext(const RenderView& view); + ~TaaJitterRemoveContext(); +}; diff --git a/Source/Engine/Renderer/AntiAliasing/TAA.cpp b/Source/Engine/Renderer/AntiAliasing/TAA.cpp index f48e0955a..22ed7a806 100644 --- a/Source/Engine/Renderer/AntiAliasing/TAA.cpp +++ b/Source/Engine/Renderer/AntiAliasing/TAA.cpp @@ -146,4 +146,7 @@ void TAA::Render(const RenderContext& renderContext, GPUTexture* input, GPUTextu context->Draw(output); renderContext.Buffers->TemporalAA = outputHistory; } + + // Mark TAA jitter as resolved for future drawing + (bool&)renderContext.View.IsTaaResolved = true; } diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index af2495b67..01da9e2ea 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -652,6 +652,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL const auto* batchesData = list.Batches.Get(); const auto context = GPUDevice::Instance->GetMainContext(); bool useInstancing = list.CanUseInstancing && CanUseInstancing(renderContext.View.Pass) && GPUDevice::Instance->Limits.HasInstancing; + TaaJitterRemoveContext taaJitterRemove(renderContext.View); // Clear SR slots to prevent any resources binding issues (leftovers from the previous passes) context->ResetSR(); diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 3246b6d85..f3c4ac6d2 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -612,7 +612,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont // Color Grading LUT generation auto colorGradingLUT = ColorGradingPass::Instance()->RenderLUT(renderContext); - // Post processing + // Post-processing EyeAdaptationPass::Instance()->Render(renderContext, frameBuffer); PostProcessingPass::Instance()->Render(renderContext, frameBuffer, tempBuffer, colorGradingLUT); RenderTargetPool::Release(colorGradingLUT); From 388b64a46db186b875e972fddd9610c8af326923 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 16:03:19 +0100 Subject: [PATCH 60/68] Add support for hot-reloading Debug Draw shader --- Source/Engine/Debug/DebugDraw.cpp | 32 ++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index b0164124c..fe6424565 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -356,6 +356,19 @@ namespace Float3 CircleCache[DEBUG_DRAW_CIRCLE_VERTICES]; Array SphereTriangleCache; DebugSphereCache SphereCache[3]; + +#if COMPILE_WITH_DEV_ENV + void OnShaderReloading(Asset* obj) + { + DebugDrawPsLinesDefault.Release(); + DebugDrawPsLinesDepthTest.Release(); + DebugDrawPsWireTrianglesDefault.Release(); + DebugDrawPsWireTrianglesDepthTest.Release(); + DebugDrawPsTrianglesDefault.Release(); + DebugDrawPsTrianglesDepthTest.Release(); + } + +#endif }; extern int32 BoxTrianglesIndicesCache[]; @@ -615,17 +628,19 @@ void DebugDrawService::Update() GlobalContext.DebugDrawDefault.Update(deltaTime); GlobalContext.DebugDrawDepthTest.Update(deltaTime); - // Check if need to setup a resources + // Lazy-init resources if (DebugDrawShader == nullptr) { - // Shader DebugDrawShader = Content::LoadAsyncInternal(TEXT("Shaders/DebugDraw")); if (DebugDrawShader == nullptr) { LOG(Fatal, "Cannot load DebugDraw shader"); } +#if COMPILE_WITH_DEV_ENV + DebugDrawShader->OnReloading.Bind(&OnShaderReloading); +#endif } - if (DebugDrawVB == nullptr && DebugDrawShader && DebugDrawShader->IsLoaded()) + if (DebugDrawPsWireTrianglesDepthTest.Depth == nullptr && DebugDrawShader && DebugDrawShader->IsLoaded()) { bool failed = false; const auto shader = DebugDrawShader->GetShader(); @@ -661,10 +676,11 @@ void DebugDrawService::Update() { LOG(Fatal, "Cannot setup DebugDraw service!"); } - - // Vertex buffer - DebugDrawVB = New((uint32)(DEBUG_DRAW_INITIAL_VB_CAPACITY * sizeof(Vertex)), (uint32)sizeof(Vertex), TEXT("DebugDraw.VB")); } + + // Vertex buffer + if (DebugDrawVB == nullptr) + DebugDrawVB = New((uint32)(DEBUG_DRAW_INITIAL_VB_CAPACITY * sizeof(Vertex)), (uint32)sizeof(Vertex), TEXT("DebugDraw.VB")); } void DebugDrawService::Dispose() @@ -723,7 +739,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe // Ensure to have shader loaded and any lines to render const int32 debugDrawDepthTestCount = Context->DebugDrawDepthTest.Count(); const int32 debugDrawDefaultCount = Context->DebugDrawDefault.Count(); - if (DebugDrawShader == nullptr || !DebugDrawShader->IsLoaded() || debugDrawDepthTestCount + debugDrawDefaultCount == 0) + if (DebugDrawShader == nullptr || !DebugDrawShader->IsLoaded() || debugDrawDepthTestCount + debugDrawDefaultCount == 0 || DebugDrawPsWireTrianglesDepthTest.Depth == nullptr) return; if (renderContext.Buffers == nullptr || !DebugDrawVB) return; @@ -849,6 +865,8 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe { PROFILE_GPU_CPU_NAMED("Text"); auto features = Render2D::Features; + + // Disable vertex snapping when rendering 3D text Render2D::Features = (Render2D::RenderingFeatures)((uint32)features & ~(uint32)Render2D::RenderingFeatures::VertexSnapping); if (!DebugDrawFont) From 71dad849084b4478b0080952bbd6f4aeb45bc655 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 17:53:14 +0100 Subject: [PATCH 61/68] Fix Z-fighting issues in Debug Draw when using TAA --- Content/Shaders/DebugDraw.flax | 4 ++-- Source/Engine/Debug/DebugDraw.cpp | 4 +++- Source/Shaders/DebugDraw.shader | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Content/Shaders/DebugDraw.flax b/Content/Shaders/DebugDraw.flax index 284c55f64..455cf8c20 100644 --- a/Content/Shaders/DebugDraw.flax +++ b/Content/Shaders/DebugDraw.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:10fefac1db81c41041c95970dc13694a17dfbca09819d6f7344b5d45f5e8c0df -size 2060 +oid sha256:7f8833a76cdda54b6c1de3b98f7993b835d17a5ac60b0bf11a9ee9faa42cc177 +size 2108 diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index fe6424565..ea8cdb196 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -127,7 +127,8 @@ PACK_STRUCT(struct Vertex { PACK_STRUCT(struct Data { Matrix ViewProjection; - Float3 Padding; + Float2 Padding; + float ClipPosZBias; bool EnableDepthTest; }); @@ -785,6 +786,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe Matrix vp; Matrix::Multiply(view.View, view.Projection, vp); Matrix::Transpose(vp, data.ViewProjection); + data.ClipPosZBias = -0.2f; // Reduce Z-fighting artifacts (eg. editor grid) data.EnableDepthTest = enableDepthTest; context->UpdateCB(cb, &data); context->BindCB(0, cb); diff --git a/Source/Shaders/DebugDraw.shader b/Source/Shaders/DebugDraw.shader index 6495cb1a3..d10dbbf06 100644 --- a/Source/Shaders/DebugDraw.shader +++ b/Source/Shaders/DebugDraw.shader @@ -4,7 +4,8 @@ META_CB_BEGIN(0, Data) float4x4 ViewProjection; -float3 Padding; +float2 Padding; +float ClipPosZBias; bool EnableDepthTest; META_CB_END @@ -23,6 +24,7 @@ VS2PS VS(float3 Position : POSITION, float4 Color : COLOR) { VS2PS output; output.Position = mul(float4(Position, 1), ViewProjection); + output.Position.z += ClipPosZBias; output.Color = Color; return output; } From 155b4a3ba92bb05a9c8a27890ca2aac6e4a4d4b8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 17:53:45 +0100 Subject: [PATCH 62/68] Refactor editor gizmo viewports to use shared code for widgets --- Source/Editor/Viewport/EditorGizmoViewport.cs | 246 ++++++++++++++++ Source/Editor/Viewport/EditorViewport.cs | 36 ++- .../Viewport/MainEditorGizmoViewport.cs | 265 +----------------- .../Editor/Viewport/PrefabWindowViewport.cs | 234 +--------------- 4 files changed, 265 insertions(+), 516 deletions(-) diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index 25dcd76e7..856ae20da 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -1,9 +1,12 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System; using System.Collections.Generic; using FlaxEditor.Gizmo; +using FlaxEditor.GUI.ContextMenu; using FlaxEditor.SceneGraph; using FlaxEditor.Viewport.Cameras; +using FlaxEditor.Viewport.Widgets; using FlaxEngine; using FlaxEngine.GUI; @@ -126,6 +129,249 @@ namespace FlaxEditor.Viewport base.OnDestroy(); } + internal static void AddGizmoViewportWidgets(EditorViewport viewport, TransformGizmo transformGizmo, bool useProjectCache = false) + { + var editor = Editor.Instance; + var inputOptions = editor.Options.Options.Input; + + if (useProjectCache) + { + // Initialize snapping enabled from cached values + if (editor.ProjectCache.TryGetCustomData("TranslateSnapState", out var cachedState)) + transformGizmo.TranslationSnapEnable = bool.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("RotationSnapState", out cachedState)) + transformGizmo.RotationSnapEnabled = bool.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("ScaleSnapState", out cachedState)) + transformGizmo.ScaleSnapEnabled = bool.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("TranslateSnapValue", out cachedState)) + transformGizmo.TranslationSnapValue = float.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("RotationSnapValue", out cachedState)) + transformGizmo.RotationSnapValue = float.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("ScaleSnapValue", out cachedState)) + transformGizmo.ScaleSnapValue = float.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("TransformSpaceState", out cachedState) && Enum.TryParse(cachedState, out TransformGizmoBase.TransformSpace space)) + transformGizmo.ActiveTransformSpace = space; + } + + // Transform space widget + var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + var transformSpaceToggle = new ViewportWidgetButton(string.Empty, editor.Icons.Globe32, null, true) + { + Checked = transformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World, + TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})", + Parent = transformSpaceWidget + }; + transformSpaceToggle.Toggled += _ => + { + transformGizmo.ToggleTransformSpace(); + if (useProjectCache) + editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString()); + }; + transformSpaceWidget.Parent = viewport; + + // Scale snapping widget + var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + var enableScaleSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.ScaleSnap32, null, true) + { + Checked = transformGizmo.ScaleSnapEnabled, + TooltipText = "Enable scale snapping", + Parent = scaleSnappingWidget + }; + enableScaleSnapping.Toggled += _ => + { + transformGizmo.ScaleSnapEnabled = !transformGizmo.ScaleSnapEnabled; + if (useProjectCache) + editor.ProjectCache.SetCustomData("ScaleSnapState", transformGizmo.ScaleSnapEnabled.ToString()); + }; + var scaleSnappingCM = new ContextMenu(); + var scaleSnapping = new ViewportWidgetButton(transformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM) + { + TooltipText = "Scale snapping values" + }; + for (int i = 0; i < ScaleSnapValues.Length; i++) + { + var v = ScaleSnapValues[i]; + var button = scaleSnappingCM.AddButton(v.ToString()); + button.Tag = v; + } + scaleSnappingCM.ButtonClicked += button => + { + var v = (float)button.Tag; + transformGizmo.ScaleSnapValue = v; + scaleSnapping.Text = v.ToString(); + if (useProjectCache) + editor.ProjectCache.SetCustomData("ScaleSnapValue", transformGizmo.ScaleSnapValue.ToString("N")); + }; + scaleSnappingCM.VisibleChanged += control => + { + if (control.Visible == false) + return; + var ccm = (ContextMenu)control; + foreach (var e in ccm.Items) + { + if (e is ContextMenuButton b) + { + var v = (float)b.Tag; + b.Icon = Mathf.Abs(transformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + } + } + }; + scaleSnapping.Parent = scaleSnappingWidget; + scaleSnappingWidget.Parent = viewport; + + // Rotation snapping widget + var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + var enableRotateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.RotateSnap32, null, true) + { + Checked = transformGizmo.RotationSnapEnabled, + TooltipText = "Enable rotation snapping", + Parent = rotateSnappingWidget + }; + enableRotateSnapping.Toggled += _ => + { + transformGizmo.RotationSnapEnabled = !transformGizmo.RotationSnapEnabled; + if (useProjectCache) + editor.ProjectCache.SetCustomData("RotationSnapState", transformGizmo.RotationSnapEnabled.ToString()); + }; + var rotateSnappingCM = new ContextMenu(); + var rotateSnapping = new ViewportWidgetButton(transformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM) + { + TooltipText = "Rotation snapping values" + }; + for (int i = 0; i < RotateSnapValues.Length; i++) + { + var v = RotateSnapValues[i]; + var button = rotateSnappingCM.AddButton(v.ToString()); + button.Tag = v; + } + rotateSnappingCM.ButtonClicked += button => + { + var v = (float)button.Tag; + transformGizmo.RotationSnapValue = v; + rotateSnapping.Text = v.ToString(); + if (useProjectCache) + editor.ProjectCache.SetCustomData("RotationSnapValue", transformGizmo.RotationSnapValue.ToString("N")); + }; + rotateSnappingCM.VisibleChanged += control => + { + if (control.Visible == false) + return; + var ccm = (ContextMenu)control; + foreach (var e in ccm.Items) + { + if (e is ContextMenuButton b) + { + var v = (float)b.Tag; + b.Icon = Mathf.Abs(transformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + } + } + }; + rotateSnapping.Parent = rotateSnappingWidget; + rotateSnappingWidget.Parent = viewport; + + // Translation snapping widget + var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.Grid32, null, true) + { + Checked = transformGizmo.TranslationSnapEnable, + TooltipText = "Enable position snapping", + Parent = translateSnappingWidget + }; + enableTranslateSnapping.Toggled += _ => + { + transformGizmo.TranslationSnapEnable = !transformGizmo.TranslationSnapEnable; + if (useProjectCache) + editor.ProjectCache.SetCustomData("TranslateSnapState", transformGizmo.TranslationSnapEnable.ToString()); + }; + var translateSnappingCM = new ContextMenu(); + var translateSnapping = new ViewportWidgetButton(transformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM) + { + TooltipText = "Position snapping values" + }; + if (transformGizmo.TranslationSnapValue < 0.0f) + translateSnapping.Text = "Bounding Box"; + for (int i = 0; i < TranslateSnapValues.Length; i++) + { + var v = TranslateSnapValues[i]; + var button = translateSnappingCM.AddButton(v.ToString()); + button.Tag = v; + } + var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume"); + buttonBB.Tag = -1.0f; + translateSnappingCM.ButtonClicked += button => + { + var v = (float)button.Tag; + transformGizmo.TranslationSnapValue = v; + if (v < 0.0f) + translateSnapping.Text = "Bounding Box"; + else + translateSnapping.Text = v.ToString(); + if (useProjectCache) + editor.ProjectCache.SetCustomData("TranslateSnapValue", transformGizmo.TranslationSnapValue.ToString("N")); + }; + translateSnappingCM.VisibleChanged += control => + { + if (control.Visible == false) + return; + var ccm = (ContextMenu)control; + foreach (var e in ccm.Items) + { + if (e is ContextMenuButton b) + { + var v = (float)b.Tag; + b.Icon = Mathf.Abs(transformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + } + } + }; + translateSnapping.Parent = translateSnappingWidget; + translateSnappingWidget.Parent = viewport; + + // Gizmo mode widget + var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + var gizmoModeTranslate = new ViewportWidgetButton(string.Empty, editor.Icons.Translate32, null, true) + { + Tag = TransformGizmoBase.Mode.Translate, + TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})", + Checked = true, + Parent = gizmoMode + }; + gizmoModeTranslate.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate; + var gizmoModeRotate = new ViewportWidgetButton(string.Empty, editor.Icons.Rotate32, null, true) + { + Tag = TransformGizmoBase.Mode.Rotate, + TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})", + Parent = gizmoMode + }; + gizmoModeRotate.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate; + var gizmoModeScale = new ViewportWidgetButton(string.Empty, editor.Icons.Scale32, null, true) + { + Tag = TransformGizmoBase.Mode.Scale, + TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})", + Parent = gizmoMode + }; + gizmoModeScale.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale; + gizmoMode.Parent = viewport; + transformGizmo.ModeChanged += () => + { + var mode = transformGizmo.ActiveMode; + gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate; + gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate; + gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale; + }; + + // Setup input actions + viewport.InputActions.Add(options => options.TranslateMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); + viewport.InputActions.Add(options => options.RotateMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); + viewport.InputActions.Add(options => options.ScaleMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); + viewport.InputActions.Add(options => options.ToggleTransformSpace, () => + { + transformGizmo.ToggleTransformSpace(); + if (useProjectCache) + editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString()); + transformSpaceToggle.Checked = !transformSpaceToggle.Checked; + }); + } + internal static readonly float[] TranslateSnapValues = { 0.1f, diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index a53f95ea8..2eeb1e8c0 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -704,9 +704,9 @@ namespace FlaxEditor.Viewport // Camera Viewpoints { var cameraView = cameraCM.AddChildMenu("Viewpoints").ContextMenu; - for (int i = 0; i < EditorViewportCameraViewpointValues.Length; i++) + for (int i = 0; i < CameraViewpointValues.Length; i++) { - var co = EditorViewportCameraViewpointValues[i]; + var co = CameraViewpointValues[i]; var button = cameraView.AddButton(co.Name); button.Tag = co.Orientation; } @@ -899,9 +899,9 @@ namespace FlaxEditor.Viewport viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32; viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32; viewFlags.AddSeparator(); - for (int i = 0; i < EditorViewportViewFlagsValues.Length; i++) + for (int i = 0; i < ViewFlagsValues.Length; i++) { - var v = EditorViewportViewFlagsValues[i]; + var v = ViewFlagsValues[i]; var button = viewFlags.AddButton(v.Name); button.CloseMenuOnClick = false; button.Tag = v.Mode; @@ -933,9 +933,9 @@ namespace FlaxEditor.Viewport } }); debugView.AddSeparator(); - for (int i = 0; i < EditorViewportViewModeValues.Length; i++) + for (int i = 0; i < ViewModeValues.Length; i++) { - ref var v = ref EditorViewportViewModeValues[i]; + ref var v = ref ViewModeValues[i]; if (v.Options != null) { var childMenu = debugView.AddChildMenu(v.Name).ContextMenu; @@ -989,12 +989,12 @@ namespace FlaxEditor.Viewport #endregion View mode widget } - InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Top").Orientation))); - InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation))); - InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Front").Orientation))); - InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Back").Orientation))); - InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Right").Orientation))); - InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Left").Orientation))); + InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Top").Orientation))); + InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation))); + InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Front").Orientation))); + InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Back").Orientation))); + InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Right").Orientation))); + InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Left").Orientation))); InputActions.Add(options => options.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown); InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1)); InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1)); @@ -1870,7 +1870,7 @@ namespace FlaxEditor.Viewport } } - private readonly CameraViewpoint[] EditorViewportCameraViewpointValues = + private readonly CameraViewpoint[] CameraViewpointValues = { new CameraViewpoint("Front", new Float3(0, 180, 0)), new CameraViewpoint("Back", new Float3(0, 0, 0)), @@ -1901,7 +1901,7 @@ namespace FlaxEditor.Viewport } } - private static readonly ViewModeOptions[] EditorViewportViewModeValues = + private static readonly ViewModeOptions[] ViewModeValues = { new ViewModeOptions(ViewMode.Default, "Default"), new ViewModeOptions(ViewMode.Unlit, "Unlit"), @@ -1973,7 +1973,7 @@ namespace FlaxEditor.Viewport } } - private static readonly ViewFlagOptions[] EditorViewportViewFlagsValues = + private static readonly ViewFlagOptions[] ViewFlagsValues = { new ViewFlagOptions(ViewFlags.AntiAliasing, "Anti Aliasing"), new ViewFlagOptions(ViewFlags.Shadows, "Shadows"), @@ -2008,16 +2008,13 @@ namespace FlaxEditor.Viewport { if (cm.Visible == false) return; - var ccm = (ContextMenu)cm; foreach (var e in ccm.Items) { if (e is ContextMenuButton b && b.Tag != null) { var v = (ViewFlags)b.Tag; - b.Icon = (Task.View.Flags & v) != 0 - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = (Task.View.Flags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } @@ -2026,7 +2023,6 @@ namespace FlaxEditor.Viewport { if (cm.Visible == false) return; - var ccm = (ContextMenu)cm; var layersMask = Task.ViewLayersMask; foreach (var e in ccm.Items) diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index c9bb4c88a..7d08213fa 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -7,9 +7,7 @@ using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.SceneGraph; using FlaxEditor.Scripting; -using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Modes; -using FlaxEditor.Viewport.Widgets; using FlaxEditor.Windows; using FlaxEngine; using FlaxEngine.GUI; @@ -27,13 +25,6 @@ namespace FlaxEditor.Viewport private readonly ContextMenuButton _showGridButton; private readonly ContextMenuButton _showNavigationButton; - private readonly ViewportWidgetButton _gizmoModeTranslate; - private readonly ViewportWidgetButton _gizmoModeRotate; - private readonly ViewportWidgetButton _gizmoModeScale; - - private readonly ViewportWidgetButton _translateSnapping; - private readonly ViewportWidgetButton _rotateSnapping; - private readonly ViewportWidgetButton _scaleSnapping; private SelectionOutline _customSelectionOutline; @@ -195,7 +186,6 @@ namespace FlaxEditor.Viewport { _editor = editor; DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType, ValidateDragScriptItem); - var inputOptions = editor.Options.Options.Input; // Prepare rendering task Task.ActorsSource = ActorsSources.Scenes; @@ -221,7 +211,6 @@ namespace FlaxEditor.Viewport // Add transformation gizmo TransformGizmo = new TransformGizmo(this); TransformGizmo.ApplyTransformation += ApplyTransform; - TransformGizmo.ModeChanged += OnGizmoModeChanged; TransformGizmo.Duplicate += _editor.SceneEditing.Duplicate; Gizmos.Active = TransformGizmo; @@ -231,137 +220,8 @@ namespace FlaxEditor.Viewport editor.SceneEditing.SelectionChanged += OnSelectionChanged; - // Initialize snapping enabled from cached values - if (_editor.ProjectCache.TryGetCustomData("TranslateSnapState", out var cachedState)) - TransformGizmo.TranslationSnapEnable = bool.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("RotationSnapState", out cachedState)) - TransformGizmo.RotationSnapEnabled = bool.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("ScaleSnapState", out cachedState)) - TransformGizmo.ScaleSnapEnabled = bool.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("TranslateSnapValue", out cachedState)) - TransformGizmo.TranslationSnapValue = float.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("RotationSnapValue", out cachedState)) - TransformGizmo.RotationSnapValue = float.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("ScaleSnapValue", out cachedState)) - TransformGizmo.ScaleSnapValue = float.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("TransformSpaceState", out cachedState) && Enum.TryParse(cachedState, out TransformGizmoBase.TransformSpace space)) - TransformGizmo.ActiveTransformSpace = space; - - // Transform space widget - var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var transformSpaceToggle = new ViewportWidgetButton(string.Empty, editor.Icons.Globe32, null, true) - { - Checked = TransformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World, - TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})", - Parent = transformSpaceWidget - }; - transformSpaceToggle.Toggled += OnTransformSpaceToggle; - transformSpaceWidget.Parent = this; - - // Scale snapping widget - var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableScaleSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.ScaleSnap32, null, true) - { - Checked = TransformGizmo.ScaleSnapEnabled, - TooltipText = "Enable scale snapping", - Parent = scaleSnappingWidget - }; - enableScaleSnapping.Toggled += OnScaleSnappingToggle; - var scaleSnappingCM = new ContextMenu(); - _scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM) - { - TooltipText = "Scale snapping values" - }; - for (int i = 0; i < ScaleSnapValues.Length; i++) - { - var v = ScaleSnapValues[i]; - var button = scaleSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - scaleSnappingCM.ButtonClicked += OnWidgetScaleSnapClick; - scaleSnappingCM.VisibleChanged += OnWidgetScaleSnapShowHide; - _scaleSnapping.Parent = scaleSnappingWidget; - scaleSnappingWidget.Parent = this; - - // Rotation snapping widget - var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableRotateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.RotateSnap32, null, true) - { - Checked = TransformGizmo.RotationSnapEnabled, - TooltipText = "Enable rotation snapping", - Parent = rotateSnappingWidget - }; - enableRotateSnapping.Toggled += OnRotateSnappingToggle; - var rotateSnappingCM = new ContextMenu(); - _rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM) - { - TooltipText = "Rotation snapping values" - }; - for (int i = 0; i < RotateSnapValues.Length; i++) - { - var v = RotateSnapValues[i]; - var button = rotateSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - rotateSnappingCM.ButtonClicked += OnWidgetRotateSnapClick; - rotateSnappingCM.VisibleChanged += OnWidgetRotateSnapShowHide; - _rotateSnapping.Parent = rotateSnappingWidget; - rotateSnappingWidget.Parent = this; - - // Translation snapping widget - var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.Grid32, null, true) - { - Checked = TransformGizmo.TranslationSnapEnable, - TooltipText = "Enable position snapping", - Parent = translateSnappingWidget - }; - enableTranslateSnapping.Toggled += OnTranslateSnappingToggle; - var translateSnappingCM = new ContextMenu(); - _translateSnapping = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM) - { - TooltipText = "Position snapping values" - }; - if (TransformGizmo.TranslationSnapValue < 0.0f) - _translateSnapping.Text = "Bounding Box"; - for (int i = 0; i < TranslateSnapValues.Length; i++) - { - var v = TranslateSnapValues[i]; - var button = translateSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume"); - buttonBB.Tag = -1.0f; - translateSnappingCM.ButtonClicked += OnWidgetTranslateSnapClick; - translateSnappingCM.VisibleChanged += OnWidgetTranslateSnapShowHide; - _translateSnapping.Parent = translateSnappingWidget; - translateSnappingWidget.Parent = this; - - // Gizmo mode widget - var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - _gizmoModeTranslate = new ViewportWidgetButton(string.Empty, editor.Icons.Translate32, null, true) - { - Tag = TransformGizmoBase.Mode.Translate, - TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})", - Checked = true, - Parent = gizmoMode - }; - _gizmoModeTranslate.Toggled += OnGizmoModeToggle; - _gizmoModeRotate = new ViewportWidgetButton(string.Empty, editor.Icons.Rotate32, null, true) - { - Tag = TransformGizmoBase.Mode.Rotate, - TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})", - Parent = gizmoMode - }; - _gizmoModeRotate.Toggled += OnGizmoModeToggle; - _gizmoModeScale = new ViewportWidgetButton(string.Empty, editor.Icons.Scale32, null, true) - { - Tag = TransformGizmoBase.Mode.Scale, - TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})", - Parent = gizmoMode - }; - _gizmoModeScale.Toggled += OnGizmoModeToggle; - gizmoMode.Parent = this; + // Gizmo widgets + AddGizmoViewportWidgets(this, TransformGizmo); // Show grid widget _showGridButton = ViewWidgetShowMenu.AddButton("Grid", () => Grid.Enabled = !Grid.Enabled); @@ -392,14 +252,6 @@ namespace FlaxEditor.Viewport } // Setup input actions - InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); - InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); - InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); - InputActions.Add(options => options.ToggleTransformSpace, () => - { - OnTransformSpaceToggle(transformSpaceToggle); - transformSpaceToggle.Checked = !transformSpaceToggle.Checked; - }); InputActions.Add(options => options.LockFocusSelection, LockFocusSelection); InputActions.Add(options => options.FocusSelection, FocusSelection); InputActions.Add(options => options.RotateSelection, RotateSelection); @@ -573,119 +425,6 @@ namespace FlaxEditor.Viewport } } - private void OnGizmoModeToggle(ViewportWidgetButton button) - { - TransformGizmo.ActiveMode = (TransformGizmoBase.Mode)(int)button.Tag; - } - - private void OnTranslateSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.TranslationSnapEnable = !TransformGizmo.TranslationSnapEnable; - _editor.ProjectCache.SetCustomData("TranslateSnapState", TransformGizmo.TranslationSnapEnable.ToString()); - } - - private void OnRotateSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.RotationSnapEnabled = !TransformGizmo.RotationSnapEnabled; - _editor.ProjectCache.SetCustomData("RotationSnapState", TransformGizmo.RotationSnapEnabled.ToString()); - } - - private void OnScaleSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.ScaleSnapEnabled = !TransformGizmo.ScaleSnapEnabled; - _editor.ProjectCache.SetCustomData("ScaleSnapState", TransformGizmo.ScaleSnapEnabled.ToString()); - } - - private void OnTransformSpaceToggle(ViewportWidgetButton button) - { - TransformGizmo.ToggleTransformSpace(); - _editor.ProjectCache.SetCustomData("TransformSpaceState", TransformGizmo.ActiveTransformSpace.ToString()); - } - - private void OnGizmoModeChanged() - { - // Update all viewport widgets status - var mode = TransformGizmo.ActiveMode; - _gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate; - _gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate; - _gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale; - } - - private void OnWidgetScaleSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.ScaleSnapValue = v; - _scaleSnapping.Text = v.ToString(); - _editor.ProjectCache.SetCustomData("ScaleSnapValue", TransformGizmo.ScaleSnapValue.ToString("N")); - } - - private void OnWidgetScaleSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - - private void OnWidgetRotateSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.RotationSnapValue = v; - _rotateSnapping.Text = v.ToString(); - _editor.ProjectCache.SetCustomData("RotationSnapValue", TransformGizmo.RotationSnapValue.ToString("N")); - } - - private void OnWidgetRotateSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - - private void OnWidgetTranslateSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.TranslationSnapValue = v; - if (v < 0.0f) - _translateSnapping.Text = "Bounding Box"; - else - _translateSnapping.Text = v.ToString(); - _editor.ProjectCache.SetCustomData("TranslateSnapValue", TransformGizmo.TranslationSnapValue.ToString("N")); - } - - private void OnWidgetTranslateSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - private void OnSelectionChanged() { var selection = _editor.SceneEditing.Selection; diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 252981555..081c1d4f1 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -5,12 +5,10 @@ using System.Collections.Generic; using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; -using FlaxEditor.GUI.ContextMenu; using FlaxEditor.SceneGraph; using FlaxEditor.Scripting; using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Previews; -using FlaxEditor.Viewport.Widgets; using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; @@ -63,14 +61,6 @@ namespace FlaxEditor.Viewport private readonly PrefabWindow _window; private UpdateDelegate _update; - private readonly ViewportWidgetButton _gizmoModeTranslate; - private readonly ViewportWidgetButton _gizmoModeRotate; - private readonly ViewportWidgetButton _gizmoModeScale; - - private ViewportWidgetButton _translateSnappng; - private ViewportWidgetButton _rotateSnapping; - private ViewportWidgetButton _scaleSnapping; - private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); private PrefabSpritesRenderer _spritesRenderer; private IntPtr _tempDebugDrawContext; @@ -129,7 +119,6 @@ namespace FlaxEditor.Viewport // Add transformation gizmo TransformGizmo = new TransformGizmo(this); TransformGizmo.ApplyTransformation += ApplyTransform; - TransformGizmo.ModeChanged += OnGizmoModeChanged; TransformGizmo.Duplicate += _window.Duplicate; Gizmos.Active = TransformGizmo; @@ -138,127 +127,9 @@ namespace FlaxEditor.Viewport _uiRoot.IndexInParent = 0; // Move viewport down below other widgets in the viewport _uiParentLink = _uiRoot.UIRoot; - // Transform space widget - var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var transformSpaceToggle = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Globe32, null, true) - { - Checked = TransformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World, - TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})", - Parent = transformSpaceWidget - }; - transformSpaceToggle.Toggled += OnTransformSpaceToggle; - transformSpaceWidget.Parent = this; - - // Scale snapping widget - var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableScaleSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.ScaleSnap32, null, true) - { - Checked = TransformGizmo.ScaleSnapEnabled, - TooltipText = "Enable scale snapping", - Parent = scaleSnappingWidget - }; - enableScaleSnapping.Toggled += OnScaleSnappingToggle; - var scaleSnappingCM = new ContextMenu(); - _scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM) - { - TooltipText = "Scale snapping values" - }; - for (int i = 0; i < EditorGizmoViewport.ScaleSnapValues.Length; i++) - { - var v = EditorGizmoViewport.ScaleSnapValues[i]; - var button = scaleSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - scaleSnappingCM.ButtonClicked += OnWidgetScaleSnapClick; - scaleSnappingCM.VisibleChanged += OnWidgetScaleSnapShowHide; - _scaleSnapping.Parent = scaleSnappingWidget; - scaleSnappingWidget.Parent = this; - - // Rotation snapping widget - var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableRotateSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.RotateSnap32, null, true) - { - Checked = TransformGizmo.RotationSnapEnabled, - TooltipText = "Enable rotation snapping", - Parent = rotateSnappingWidget - }; - enableRotateSnapping.Toggled += OnRotateSnappingToggle; - var rotateSnappingCM = new ContextMenu(); - _rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM) - { - TooltipText = "Rotation snapping values" - }; - for (int i = 0; i < EditorGizmoViewport.RotateSnapValues.Length; i++) - { - var v = EditorGizmoViewport.RotateSnapValues[i]; - var button = rotateSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - rotateSnappingCM.ButtonClicked += OnWidgetRotateSnapClick; - rotateSnappingCM.VisibleChanged += OnWidgetRotateSnapShowHide; - _rotateSnapping.Parent = rotateSnappingWidget; - rotateSnappingWidget.Parent = this; - - // Translation snapping widget - var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Grid32, null, true) - { - Checked = TransformGizmo.TranslationSnapEnable, - TooltipText = "Enable position snapping", - Parent = translateSnappingWidget - }; - enableTranslateSnapping.Toggled += OnTranslateSnappingToggle; - var translateSnappingCM = new ContextMenu(); - _translateSnappng = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM) - { - TooltipText = "Position snapping values" - }; - for (int i = 0; i < EditorGizmoViewport.TranslateSnapValues.Length; i++) - { - var v = EditorGizmoViewport.TranslateSnapValues[i]; - var button = translateSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - translateSnappingCM.ButtonClicked += OnWidgetTranslateSnapClick; - translateSnappingCM.VisibleChanged += OnWidgetTranslateSnapShowHide; - _translateSnappng.Parent = translateSnappingWidget; - translateSnappingWidget.Parent = this; - - // Gizmo mode widget - var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - _gizmoModeTranslate = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Translate32, null, true) - { - Tag = TransformGizmoBase.Mode.Translate, - TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})", - Checked = true, - Parent = gizmoMode - }; - _gizmoModeTranslate.Toggled += OnGizmoModeToggle; - _gizmoModeRotate = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Rotate32, null, true) - { - Tag = TransformGizmoBase.Mode.Rotate, - TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})", - Parent = gizmoMode - }; - _gizmoModeRotate.Toggled += OnGizmoModeToggle; - _gizmoModeScale = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Scale32, null, true) - { - Tag = TransformGizmoBase.Mode.Scale, - TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})", - Parent = gizmoMode - }; - _gizmoModeScale.Toggled += OnGizmoModeToggle; - gizmoMode.Parent = this; + EditorGizmoViewport.AddGizmoViewportWidgets(this, TransformGizmo); // Setup input actions - InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); - InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); - InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); - InputActions.Add(options => options.ToggleTransformSpace, () => - { - OnTransformSpaceToggle(transformSpaceToggle); - transformSpaceToggle.Checked = !transformSpaceToggle.Checked; - }); InputActions.Add(options => options.FocusSelection, ShowSelectedActors); SetUpdate(ref _update, OnUpdate); @@ -472,109 +343,6 @@ namespace FlaxEditor.Viewport root.UpdateCallbacksToRemove.Add(_update); } - private void OnGizmoModeToggle(ViewportWidgetButton button) - { - TransformGizmo.ActiveMode = (TransformGizmoBase.Mode)(int)button.Tag; - } - - private void OnTranslateSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.TranslationSnapEnable = !TransformGizmo.TranslationSnapEnable; - } - - private void OnRotateSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.RotationSnapEnabled = !TransformGizmo.RotationSnapEnabled; - } - - private void OnScaleSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.ScaleSnapEnabled = !TransformGizmo.ScaleSnapEnabled; - } - - private void OnTransformSpaceToggle(ViewportWidgetButton button) - { - TransformGizmo.ToggleTransformSpace(); - } - - private void OnGizmoModeChanged() - { - // Update all viewport widgets status - var mode = TransformGizmo.ActiveMode; - _gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate; - _gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate; - _gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale; - } - - private void OnWidgetScaleSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.ScaleSnapValue = v; - _scaleSnapping.Text = v.ToString(); - } - - private void OnWidgetScaleSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - - private void OnWidgetRotateSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.RotationSnapValue = v; - _rotateSnapping.Text = v.ToString(); - } - - private void OnWidgetRotateSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - - private void OnWidgetTranslateSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.TranslationSnapValue = v; - _translateSnappng.Text = v.ToString(); - } - - private void OnWidgetTranslateSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - private void OnSelectionChanged() { Gizmos.ForEach(x => x.OnSelectionChanged(_window.Selection)); From 35d1d97840b84b59679608dab8ef1c9a05def8f5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 18:30:02 +0100 Subject: [PATCH 63/68] Rename `NumberCategoryAttribute` to `ValueCategoryAttribute` #2213 --- Source/Editor/CustomEditors/Editors/DoubleEditor.cs | 4 ++-- Source/Editor/CustomEditors/Editors/FloatEditor.cs | 4 ++-- Source/Editor/CustomEditors/Editors/Vector3Editor.cs | 4 ++-- Source/Engine/Core/Math/Transform.h | 4 ++-- ...CategoryAttribute.cs => ValueCategoryAttribute.cs} | 11 +++++------ 5 files changed, 13 insertions(+), 14 deletions(-) rename Source/Engine/Scripting/Attributes/Editor/{NumberCategoryAttribute.cs => ValueCategoryAttribute.cs} (66%) diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index a2d2cfb25..477f68855 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -26,8 +26,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is NumberCategoryAttribute); - var valueCategory = ((NumberCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; + var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var limit = attributes.FirstOrDefault(x => x is LimitAttribute); diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index 1ea1ab15f..af6a95c4d 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -32,8 +32,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is NumberCategoryAttribute); - var valueCategory = ((NumberCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; + var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var range = attributes.FirstOrDefault(x => x is RangeAttribute); diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index 8d156f075..2850fa4f3 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -75,7 +75,7 @@ namespace FlaxEditor.CustomEditors.Editors if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); - var categoryAttribute = (NumberCategoryAttribute)attributes.FirstOrDefault(x => x is NumberCategoryAttribute); + var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); if (categoryAttribute != null) category = categoryAttribute.Category; } @@ -273,7 +273,7 @@ namespace FlaxEditor.CustomEditors.Editors if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); - var categoryAttribute = (NumberCategoryAttribute)attributes.FirstOrDefault(x => x is NumberCategoryAttribute); + var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); if (categoryAttribute != null) category = categoryAttribute.Category; } diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h index 3dd47df11..2c51bfa4a 100644 --- a/Source/Engine/Core/Math/Transform.h +++ b/Source/Engine/Core/Math/Transform.h @@ -18,13 +18,13 @@ API_STRUCT() struct FLAXENGINE_API Transform /// /// The translation vector of the transform. /// - API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\"), NumberCategory(Utils.ValueCategory.Distance)") + API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\"), ValueCategory(Utils.ValueCategory.Distance)") Vector3 Translation; /// /// The rotation of the transform. /// - API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\"), NumberCategory(Utils.ValueCategory.Angle)") + API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\"), ValueCategory(Utils.ValueCategory.Angle)") Quaternion Orientation; /// diff --git a/Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs similarity index 66% rename from Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs rename to Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs index 6b5726bb1..8ba91e9d0 100644 --- a/Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs @@ -5,29 +5,28 @@ using System; namespace FlaxEngine { /// - /// Used to specify the value category of a numeric value as either as-is (a scalar), a distance (formatted as cm/m/km) - /// or an angle (formatted with a degree sign). + /// Used to specify the value category of a numeric value as either as-is (a scalar), a distance (formatted as cm/m/km) or an angle (formatted with a degree sign). /// /// [Serializable] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public sealed class NumberCategoryAttribute : Attribute + public sealed class ValueCategoryAttribute : Attribute { /// /// The value category used for formatting. /// public Utils.ValueCategory Category; - private NumberCategoryAttribute() + private ValueCategoryAttribute() { Category = Utils.ValueCategory.None; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The value category. - public NumberCategoryAttribute(Utils.ValueCategory category) + public ValueCategoryAttribute(Utils.ValueCategory category) { Category = category; } From 127e8b077d21a4f6b45149005e7ab7cb701076f3 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 19 Mar 2024 12:30:46 -0500 Subject: [PATCH 64/68] Add focusing search bar in content view and output textbox. --- Source/Editor/Windows/ContentWindow.cs | 3 +++ Source/Editor/Windows/OutputLogWindow.cs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index c96aa8e59..3e29b979f 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -281,6 +281,9 @@ namespace FlaxEditor.Windows _view.OnDelete += Delete; _view.OnDuplicate += Duplicate; _view.OnPaste += Paste; + + _view.InputActions.Add(options => options.Search, () => _itemsSearchBox.Focus()); + InputActions.Add(options => options.Search, () => _itemsSearchBox.Focus()); } private ContextMenu OnViewDropdownPopupCreate(ComboBox comboBox) diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 87d18246a..cd76412c8 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -65,6 +65,11 @@ namespace FlaxEditor.Windows /// public OutputLogWindow Window; + /// + /// The input actions collection to processed during user input. + /// + public InputActionsContainer InputActions = new InputActionsContainer(); + /// /// The default text style. /// @@ -80,6 +85,14 @@ namespace FlaxEditor.Windows /// public TextBlockStyle ErrorStyle; + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (InputActions.Process(Editor.Instance, this, key)) + return true; + return base.OnKeyDown(key); + } + /// protected override void OnParseTextBlocks() { @@ -201,6 +214,9 @@ namespace FlaxEditor.Windows // Setup editor options Editor.Options.OptionsChanged += OnEditorOptionsChanged; OnEditorOptionsChanged(Editor.Options.Options); + + _output.InputActions.Add(options => options.Search, () => _searchBox.Focus()); + InputActions.Add(options => options.Search, () => _searchBox.Focus()); GameCooker.Event += OnGameCookerEvent; ScriptsBuilder.CompilationFailed += OnScriptsCompilationFailed; From bfbabbc39587338e1e5dc5f3181c80ad2ef2b3cd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 20:04:00 +0100 Subject: [PATCH 65/68] Refactor value editors to simplify code after many new features added --- .../CustomEditors/Editors/DoubleEditor.cs | 41 +++--------- .../CustomEditors/Editors/FloatEditor.cs | 63 +++++++------------ 2 files changed, 30 insertions(+), 74 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index 477f68855..2049860c6 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; -using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -22,50 +21,28 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - _element = null; - - // Try get limit attribute for value min/max range setting and slider speed + var doubleValue = layout.DoubleValue(); + doubleValue.ValueBox.ValueChanged += OnValueChanged; + doubleValue.ValueBox.SlidingEnd += ClearToken; + _element = doubleValue; var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); - var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { - var limit = attributes.FirstOrDefault(x => x is LimitAttribute); - if (limit != null) + var limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); + doubleValue.SetLimits(limit); + var valueCategory = ((ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute))?.Category ?? Utils.ValueCategory.None; + if (valueCategory != Utils.ValueCategory.None) { - // Use double value editor with limit - var doubleValue = layout.DoubleValue(); doubleValue.SetCategory(valueCategory); - doubleValue.SetLimits((LimitAttribute)limit); - doubleValue.ValueBox.ValueChanged += OnValueChanged; - doubleValue.ValueBox.SlidingEnd += ClearToken; - _element = doubleValue; LinkedLabel.SetupContextMenu += (label, menu, editor) => { menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); }); mb.AutoCheck = true; mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None; }; - return; } } - if (_element == null) - { - // Use double value editor - var doubleValue = layout.DoubleValue(); - doubleValue.SetCategory(valueCategory); - doubleValue.ValueBox.ValueChanged += OnValueChanged; - doubleValue.ValueBox.SlidingEnd += ClearToken; - LinkedLabel.SetupContextMenu += (label, menu, editor) => - { - menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); - mb.AutoCheck = true; - mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None; - }; - _element = doubleValue; - } } private void OnValueChanged() diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index af6a95c4d..9d3ff1490 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -3,7 +3,6 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; -using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using Utils = FlaxEngine.Utils; @@ -29,60 +28,40 @@ namespace FlaxEditor.CustomEditors.Editors public override void Initialize(LayoutElementsContainer layout) { _element = null; - - // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); - var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; + var range = (RangeAttribute)attributes?.FirstOrDefault(x => x is RangeAttribute); + if (range != null) + { + // Use slider + var slider = layout.Slider(); + slider.Slider.SetLimits(range); + slider.Slider.ValueChanged += OnValueChanged; + slider.Slider.SlidingEnd += ClearToken; + _element = slider; + return; + } + + var floatValue = layout.FloatValue(); + floatValue.ValueBox.ValueChanged += OnValueChanged; + floatValue.ValueBox.SlidingEnd += ClearToken; + _element = floatValue; if (attributes != null) { - var range = attributes.FirstOrDefault(x => x is RangeAttribute); - if (range != null) + var limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); + floatValue.SetLimits(limit); + var valueCategory = ((ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute))?.Category ?? Utils.ValueCategory.None; + if (valueCategory != Utils.ValueCategory.None) { - // Use slider - var slider = layout.Slider(); - slider.SetLimits((RangeAttribute)range); - slider.Slider.ValueChanged += OnValueChanged; - slider.Slider.SlidingEnd += ClearToken; - _element = slider; - return; - } - var limit = attributes.FirstOrDefault(x => x is LimitAttribute); - if (limit != null) - { - // Use float value editor with limit - var floatValue = layout.FloatValue(); - floatValue.SetLimits((LimitAttribute)limit); floatValue.SetCategory(valueCategory); - floatValue.ValueBox.ValueChanged += OnValueChanged; - floatValue.ValueBox.SlidingEnd += ClearToken; - _element = floatValue; LinkedLabel.SetupContextMenu += (label, menu, editor) => { menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); }); mb.AutoCheck = true; mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None; }; - return; } } - if (_element == null) - { - // Use float value editor - var floatValue = layout.FloatValue(); - floatValue.SetCategory(valueCategory); - floatValue.ValueBox.ValueChanged += OnValueChanged; - floatValue.ValueBox.SlidingEnd += ClearToken; - _element = floatValue; - LinkedLabel.SetupContextMenu += (label, menu, editor) => - { - menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); - mb.AutoCheck = true; - mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None; - }; - } } private void OnValueChanged() From 9e54827cb3d1acab4b43c07f4f6b912635913e5a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 20:13:41 +0100 Subject: [PATCH 66/68] Cleanup code #2213 --- .../CustomEditors/Editors/Vector3Editor.cs | 1 - .../Elements/DoubleValueElement.cs | 5 +- .../Elements/FloatValueElement.cs | 5 +- Source/Editor/GUI/Input/DoubleValueBox.cs | 16 ++++- Source/Editor/GUI/Input/FloatValueBox.cs | 17 ++++-- Source/Editor/Options/InterfaceOptions.cs | 14 +++-- Source/Editor/Utilities/ShuntingYardParser.cs | 12 ++-- Source/Editor/Utilities/Units.cs | 11 +++- Source/Editor/Utilities/Utils.cs | 59 +++++++++---------- Source/Engine/Level/Actors/Camera.h | 6 +- Source/Engine/Physics/Actors/RigidBody.h | 2 +- Source/Engine/Physics/Colliders/BoxCollider.h | 2 +- .../Physics/Colliders/CapsuleCollider.h | 4 +- .../Physics/Colliders/CharacterController.h | 10 ++-- Source/Engine/Physics/Colliders/Collider.h | 4 +- .../Engine/Physics/Colliders/SphereCollider.h | 2 +- Source/Engine/Physics/Joints/DistanceJoint.h | 4 +- Source/Engine/Physics/Joints/Joint.h | 4 +- .../Editor/ValueCategoryAttribute.cs | 8 ++- Source/Engine/Utilities/Utils.cs | 48 ++++++++++++++- 20 files changed, 150 insertions(+), 84 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index 2850fa4f3..e7170a22d 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -4,7 +4,6 @@ using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Editors { diff --git a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs index a8bfe42ba..cf16a1194 100644 --- a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs @@ -5,7 +5,6 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Elements { @@ -53,9 +52,9 @@ namespace FlaxEditor.CustomEditors.Elements } /// - /// Set the value category of this float element + /// Sets the editor value category. /// - /// + /// The category. public void SetCategory(Utils.ValueCategory category) { ValueBox.Category = category; diff --git a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs index a14c796b1..aabdb79e4 100644 --- a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs @@ -5,7 +5,6 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Elements { @@ -53,9 +52,9 @@ namespace FlaxEditor.CustomEditors.Elements } /// - /// Set the value category of this float element + /// Sets the editor value category. /// - /// + /// The category. public void SetCategory(Utils.ValueCategory category) { ValueBox.Category = category; diff --git a/Source/Editor/GUI/Input/DoubleValueBox.cs b/Source/Editor/GUI/Input/DoubleValueBox.cs index ce1eab400..86acc1203 100644 --- a/Source/Editor/GUI/Input/DoubleValueBox.cs +++ b/Source/Editor/GUI/Input/DoubleValueBox.cs @@ -14,6 +14,8 @@ namespace FlaxEditor.GUI.Input [HideInEditor] public class DoubleValueBox : ValueBox { + private Utils.ValueCategory _category = Utils.ValueCategory.None; + /// public override double Value { @@ -131,9 +133,19 @@ namespace FlaxEditor.GUI.Input } /// - /// Get or set the category of the value. This can either be none for just a number, a distance or an angle. + /// Gets or sets the category of the value. This can be none for just a number or a more specific one like a distance. /// - public Utils.ValueCategory Category = Utils.ValueCategory.None; + public Utils.ValueCategory Category + { + get => _category; + set + { + if (_category == value) + return; + _category = value; + UpdateText(); + } + } /// protected sealed override void UpdateText() diff --git a/Source/Editor/GUI/Input/FloatValueBox.cs b/Source/Editor/GUI/Input/FloatValueBox.cs index 1b81df45f..f22ecd89c 100644 --- a/Source/Editor/GUI/Input/FloatValueBox.cs +++ b/Source/Editor/GUI/Input/FloatValueBox.cs @@ -14,6 +14,8 @@ namespace FlaxEditor.GUI.Input [HideInEditor] public class FloatValueBox : ValueBox { + private Utils.ValueCategory _category = Utils.ValueCategory.None; + /// public override float Value { @@ -137,14 +139,19 @@ namespace FlaxEditor.GUI.Input Value = Value; } - private Utils.ValueCategory _category = Utils.ValueCategory.None; - /// - /// Get or set the category of the value. This can be none for just a number or a more specific one like a distance. + /// Gets or sets the category of the value. This can be none for just a number or a more specific one like a distance. /// - public Utils.ValueCategory Category { + public Utils.ValueCategory Category + { get => _category; - set { _category = value; UpdateText(); } + set + { + if (_category == value) + return; + _category = value; + UpdateText(); + } } /// diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 8d8e63899..42f3a00d2 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -118,22 +118,24 @@ namespace FlaxEditor.Options } /// - /// Options for formatting numerical values + /// Options for formatting numerical values. /// public enum ValueFormattingType { /// - /// No formatting + /// No formatting. /// None, + /// - /// Format using the base SI unit + /// Format using the base SI unit. /// BaseUnit, + /// - /// Format using a unit that matches the value best + /// Format using a unit that matches the value best. /// - AutoUnit + AutoUnit, } /// @@ -214,7 +216,7 @@ namespace FlaxEditor.Options private bool _spaceNumberAndUnits = false; /// - /// Gets or sets the option to put a space between numbers and units for unit formatting + /// Gets or sets the option to put a space between numbers and units for unit formatting. /// [DefaultValue(false)] [EditorDisplay("Interface"), EditorOrder(310), Tooltip("Put a space between numbers and units.")] diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index 85279d0a4..5ba8e969c 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -141,19 +141,17 @@ namespace FlaxEditor.Utilities }; /// - /// List known units which cannot be handled as a variable easily because they contain operator - /// symbols (mostly a forward slash). The value is the factor to calculate game units. + /// List known units which cannot be handled as a variable easily because they contain operator symbols (mostly a forward slash). The value is the factor to calculate game units. /// - // Nm is here because these values are compared case-sensitive and we don't want to confuse - // nanometers and Newtonmeters private static readonly IDictionary UnitSymbols = new Dictionary { ["cm/s"] = Units.Meters2Units / 100, ["cm/s²"] = Units.Meters2Units / 100, ["m/s"] = Units.Meters2Units, ["m/s²"] = Units.Meters2Units, - ["km/h"] = 1/3.6 * Units.Meters2Units, - ["Nm"] = Units.Meters2Units * Units.Meters2Units + ["km/h"] = 1 / 3.6 * Units.Meters2Units, + // Nm is here because these values are compared case-sensitive, and we don't want to confuse nanometers and Newtonmeters + ["Nm"] = Units.Meters2Units * Units.Meters2Units, }; /// @@ -189,7 +187,7 @@ namespace FlaxEditor.Utilities if (Operators.ContainsKey(str)) return TokenType.Operator; - if (char.IsLetter(c) || c=='²' || c=='³') + if (char.IsLetter(c) || c == '²' || c == '³') return TokenType.Variable; throw new ParsingException("wrong character"); diff --git a/Source/Editor/Utilities/Units.cs b/Source/Editor/Utilities/Units.cs index cd4e67882..4c4131337 100644 --- a/Source/Editor/Utilities/Units.cs +++ b/Source/Editor/Utilities/Units.cs @@ -1,5 +1,10 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + namespace FlaxEditor.Utilities; +/// +/// Units display utilities for Editor. +/// public class Units { /// @@ -8,7 +13,7 @@ public class Units public static readonly float Meters2Units = 100f; /// - /// Set it to false to always show game units without any postfix. + /// False to always show game units without any postfix. /// public static bool UseUnitsFormatting = true; @@ -25,8 +30,8 @@ public class Units /// /// Return the unit according to user settings. /// - /// - /// + /// The unit name. + /// The formatted text. public static string Unit(string unit) { if (SpaceNumberAndUnits) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 39e371b28..1b76de47e 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1248,38 +1248,37 @@ namespace FlaxEditor.Utilities { switch (category) { - case FlaxEngine.Utils.ValueCategory.Distance: - if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); - var absValue = Mathf.Abs(value); - // in case a unit != cm this would be (value / Meters2Units * 100) - if (absValue < Units.Meters2Units) - return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("cm"); - if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); - return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("km"); - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; - case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("s"); - // some fonts have a symbol for that: "\u33A7" - case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s"); - case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s²"); - case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m²"); - case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m³"); - case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("kg"); - case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("N"); - case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("Nm"); - case FlaxEngine.Utils.ValueCategory.None: - default: - return FormatFloat(value); + case FlaxEngine.Utils.ValueCategory.Distance: + if (!Units.AutomaticUnitsFormatting) + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); + var absValue = Mathf.Abs(value); + // in case a unit != cm this would be (value / Meters2Units * 100) + if (absValue < Units.Meters2Units) + return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("cm"); + if (absValue < Units.Meters2Units * 1000) + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); + return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("km"); + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; + case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("s"); + // some fonts have a symbol for that: "\u33A7" + case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s"); + case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s²"); + case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m²"); + case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m³"); + case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("kg"); + case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("N"); + case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("Nm"); + case FlaxEngine.Utils.ValueCategory.None: + default: return FormatFloat(value); } } /// - /// Format a float value either as-is, with a distance unit or with a degree sign + /// Format a float value either as-is, with a distance unit or with a degree sign. /// - /// the value to format - /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign - /// the formatted string + /// The value to format. + /// The value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign. + /// The formatted string. public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { if (float.IsPositiveInfinity(value) || value == float.MaxValue) @@ -1295,9 +1294,9 @@ namespace FlaxEditor.Utilities /// /// Format a double value either as-is, with a distance unit or with a degree sign /// - /// the value to format - /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign - /// the formatted string + /// The value to format. + /// The value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign. + /// The formatted string. public static string FormatFloat(double value, FlaxEngine.Utils.ValueCategory category) { if (double.IsPositiveInfinity(value) || value == double.MaxValue) diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h index e7c7971b7..190126e54 100644 --- a/Source/Engine/Level/Actors/Camera.h +++ b/Source/Engine/Level/Actors/Camera.h @@ -77,7 +77,7 @@ public: /// /// Gets the camera's field of view (in degrees). /// - API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(60.0f), Limit(0, 179), EditorDisplay(\"Camera\", \"Field Of View\"), VisibleIf(nameof(UsePerspective))") + API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(60.0f), Limit(0, 179), EditorDisplay(\"Camera\", \"Field Of View\"), VisibleIf(nameof(UsePerspective)), ValueCategory(Utils.ValueCategory.Angle)") float GetFieldOfView() const; /// @@ -99,7 +99,7 @@ public: /// /// Gets camera's near plane distance. /// - API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(10.0f), Limit(0, 1000, 0.05f), EditorDisplay(\"Camera\")") + API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(10.0f), Limit(0, 1000, 0.05f), EditorDisplay(\"Camera\"), ValueCategory(Utils.ValueCategory.Distance)") float GetNearPlane() const; /// @@ -110,7 +110,7 @@ public: /// /// Gets camera's far plane distance. /// - API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(40000.0f), Limit(0, float.MaxValue, 5), EditorDisplay(\"Camera\")") + API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(40000.0f), Limit(0, float.MaxValue, 5), EditorDisplay(\"Camera\"), ValueCategory(Utils.ValueCategory.Distance)") float GetFarPlane() const; /// diff --git a/Source/Engine/Physics/Actors/RigidBody.h b/Source/Engine/Physics/Actors/RigidBody.h index e23c4dde8..c862a8e97 100644 --- a/Source/Engine/Physics/Actors/RigidBody.h +++ b/Source/Engine/Physics/Actors/RigidBody.h @@ -181,7 +181,7 @@ public: /// /// Gets the mass value measured in kilograms (use override value only if OverrideMass is checked). /// - API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\"), NumberCategory(Utils.ValueCategory.Mass)") + API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\"), ValueCategory(Utils.ValueCategory.Mass)") float GetMass() const; /// diff --git a/Source/Engine/Physics/Colliders/BoxCollider.h b/Source/Engine/Physics/Colliders/BoxCollider.h index e875217a4..58298ada8 100644 --- a/Source/Engine/Physics/Colliders/BoxCollider.h +++ b/Source/Engine/Physics/Colliders/BoxCollider.h @@ -23,7 +23,7 @@ public: /// Gets the size of the box, measured in the object's local space. /// /// The box size will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Float3), \"100,100,100\"), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Float3), \"100,100,100\"), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE Float3 GetSize() const { return _size; diff --git a/Source/Engine/Physics/Colliders/CapsuleCollider.h b/Source/Engine/Physics/Colliders/CapsuleCollider.h index f7870e29c..c225ac2a5 100644 --- a/Source/Engine/Physics/Colliders/CapsuleCollider.h +++ b/Source/Engine/Physics/Colliders/CapsuleCollider.h @@ -25,7 +25,7 @@ public: /// Gets the radius of the sphere, measured in the object's local space. /// /// The sphere radius will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetRadius() const { return _radius; @@ -41,7 +41,7 @@ public: /// Gets the height of the capsule, measured in the object's local space between the centers of the hemispherical ends. /// /// The capsule height will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetHeight() const { return _height; diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index 829d1617d..545f4e9d9 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -73,7 +73,7 @@ public: /// /// Gets the radius of the sphere, measured in the object's local space. The sphere radius will be scaled by the actor's world scale. /// - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") float GetRadius() const; /// @@ -84,7 +84,7 @@ public: /// /// Gets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale. /// - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") float GetHeight() const; /// @@ -95,7 +95,7 @@ public: /// /// Gets the slope limit (in degrees). Limits the collider to only climb slopes that are less steep (in degrees) than the indicated value. /// - API_PROPERTY(Attributes="EditorOrder(210), DefaultValue(45.0f), Limit(0, 100), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Angle)") + API_PROPERTY(Attributes="EditorOrder(210), DefaultValue(45.0f), Limit(0, 100), EditorDisplay(\"Character Controller\"), ValueCategory(Utils.ValueCategory.Angle)") float GetSlopeLimit() const; /// @@ -117,7 +117,7 @@ public: /// /// Gets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controller’s height or it will generate an error. /// - API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\"), ValueCategory(Utils.ValueCategory.Distance)") float GetStepOffset() const; /// @@ -139,7 +139,7 @@ public: /// /// Gets the minimum move distance of the character controller. The minimum traveled distance to consider. If traveled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small. /// - API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\"), ValueCategory(Utils.ValueCategory.Distance)") float GetMinMoveDistance() const; /// diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index 36db78119..05f2a6eb2 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -52,7 +52,7 @@ public: /// /// Gets the center of the collider, measured in the object's local space. /// - API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE Vector3 GetCenter() const { return _center; @@ -66,7 +66,7 @@ public: /// /// Gets the contact offset. Colliders whose distance is less than the sum of their ContactOffset values will generate contacts. The contact offset must be positive. Contact offset allows the collision detection system to predictively enforce the contact constraint even when the objects are slightly separated. /// - API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(2.0f), Limit(0, 100), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(2.0f), Limit(0, 100), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetContactOffset() const { return _contactOffset; diff --git a/Source/Engine/Physics/Colliders/SphereCollider.h b/Source/Engine/Physics/Colliders/SphereCollider.h index 5c97bc553..e3b295eda 100644 --- a/Source/Engine/Physics/Colliders/SphereCollider.h +++ b/Source/Engine/Physics/Colliders/SphereCollider.h @@ -21,7 +21,7 @@ public: /// Gets the radius of the sphere, measured in the object's local space. /// /// The sphere radius will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetRadius() const { return _radius; diff --git a/Source/Engine/Physics/Joints/DistanceJoint.h b/Source/Engine/Physics/Joints/DistanceJoint.h index f4a60cb62..0f0f59b01 100644 --- a/Source/Engine/Physics/Joints/DistanceJoint.h +++ b/Source/Engine/Physics/Joints/DistanceJoint.h @@ -67,7 +67,7 @@ public: /// Gets the allowed minimum distance for the joint. /// /// Used only when DistanceJointFlag.MinDistance flag is set. The minimum distance must be no more than the maximum distance. Default: 0, Range: [0, float.MaxValue]. - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(0.0f), Limit(0.0f), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(0.0f), Limit(0.0f), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetMinDistance() const { return _minDistance; @@ -83,7 +83,7 @@ public: /// Gets the allowed maximum distance for the joint. /// /// Used only when DistanceJointFlag.MaxDistance flag is set. The maximum distance must be no less than the minimum distance. Default: 0, Range: [0, float.MaxValue]. - API_PROPERTY(Attributes="EditorOrder(120), DefaultValue(10.0f), Limit(0.0f), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(120), DefaultValue(10.0f), Limit(0.0f), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetMaxDistance() const { return _maxDistance; diff --git a/Source/Engine/Physics/Joints/Joint.h b/Source/Engine/Physics/Joints/Joint.h index 408d19ae5..a4584a07e 100644 --- a/Source/Engine/Physics/Joints/Joint.h +++ b/Source/Engine/Physics/Joints/Joint.h @@ -38,7 +38,7 @@ public: /// /// Gets the break force. Determines the maximum force the joint can apply before breaking. Broken joints no longer participate in physics simulation. /// - API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Force)") + API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Force)") FORCE_INLINE float GetBreakForce() const { return _breakForce; @@ -52,7 +52,7 @@ public: /// /// Gets the break torque. Determines the maximum torque the joint can apply before breaking. Broken joints no longer participate in physics simulation. /// - API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Torque)") + API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Torque)") FORCE_INLINE float GetBreakTorque() const { return _breakTorque; diff --git a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs index 8ba91e9d0..4f51efbe3 100644 --- a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs @@ -1,13 +1,12 @@ -// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; namespace FlaxEngine { /// - /// Used to specify the value category of a numeric value as either as-is (a scalar), a distance (formatted as cm/m/km) or an angle (formatted with a degree sign). + /// Specifies the value category of a numeric value as either as-is (a scalar), a distance (formatted as cm/m/km) or an angle (formatted with a degree sign). /// - /// [Serializable] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class ValueCategoryAttribute : Attribute @@ -17,6 +16,9 @@ namespace FlaxEngine /// public Utils.ValueCategory Category; + /// + /// Initializes a new instance of the class. + /// private ValueCategoryAttribute() { Category = Utils.ValueCategory.None; diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index 50e2a7d50..0767ab8f4 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -257,6 +257,7 @@ namespace FlaxEngine { public static FieldInfo itemsField; } + internal static T[] ExtractArrayFromList(List list) { if (list == null) @@ -1040,21 +1041,64 @@ namespace FlaxEngine } /// - /// A category of number values used for formatting and input boxes + /// A category of number values used for formatting and input fields. /// public enum ValueCategory { + /// + /// Nothing. + /// None, + + /// + /// Distance (eg. meters). + /// Distance, + + /// + /// Area (eg. m^2). + /// Area, + + /// + /// Volume (eg. m^3). + /// Volume, + + /// + /// Mass (eg. kilograms). + /// Mass, + + /// + /// Angle (eg. degrees). + /// Angle, + + /// + /// Speed (distance / time). + /// Speed, + + /// + /// Acceleration (distance^2 / time). + /// Acceleration, + + /// + /// Time (eg. seconds). + /// Time, + + /// + /// Force (mass * distance / time^2). + /// Force, - Torque + + /// + /// Torque (mass * distance^2 / time^2). + /// + Torque, } } } From fc37cb3986f5c8aa4e7db9b2b1ee66cc93975434 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 20:14:07 +0100 Subject: [PATCH 67/68] Update values formatting on editor options save #2213 --- Source/Editor/Options/InterfaceOptions.cs | 26 ++++------------------- Source/Editor/Options/OptionsModule.cs | 21 ++++++++++++++++++ Source/Editor/Utilities/Units.cs | 4 ++-- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 42f3a00d2..4cf1fe050 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -196,37 +196,19 @@ namespace FlaxEditor.Options [EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")] public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal; - private ValueFormattingType _valueFormatting = ValueFormattingType.None; - /// /// Gets or sets the formatting option for numeric values in the editor. /// [DefaultValue(ValueFormattingType.None)] - [EditorDisplay("Interface"), EditorOrder(300), Tooltip("Formatting of numeric values.")] - public ValueFormattingType ValueFormatting { - get => _valueFormatting; - set - { - _valueFormatting = value; - Units.UseUnitsFormatting = _valueFormatting != ValueFormattingType.None; - Units.AutomaticUnitsFormatting = _valueFormatting == ValueFormattingType.AutoUnit; - } - } - - private bool _spaceNumberAndUnits = false; + [EditorDisplay("Interface"), EditorOrder(300)] + public ValueFormattingType ValueFormatting { get; set; } /// /// Gets or sets the option to put a space between numbers and units for unit formatting. /// [DefaultValue(false)] - [EditorDisplay("Interface"), EditorOrder(310), Tooltip("Put a space between numbers and units.")] - public bool SpaceNumberAndUnits { get => _spaceNumberAndUnits; - set - { - _spaceNumberAndUnits = value; - Units.SpaceNumberAndUnits = _spaceNumberAndUnits; - } - } + [EditorDisplay("Interface"), EditorOrder(310)] + public bool SeparateValueAndUnit { get; set; } /// /// Gets or sets the timestamps prefix mode for output log messages. diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 2dceb3400..debc0a663 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -200,6 +200,27 @@ namespace FlaxEditor.Options EditorAssets.Cache.OnEditorOptionsChanged(Options); + // Units formatting options + bool useUnitsFormatting = Options.Interface.ValueFormatting != InterfaceOptions.ValueFormattingType.None; + bool automaticUnitsFormatting = Options.Interface.ValueFormatting == InterfaceOptions.ValueFormattingType.AutoUnit; + bool separateValueAndUnit = Options.Interface.SeparateValueAndUnit; + if (useUnitsFormatting != Utilities.Units.UseUnitsFormatting || + automaticUnitsFormatting != Utilities.Units.AutomaticUnitsFormatting || + separateValueAndUnit != Utilities.Units.SeparateValueAndUnit) + { + Utilities.Units.UseUnitsFormatting = useUnitsFormatting; + Utilities.Units.AutomaticUnitsFormatting = automaticUnitsFormatting; + Utilities.Units.SeparateValueAndUnit = separateValueAndUnit; + + // Refresh UI in property panels + Editor.Windows.PropertiesWin?.Presenter.BuildLayoutOnUpdate(); + foreach (var window in Editor.Windows.Windows) + { + if (window is Windows.Assets.PrefabWindow prefabWindow) + prefabWindow.Presenter.BuildLayoutOnUpdate(); + } + } + // Send event OptionsChanged?.Invoke(Options); } diff --git a/Source/Editor/Utilities/Units.cs b/Source/Editor/Utilities/Units.cs index 4c4131337..64977c18b 100644 --- a/Source/Editor/Utilities/Units.cs +++ b/Source/Editor/Utilities/Units.cs @@ -20,7 +20,7 @@ public class Units /// /// Add a space between numbers and units for readability. /// - public static bool SpaceNumberAndUnits = true; + public static bool SeparateValueAndUnit = true; /// /// If set to true, the distance unit is chosen on the magnitude, otherwise it's meters. @@ -34,7 +34,7 @@ public class Units /// The formatted text. public static string Unit(string unit) { - if (SpaceNumberAndUnits) + if (SeparateValueAndUnit) return $" {unit}"; return unit; } From 292694d89c40e6ea9ac55aa01f4e04ad2649c76d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 20:15:56 +0100 Subject: [PATCH 68/68] Fix selecting `CanvasContainer` in UI Editor queries --- Source/Editor/Gizmo/UIEditorGizmo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index ea4b75d28..43461b0a6 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -689,7 +689,7 @@ namespace FlaxEditor #else // Find any control under mouse (hierarchical) hit = GetChildAtRecursive(location); - if (hit is View) + if (hit is View || hit is CanvasContainer) hit = null; return hit != null; #endif