diff --git a/Content/Editor/Particles/Smoke.flax b/Content/Editor/Particles/Smoke.flax index 1335a84f4..b42c2f325 100644 --- a/Content/Editor/Particles/Smoke.flax +++ b/Content/Editor/Particles/Smoke.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3dc51e7805056006ca6cbb481ba202583a9b2287c152fc04e28e1d07747d6ce -size 14706 +oid sha256:334ac0d00495fc88b10839061ff0c3f45323d4f75ab6176b19005199a7324a19 +size 14569 diff --git a/Content/Editor/Particles/Sparks.flax b/Content/Editor/Particles/Sparks.flax index 7977e231b..7ee0ed6e9 100644 --- a/Content/Editor/Particles/Sparks.flax +++ b/Content/Editor/Particles/Sparks.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77d902ab5f79426cc66dc5f19a3b8280136a58aa3c6fd317554d1a032357c65a -size 15275 +oid sha256:87046a9bfe275cac290b4764de8a512c222ccc386d01af9026d57c3e4b7773b6 +size 13625 diff --git a/Content/Editor/Scripting/ScriptTemplate.cs b/Content/Editor/Scripting/ScriptTemplate.cs index 663acf05f..30fbe9d86 100644 --- a/Content/Editor/Scripting/ScriptTemplate.cs +++ b/Content/Editor/Scripting/ScriptTemplate.cs @@ -33,4 +33,3 @@ public class %class% : Script // Here you can add code that needs to be called every frame } } - diff --git a/Flax.flaxproj b/Flax.flaxproj index 6b3014e94..a3157a032 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -3,7 +3,8 @@ "Version": { "Major": 1, "Minor": 7, - "Build": 6402 + "Revision": 0, + "Build": 6404 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.", diff --git a/Source/Editor/Options/InputBinding.cs b/Source/Editor/Options/InputBinding.cs index eb61c0f68..95c3c1d6f 100644 --- a/Source/Editor/Options/InputBinding.cs +++ b/Source/Editor/Options/InputBinding.cs @@ -259,10 +259,7 @@ namespace FlaxEditor.Options public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) - { return true; - } - return base.CanConvertFrom(context, sourceType); } @@ -270,9 +267,7 @@ namespace FlaxEditor.Options public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (destinationType == typeof(string)) - { return false; - } return base.CanConvertTo(context, destinationType); } @@ -284,7 +279,6 @@ namespace FlaxEditor.Options InputBinding.TryParse(str, out var result); return result; } - return base.ConvertFrom(context, culture, value); } @@ -295,7 +289,6 @@ namespace FlaxEditor.Options { return ((InputBinding)value).ToString(); } - return base.ConvertTo(context, culture, value, destinationType); } } diff --git a/Source/Editor/Options/ViewportOptions.cs b/Source/Editor/Options/ViewportOptions.cs index cee63a562..0fd018cbc 100644 --- a/Source/Editor/Options/ViewportOptions.cs +++ b/Source/Editor/Options/ViewportOptions.cs @@ -26,45 +26,108 @@ namespace FlaxEditor.Options public float MouseWheelSensitivity { get; set; } = 1.0f; /// - /// Gets or sets the default movement speed for the viewport camera (must match the dropdown menu values in the viewport). + /// Gets or sets the total amount of steps the camera needs to go from minimum to maximum speed. /// - [DefaultValue(1.0f), Limit(0.01f, 100.0f)] - [EditorDisplay("Defaults"), EditorOrder(110), Tooltip("The default movement speed for the viewport camera (must match the dropdown menu values in the viewport).")] - public float DefaultMovementSpeed { get; set; } = 1.0f; + [DefaultValue(64), Limit(1, 128)] + [EditorDisplay("Camera"), EditorOrder(110), Tooltip("The total amount of steps the camera needs to go from minimum to maximum speed.")] + public int TotalCameraSpeedSteps { get; set; } = 64; + + /// + /// Gets or sets the degree to which the camera will be eased when using camera flight in the editor window. + /// + [DefaultValue(3.0f), Limit(1.0f, 8.0f)] + [EditorDisplay("Camera"), EditorOrder(111), Tooltip("The degree to which the camera will be eased when using camera flight in the editor window (ignored if camera easing degree is enabled).")] + public float CameraEasingDegree { get; set; } = 3.0f; + + /// + /// Gets or sets the default movement speed for the viewport camera (must be in range between minimum and maximum movement speed values). + /// + [DefaultValue(1.0f), Limit(0.05f, 32.0f)] + [EditorDisplay("Defaults"), EditorOrder(120), Tooltip("The default movement speed for the viewport camera (must be in range between minimum and maximum movement speed values).")] + public float MovementSpeed { get; set; } = 1.0f; + + /// + /// Gets or sets the default minimum camera movement speed. + /// + [DefaultValue(0.05f), Limit(0.05f, 32.0f)] + [EditorDisplay("Defaults"), EditorOrder(121), Tooltip("The default minimum movement speed for the viewport camera.")] + public float MinMovementSpeed { get; set; } = 0.05f; + + /// + /// Gets or sets the default maximum camera movement speed. + /// + [DefaultValue(32.0f), Limit(16.0f, 1000.0f)] + [EditorDisplay("Defaults"), EditorOrder(122), Tooltip("The default maximum movement speed for the viewport camera.")] + public float MaxMovementSpeed { get; set; } = 32f; + + /// + /// Gets or sets the default camera easing mode. + /// + [DefaultValue(true)] + [EditorDisplay("Defaults"), EditorOrder(130), Tooltip("The default camera easing mode.")] + public bool UseCameraEasing { get; set; } = true; /// /// Gets or sets the default near clipping plane distance for the viewport camera. /// [DefaultValue(10.0f), Limit(0.001f, 1000.0f)] - [EditorDisplay("Defaults"), EditorOrder(120), Tooltip("The default near clipping plane distance for the viewport camera.")] - public float DefaultNearPlane { get; set; } = 10.0f; + [EditorDisplay("Defaults"), EditorOrder(140), Tooltip("The default near clipping plane distance for the viewport camera.")] + public float NearPlane { get; set; } = 10.0f; /// /// Gets or sets the default far clipping plane distance for the viewport camera. /// [DefaultValue(40000.0f), Limit(10.0f)] - [EditorDisplay("Defaults"), EditorOrder(130), Tooltip("The default far clipping plane distance for the viewport camera.")] - public float DefaultFarPlane { get; set; } = 40000.0f; + [EditorDisplay("Defaults"), EditorOrder(150), Tooltip("The default far clipping plane distance for the viewport camera.")] + public float FarPlane { get; set; } = 40000.0f; /// /// Gets or sets the default field of view angle (in degrees) for the viewport camera. /// [DefaultValue(60.0f), Limit(35.0f, 160.0f, 0.1f)] - [EditorDisplay("Defaults", "Default Field Of View"), EditorOrder(140), Tooltip("The default field of view angle (in degrees) for the viewport camera.")] - public float DefaultFieldOfView { get; set; } = 60.0f; + [EditorDisplay("Defaults"), EditorOrder(160), Tooltip("The default field of view angle (in degrees) for the viewport camera.")] + public float FieldOfView { get; set; } = 60.0f; /// - /// Gets or sets if the panning direction is inverted for the viewport camera. + /// Gets or sets the default camera orthographic mode. /// [DefaultValue(false)] - [EditorDisplay("Defaults"), EditorOrder(150), Tooltip("Invert the panning direction for the viewport camera.")] - public bool DefaultInvertPanning { get; set; } = false; + [EditorDisplay("Defaults"), EditorOrder(170), Tooltip("The default camera orthographic mode.")] + public bool UseOrthographicProjection { get; set; } = false; /// - /// Scales editor viewport grid. + /// Gets or sets the default camera orthographic scale (if camera uses orthographic mode). + /// + [DefaultValue(5.0f), Limit(0.001f, 100000.0f, 0.1f)] + [EditorDisplay("Defaults"), EditorOrder(180), Tooltip("The default camera orthographic scale (if camera uses orthographic mode).")] + public float OrthographicScale { get; set; } = 5.0f; + + /// + /// Gets or sets the default panning direction for the viewport camera. + /// + [DefaultValue(false)] + [EditorDisplay("Defaults"), EditorOrder(190), Tooltip("The default panning direction for the viewport camera.")] + public bool InvertPanning { get; set; } = false; + + /// + /// Gets or sets the default relative panning mode. + /// + [DefaultValue(true)] + [EditorDisplay("Defaults"), EditorOrder(200), Tooltip("The default relative panning mode. Uses distance between camera and target to determine panning speed.")] + public bool UseRelativePanning { get; set; } = true; + + /// + /// Gets or sets the default panning speed (ignored if relative panning is speed enabled). + /// + [DefaultValue(0.8f), Limit(0.01f, 128.0f, 0.1f)] + [EditorDisplay("Defaults"), EditorOrder(210), Tooltip("The default camera panning speed (ignored if relative panning is enabled).")] + public float PanningSpeed { get; set; } = 0.8f; + + /// + /// Gets or sets the default editor viewport grid scale. /// [DefaultValue(50.0f), Limit(25.0f, 500.0f, 5.0f)] - [EditorDisplay("Defaults"), EditorOrder(160), Tooltip("Scales editor viewport grid.")] + [EditorDisplay("Defaults"), EditorOrder(220), Tooltip("The default editor viewport grid scale.")] public float ViewportGridScale { get; set; } = 50.0f; } } diff --git a/Source/Editor/ProjectInfo.cpp b/Source/Editor/ProjectInfo.cpp index 4e7ee4483..30c558b5d 100644 --- a/Source/Editor/ProjectInfo.cpp +++ b/Source/Editor/ProjectInfo.cpp @@ -154,7 +154,8 @@ bool ProjectInfo::LoadProject(const String& projectPath) Version = ::Version( JsonTools::GetInt(version, "Major", 0), JsonTools::GetInt(version, "Minor", 0), - JsonTools::GetInt(version, "Build", 0)); + JsonTools::GetInt(version, "Build", -1), + JsonTools::GetInt(version, "Revision", -1)); } } if (Version.Revision() == 0) diff --git a/Source/Editor/ProjectInfo.cs b/Source/Editor/ProjectInfo.cs index b00c4e042..083665f0f 100644 --- a/Source/Editor/ProjectInfo.cs +++ b/Source/Editor/ProjectInfo.cs @@ -23,17 +23,11 @@ namespace FlaxEditor public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (value == null) - { writer.WriteNull(); - } else if (value is Version) - { writer.WriteValue(value.ToString()); - } else - { throw new JsonSerializationException("Expected Version object value"); - } } /// @@ -47,65 +41,60 @@ namespace FlaxEditor public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) - { return null; - } - else + + if (reader.TokenType == JsonToken.StartObject) { - if (reader.TokenType == JsonToken.StartObject) + try { - try + reader.Read(); + var values = new Dictionary(); + while (reader.TokenType == JsonToken.PropertyName) { + var key = reader.Value as string; reader.Read(); - Dictionary values = new Dictionary(); - while (reader.TokenType == JsonToken.PropertyName) - { - var key = reader.Value as string; - reader.Read(); - var val = (long)reader.Value; - reader.Read(); - values.Add(key, (int)val); - } + var val = (long)reader.Value; + reader.Read(); + values.Add(key, (int)val); + } - int major = 0, minor = 0, build = 0; - values.TryGetValue("Major", out major); - values.TryGetValue("Minor", out minor); - values.TryGetValue("Build", out build); + values.TryGetValue("Major", out var major); + values.TryGetValue("Minor", out var minor); + if (!values.TryGetValue("Build", out var build)) + build = -1; + if (!values.TryGetValue("Revision", out var revision)) + revision = -1; - Version v = new Version(major, minor, build); - return v; - } - catch (Exception ex) - { - throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex); - } + if (build <= 0) + return new Version(major, minor); + if (revision <= 0) + return new Version(major, minor, build); + return new Version(major, minor, build, revision); } - else if (reader.TokenType == JsonToken.String) + catch (Exception ex) { - try - { - Version v = new Version((string)reader.Value!); - return v; - } - catch (Exception ex) - { - throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex); - } - } - else - { - throw new Exception(String.Format("Unexpected token or value when parsing version. Token: {0}, Value: {1}", reader.TokenType, reader.Value)); + throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex); } } + if (reader.TokenType == JsonToken.String) + { + try + { + return new Version((string)reader.Value!); + } + catch (Exception ex) + { + throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex); + } + } + throw new Exception(String.Format("Unexpected token or value when parsing version. Token: {0}, Value: {1}", reader.TokenType, reader.Value)); } /// /// Determines whether this instance can convert the specified object type. /// /// Type of the object. - /// - /// true if this instance can convert the specified object type; otherwise, false. - /// + /// true if this instance can convert the specified object type; otherwise, false. public override bool CanConvert(Type objectType) { return objectType == typeof(Version); diff --git a/Source/Editor/Viewport/Cameras/FPSCamera.cs b/Source/Editor/Viewport/Cameras/FPSCamera.cs index e578933cf..bf2e840ea 100644 --- a/Source/Editor/Viewport/Cameras/FPSCamera.cs +++ b/Source/Editor/Viewport/Cameras/FPSCamera.cs @@ -259,7 +259,10 @@ namespace FlaxEditor.Viewport.Cameras // Pan if (input.IsPanning) { - var panningSpeed = 0.8f; + var panningSpeed = (Viewport.RelativePanning) + ? Mathf.Abs((position - TargetPoint).Length) * 0.005f + : Viewport.PanningSpeed; + if (Viewport.InvertPanning) { position += up * (mouseDelta.Y * panningSpeed); diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 9a444f2da..c49392d01 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -128,12 +128,26 @@ namespace FlaxEditor.Viewport public const int FpsCameraFilteringFrames = 3; /// - /// The speed widget button. + /// The camera settings widget. /// - protected ViewportWidgetButton _speedWidget; + protected ViewportWidgetsContainer _cameraWidget; + + /// + /// The camera settings widget button. + /// + protected ViewportWidgetButton _cameraButton; + + /// + /// The orthographic mode widget button. + /// + protected ViewportWidgetButton _orthographicModeButton; + + private readonly Editor _editor; private float _mouseSensitivity; private float _movementSpeed; + private float _minMovementSpeed; + private float _maxMovementSpeed; private float _mouseAccelerationScale; private bool _useMouseFiltering; private bool _useMouseAcceleration; @@ -174,11 +188,17 @@ namespace FlaxEditor.Viewport private float _fieldOfView; private float _nearPlane; private float _farPlane; - private float _orthoSize = 1.0f; - private bool _isOrtho = false; - private float _wheelMovementChangeDeltaSum = 0; + private float _orthoSize; + private bool _isOrtho; + private bool _useCameraEasing; + private float _cameraEasingDegree; + private float _panningSpeed; + private bool _relativePanning; private bool _invertPanning; + private int _speedStep; + private int _maxSpeedSteps; + /// /// Speed of the mouse. /// @@ -189,6 +209,25 @@ namespace FlaxEditor.Viewport /// public float MouseWheelZoomSpeedFactor = 1; + /// + /// Format of the text for the camera move speed. + /// + private string MovementSpeedTextFormat + { + get + { + if (Mathf.Abs(_movementSpeed - _maxMovementSpeed) < Mathf.Epsilon || Mathf.Abs(_movementSpeed - _minMovementSpeed) < Mathf.Epsilon) + return "{0:0.##}"; + + if (_movementSpeed < 10.0f) + return "{0:0.00}"; + else if (_movementSpeed < 100.0f) + return "{0:0.0}"; + else + return "{0:#}"; + } + } + /// /// Gets or sets the camera movement speed. /// @@ -197,19 +236,40 @@ namespace FlaxEditor.Viewport get => _movementSpeed; set { - for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++) - { - if (Math.Abs(value - EditorViewportCameraSpeedValues[i]) < 0.001f) - { - _movementSpeed = EditorViewportCameraSpeedValues[i]; - if (_speedWidget != null) - _speedWidget.Text = _movementSpeed.ToString(); - break; - } - } + _movementSpeed = value; + + if (_cameraButton != null) + _cameraButton.Text = string.Format(MovementSpeedTextFormat, _movementSpeed); } } + /// + /// Gets or sets the minimum camera movement speed. + /// + public float MinMovementSpeed + { + get => _minMovementSpeed; + set => _minMovementSpeed = value; + } + + /// + /// Gets or sets the maximum camera movement speed. + /// + public float MaxMovementSpeed + { + get => _maxMovementSpeed; + set => _maxMovementSpeed = value; + } + + /// + /// Gets or sets the camera easing mode. + /// + public bool UseCameraEasing + { + get => _useCameraEasing; + set => _useCameraEasing = value; + } + /// /// Gets the mouse movement position delta (user press and move). /// @@ -396,6 +456,15 @@ namespace FlaxEditor.Viewport set => _isOrtho = value; } + /// + /// Gets or sets if the panning speed should be relative to the camera target. + /// + public bool RelativePanning + { + get => _relativePanning; + set => _relativePanning = value; + } + /// /// Gets or sets if the panning direction is inverted. /// @@ -405,6 +474,15 @@ namespace FlaxEditor.Viewport set => _invertPanning = value; } + /// + /// Gets or sets the camera panning speed. + /// + public float PanningSpeed + { + get => _panningSpeed; + set => _panningSpeed = value; + } + /// /// The input actions collection to processed during user input. /// @@ -419,6 +497,8 @@ namespace FlaxEditor.Viewport public EditorViewport(SceneRenderTask task, ViewportCamera camera, bool useWidgets) : base(task) { + _editor = Editor.Instance; + _mouseAccelerationScale = 0.1f; _useMouseFiltering = false; _useMouseAcceleration = false; @@ -431,43 +511,299 @@ namespace FlaxEditor.Viewport // Setup options { - var options = Editor.Instance.Options.Options; - _movementSpeed = options.Viewport.DefaultMovementSpeed; - _nearPlane = options.Viewport.DefaultNearPlane; - _farPlane = options.Viewport.DefaultFarPlane; - _fieldOfView = options.Viewport.DefaultFieldOfView; - _invertPanning = options.Viewport.DefaultInvertPanning; - Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged; - OnEditorOptionsChanged(options); + SetupViewportOptions(); } + // Initialize camera values from cache + if (_editor.ProjectCache.TryGetCustomData("CameraMovementSpeedValue", out var cachedState)) + MovementSpeed = float.Parse(cachedState); + if (_editor.ProjectCache.TryGetCustomData("CameraMinMovementSpeedValue", out cachedState)) + _minMovementSpeed = float.Parse(cachedState); + if (_editor.ProjectCache.TryGetCustomData("CameraMaxMovementSpeedValue", out cachedState)) + _maxMovementSpeed = float.Parse(cachedState); + if (_editor.ProjectCache.TryGetCustomData("UseCameraEasingState", out cachedState)) + _useCameraEasing = bool.Parse(cachedState); + if (_editor.ProjectCache.TryGetCustomData("CameraPanningSpeedValue", out cachedState)) + _panningSpeed = float.Parse(cachedState); + if (_editor.ProjectCache.TryGetCustomData("CameraInvertPanningState", out cachedState)) + _invertPanning = bool.Parse(cachedState); + if (_editor.ProjectCache.TryGetCustomData("CameraRelativePanningState", out cachedState)) + _relativePanning = bool.Parse(cachedState); + if (_editor.ProjectCache.TryGetCustomData("CameraOrthographicState", out cachedState)) + _isOrtho = bool.Parse(cachedState); + if (_editor.ProjectCache.TryGetCustomData("CameraOrthographicSizeValue", out cachedState)) + _orthoSize = float.Parse(cachedState); + if (_editor.ProjectCache.TryGetCustomData("CameraFieldOfViewValue", out cachedState)) + _fieldOfView = float.Parse(cachedState); + if (_editor.ProjectCache.TryGetCustomData("CameraNearPlaneValue", out cachedState)) + _nearPlane = float.Parse(cachedState); + if (_editor.ProjectCache.TryGetCustomData("CameraFarPlaneValue", out cachedState)) + _farPlane = float.Parse(cachedState); + + OnCameraMovementProgressChanged(); + if (useWidgets) { - var largestText = "Invert Panning"; + #region Camera settings widget + + var largestText = "Relative Panning"; var textSize = Style.Current.FontMedium.MeasureText(largestText); var xLocationForExtras = textSize.X + 5; - // Camera speed widget - var camSpeed = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var camSpeedCM = new ContextMenu(); - var camSpeedButton = new ViewportWidgetButton(_movementSpeed.ToString(), Editor.Instance.Icons.CamSpeed32, camSpeedCM) + var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X; + + // Camera Settings Widget + _cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + + // Camera Settings Menu + var cameraCM = new ContextMenu(); + _cameraButton = new ViewportWidgetButton(string.Format(MovementSpeedTextFormat, _movementSpeed), Editor.Instance.Icons.Camera64, cameraCM, false, cameraSpeedTextWidth) { Tag = this, - TooltipText = "Camera speed scale" + TooltipText = "Camera Settings", + Parent = _cameraWidget }; - _speedWidget = camSpeedButton; - for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++) - { - var v = EditorViewportCameraSpeedValues[i]; - var button = camSpeedCM.AddButton(v.ToString()); - button.Tag = v; - } - camSpeedCM.ButtonClicked += button => MovementSpeed = (float)button.Tag; - camSpeedCM.VisibleChanged += WidgetCamSpeedShowHide; - camSpeedButton.Parent = camSpeed; - camSpeed.Parent = this; + _cameraWidget.Parent = this; + + // Orthographic/Perspective Mode Widget + _orthographicModeButton = new ViewportWidgetButton(string.Empty, Editor.Instance.Icons.CamSpeed32, null, true) + { + Checked = !_isOrtho, + TooltipText = "Toggle Orthographic/Perspective Mode", + Parent = _cameraWidget + }; + _orthographicModeButton.Toggled += OnOrthographicModeToggled; + + // Camera Speed + var camSpeedButton = cameraCM.AddButton("Camera Speed"); + camSpeedButton.CloseMenuOnClick = false; + var camSpeedValue = new FloatValueBox(_movementSpeed, xLocationForExtras, 2, 70.0f, _minMovementSpeed, _maxMovementSpeed, 0.5f) + { + Parent = camSpeedButton + }; + + camSpeedValue.ValueChanged += () => OnMovementSpeedChanged(camSpeedValue); + cameraCM.VisibleChanged += control => camSpeedValue.Value = _movementSpeed; + + // Minimum & Maximum Camera Speed + var minCamSpeedButton = cameraCM.AddButton("Min Cam Speed"); + minCamSpeedButton.CloseMenuOnClick = false; + var minCamSpeedValue = new FloatValueBox(_minMovementSpeed, xLocationForExtras, 2, 70.0f, 0.05f, _maxMovementSpeed, 0.5f) + { + Parent = minCamSpeedButton + }; + var maxCamSpeedButton = cameraCM.AddButton("Max Cam Speed"); + maxCamSpeedButton.CloseMenuOnClick = false; + var maxCamSpeedValue = new FloatValueBox(_maxMovementSpeed, xLocationForExtras, 2, 70.0f, _minMovementSpeed, 1000.0f, 0.5f) + { + Parent = maxCamSpeedButton + }; + + minCamSpeedValue.ValueChanged += () => + { + OnMinMovementSpeedChanged(minCamSpeedValue); + + maxCamSpeedValue.MinValue = minCamSpeedValue.Value; + + if (Math.Abs(camSpeedValue.MinValue - minCamSpeedValue.Value) > Mathf.Epsilon) + camSpeedValue.MinValue = minCamSpeedValue.Value; + }; + cameraCM.VisibleChanged += control => minCamSpeedValue.Value = _minMovementSpeed; + maxCamSpeedValue.ValueChanged += () => + { + OnMaxMovementSpeedChanged(maxCamSpeedValue); + + minCamSpeedValue.MaxValue = maxCamSpeedValue.Value; + + if (Math.Abs(camSpeedValue.MaxValue - maxCamSpeedValue.Value) > Mathf.Epsilon) + camSpeedValue.MaxValue = maxCamSpeedValue.Value; + }; + cameraCM.VisibleChanged += control => maxCamSpeedValue.Value = _maxMovementSpeed; + + // Camera Easing + { + var useCameraEasing = cameraCM.AddButton("Camera Easing"); + useCameraEasing.CloseMenuOnClick = false; + var useCameraEasingValue = new CheckBox(xLocationForExtras, 2, _useCameraEasing) + { + Parent = useCameraEasing + }; + + useCameraEasingValue.StateChanged += OnCameraEasingToggled; + cameraCM.VisibleChanged += control => useCameraEasingValue.Checked = _useCameraEasing; + } + + // Panning Speed + { + var panningSpeed = cameraCM.AddButton("Panning Speed"); + panningSpeed.CloseMenuOnClick = false; + var panningSpeedValue = new FloatValueBox(_panningSpeed, xLocationForExtras, 2, 70.0f, 0.01f, 128.0f, 0.1f) + { + Parent = panningSpeed + }; + + panningSpeedValue.ValueChanged += () => OnPanningSpeedChanged(panningSpeedValue); + cameraCM.VisibleChanged += control => + { + panningSpeed.Visible = !_relativePanning; + panningSpeedValue.Value = _panningSpeed; + }; + } + + // Relative Panning + { + var relativePanning = cameraCM.AddButton("Relative Panning"); + relativePanning.CloseMenuOnClick = false; + var relativePanningValue = new CheckBox(xLocationForExtras, 2, _relativePanning) + { + Parent = relativePanning + }; + + relativePanningValue.StateChanged += checkBox => + { + if (checkBox.Checked != _relativePanning) + { + OnRelativePanningToggled(checkBox); + cameraCM.Hide(); + } + }; + cameraCM.VisibleChanged += control => relativePanningValue.Checked = _relativePanning; + } + + // Invert Panning + { + var invertPanning = cameraCM.AddButton("Invert Panning"); + invertPanning.CloseMenuOnClick = false; + var invertPanningValue = new CheckBox(xLocationForExtras, 2, _invertPanning) + { + Parent = invertPanning + }; + + invertPanningValue.StateChanged += OnInvertPanningToggled; + cameraCM.VisibleChanged += control => invertPanningValue.Checked = _invertPanning; + } + + cameraCM.AddSeparator(); + + // Camera Viewpoints + { + var cameraView = cameraCM.AddChildMenu("Viewpoints").ContextMenu; + for (int i = 0; i < EditorViewportCameraViewpointValues.Length; i++) + { + var co = EditorViewportCameraViewpointValues[i]; + var button = cameraView.AddButton(co.Name); + button.Tag = co.Orientation; + } + + cameraView.ButtonClicked += OnViewpointChanged; + } + + // Orthographic Mode + { + var ortho = cameraCM.AddButton("Orthographic"); + ortho.CloseMenuOnClick = false; + var orthoValue = new CheckBox(xLocationForExtras, 2, _isOrtho) + { + Parent = ortho + }; + + orthoValue.StateChanged += checkBox => + { + if (checkBox.Checked != _isOrtho) + { + OnOrthographicModeToggled(checkBox); + cameraCM.Hide(); + } + }; + cameraCM.VisibleChanged += control => orthoValue.Checked = _isOrtho; + } + + // Field of View + { + var fov = cameraCM.AddButton("Field Of View"); + fov.CloseMenuOnClick = false; + var fovValue = new FloatValueBox(_fieldOfView, xLocationForExtras, 2, 70.0f, 35.0f, 160.0f, 0.1f) + { + Parent = fov + }; + + fovValue.ValueChanged += () => OnFieldOfViewChanged(fovValue); + cameraCM.VisibleChanged += control => + { + fov.Visible = !_isOrtho; + fovValue.Value = _fieldOfView; + }; + } + + // Orthographic Scale + { + var orthoSize = cameraCM.AddButton("Ortho Scale"); + orthoSize.CloseMenuOnClick = false; + var orthoSizeValue = new FloatValueBox(_orthoSize, xLocationForExtras, 2, 70.0f, 0.001f, 100000.0f, 0.01f) + { + Parent = orthoSize + }; + + orthoSizeValue.ValueChanged += () => OnOrthographicSizeChanged(orthoSizeValue); + cameraCM.VisibleChanged += control => + { + orthoSize.Visible = _isOrtho; + orthoSizeValue.Value = _orthoSize; + }; + } + + // Near Plane + { + var nearPlane = cameraCM.AddButton("Near Plane"); + nearPlane.CloseMenuOnClick = false; + var nearPlaneValue = new FloatValueBox(_nearPlane, xLocationForExtras, 2, 70.0f, 0.001f, 1000.0f) + { + Parent = nearPlane + }; + + nearPlaneValue.ValueChanged += () => OnNearPlaneChanged(nearPlaneValue); + cameraCM.VisibleChanged += control => nearPlaneValue.Value = _nearPlane; + } + + // Far Plane + { + var farPlane = cameraCM.AddButton("Far Plane"); + farPlane.CloseMenuOnClick = false; + var farPlaneValue = new FloatValueBox(_farPlane, xLocationForExtras, 2, 70.0f, 10.0f) + { + Parent = farPlane + }; + + farPlaneValue.ValueChanged += () => OnFarPlaneChanged(farPlaneValue); + cameraCM.VisibleChanged += control => farPlaneValue.Value = _farPlane; + } + + cameraCM.AddSeparator(); + + // Reset Button + { + var reset = cameraCM.AddButton("Reset to default"); + reset.ButtonClicked += button => + { + SetupViewportOptions(); + + // if the context menu is opened without triggering the value changes beforehand, + // the movement speed will not be correctly reset to its default value in certain cases + // therefore, a UI update needs to be triggered here + minCamSpeedValue.Value = _minMovementSpeed; + camSpeedValue.Value = _movementSpeed; + maxCamSpeedValue.Value = _maxMovementSpeed; + }; + } + + #endregion Camera settings widget + + #region View mode widget + + largestText = "Brightness"; + textSize = Style.Current.FontMedium.MeasureText(largestText); + xLocationForExtras = textSize.X + 5; - // View mode widget var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft); ViewWidgetButtonMenu = new ContextMenu(); var viewModeButton = new ViewportWidgetButton("View", SpriteHandle.Invalid, ViewWidgetButtonMenu) @@ -484,8 +820,8 @@ namespace FlaxEditor.Viewport // Show FPS { InitFpsCounter(); - _showFpsButon = ViewWidgetShowMenu.AddButton("FPS Counter", () => ShowFpsCounter = !ShowFpsCounter); - _showFpsButon.CloseMenuOnClick = false; + _showFpsButton = ViewWidgetShowMenu.AddButton("FPS Counter", () => ShowFpsCounter = !ShowFpsCounter); + _showFpsButton.CloseMenuOnClick = false; } } @@ -610,104 +946,6 @@ namespace FlaxEditor.Viewport ViewWidgetButtonMenu.AddSeparator(); - // Orthographic - { - var ortho = ViewWidgetButtonMenu.AddButton("Orthographic"); - ortho.CloseMenuOnClick = false; - var orthoValue = new CheckBox(xLocationForExtras, 2, _isOrtho) - { - Parent = ortho - }; - orthoValue.StateChanged += checkBox => - { - if (checkBox.Checked != _isOrtho) - { - _isOrtho = checkBox.Checked; - ViewWidgetButtonMenu.Hide(); - if (_isOrtho) - { - var orient = ViewOrientation; - OrientViewport(ref orient); - } - } - }; - ViewWidgetButtonMenu.VisibleChanged += control => orthoValue.Checked = _isOrtho; - } - - // Camera Viewpoints - { - var cameraView = ViewWidgetButtonMenu.AddChildMenu("Viewpoints").ContextMenu; - for (int i = 0; i < EditorViewportCameraViewpointValues.Length; i++) - { - var co = EditorViewportCameraViewpointValues[i]; - var button = cameraView.AddButton(co.Name); - button.Tag = co.Orientation; - } - cameraView.ButtonClicked += button => - { - var orient = Quaternion.Euler((Float3)button.Tag); - OrientViewport(ref orient); - }; - } - - // Field of View - { - var fov = ViewWidgetButtonMenu.AddButton("Field Of View"); - fov.CloseMenuOnClick = false; - var fovValue = new FloatValueBox(1, xLocationForExtras, 2, 70.0f, 35.0f, 160.0f, 0.1f) - { - Parent = fov - }; - - fovValue.ValueChanged += () => _fieldOfView = fovValue.Value; - ViewWidgetButtonMenu.VisibleChanged += control => - { - fov.Visible = !_isOrtho; - fovValue.Value = _fieldOfView; - }; - } - - // Ortho Scale - { - var orthoSize = ViewWidgetButtonMenu.AddButton("Ortho Scale"); - orthoSize.CloseMenuOnClick = false; - var orthoSizeValue = new FloatValueBox(_orthoSize, xLocationForExtras, 2, 70.0f, 0.001f, 100000.0f, 0.01f) - { - Parent = orthoSize - }; - - orthoSizeValue.ValueChanged += () => _orthoSize = orthoSizeValue.Value; - ViewWidgetButtonMenu.VisibleChanged += control => - { - orthoSize.Visible = _isOrtho; - orthoSizeValue.Value = _orthoSize; - }; - } - - // Near Plane - { - var nearPlane = ViewWidgetButtonMenu.AddButton("Near Plane"); - nearPlane.CloseMenuOnClick = false; - var nearPlaneValue = new FloatValueBox(2.0f, xLocationForExtras, 2, 70.0f, 0.001f, 1000.0f) - { - Parent = nearPlane - }; - nearPlaneValue.ValueChanged += () => _nearPlane = nearPlaneValue.Value; - ViewWidgetButtonMenu.VisibleChanged += control => nearPlaneValue.Value = _nearPlane; - } - - // Far Plane - { - var farPlane = ViewWidgetButtonMenu.AddButton("Far Plane"); - farPlane.CloseMenuOnClick = false; - var farPlaneValue = new FloatValueBox(1000, xLocationForExtras, 2, 70.0f, 10.0f) - { - Parent = farPlane - }; - farPlaneValue.ValueChanged += () => _farPlane = farPlaneValue.Value; - ViewWidgetButtonMenu.VisibleChanged += control => farPlaneValue.Value = _farPlane; - } - // Brightness { var brightness = ViewWidgetButtonMenu.AddButton("Brightness"); @@ -732,24 +970,7 @@ namespace FlaxEditor.Viewport ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale; } - // Invert Panning - { - var invert = ViewWidgetButtonMenu.AddButton("Invert Panning"); - invert.CloseMenuOnClick = false; - var invertValue = new CheckBox(xLocationForExtras, 2, _invertPanning) - { - Parent = invert - }; - - invertValue.StateChanged += checkBox => - { - if (checkBox.Checked != _invertPanning) - { - _invertPanning = checkBox.Checked; - } - }; - ViewWidgetButtonMenu.VisibleChanged += control => invertValue.Checked = _invertPanning; - } + #endregion View mode widget } InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Top").Orientation))); @@ -766,6 +987,135 @@ namespace FlaxEditor.Viewport task.Begin += OnRenderBegin; } + /// + /// Sets the viewport options to the default values. + /// + private void SetupViewportOptions() + { + var options = Editor.Instance.Options.Options; + _minMovementSpeed = options.Viewport.MinMovementSpeed; + MovementSpeed = options.Viewport.MovementSpeed; + _maxMovementSpeed = options.Viewport.MaxMovementSpeed; + _useCameraEasing = options.Viewport.UseCameraEasing; + _panningSpeed = options.Viewport.PanningSpeed; + _invertPanning = options.Viewport.InvertPanning; + _relativePanning = options.Viewport.UseRelativePanning; + + _isOrtho = options.Viewport.UseOrthographicProjection; + _orthoSize = options.Viewport.OrthographicScale; + _fieldOfView = options.Viewport.FieldOfView; + _nearPlane = options.Viewport.NearPlane; + _farPlane = options.Viewport.FarPlane; + + OnEditorOptionsChanged(options); + } + + private void OnMovementSpeedChanged(FloatValueBox control) + { + var value = Mathf.Clamp(control.Value, _minMovementSpeed, _maxMovementSpeed); + MovementSpeed = value; + + OnCameraMovementProgressChanged(); + _editor.ProjectCache.SetCustomData("CameraMovementSpeedValue", _movementSpeed.ToString()); + } + + private void OnMinMovementSpeedChanged(FloatValueBox control) + { + var value = Mathf.Clamp(control.Value, 0.05f, _maxMovementSpeed); + _minMovementSpeed = value; + + if (_movementSpeed < value) + MovementSpeed = value; + + OnCameraMovementProgressChanged(); + _editor.ProjectCache.SetCustomData("CameraMinMovementSpeedValue", _minMovementSpeed.ToString()); + } + + private void OnMaxMovementSpeedChanged(FloatValueBox control) + { + var value = Mathf.Clamp(control.Value, _minMovementSpeed, 1000.0f); + _maxMovementSpeed = value; + + if (_movementSpeed > value) + MovementSpeed = value; + + OnCameraMovementProgressChanged(); + _editor.ProjectCache.SetCustomData("CameraMaxMovementSpeedValue", _maxMovementSpeed.ToString()); + } + + private void OnCameraEasingToggled(Control control) + { + _useCameraEasing = !_useCameraEasing; + + OnCameraMovementProgressChanged(); + _editor.ProjectCache.SetCustomData("UseCameraEasingState", _useCameraEasing.ToString()); + } + + private void OnPanningSpeedChanged(FloatValueBox control) + { + _panningSpeed = control.Value; + _editor.ProjectCache.SetCustomData("CameraPanningSpeedValue", _panningSpeed.ToString()); + } + + private void OnRelativePanningToggled(Control control) + { + _relativePanning = !_relativePanning; + _editor.ProjectCache.SetCustomData("CameraRelativePanningState", _relativePanning.ToString()); + } + + private void OnInvertPanningToggled(Control control) + { + _invertPanning = !_invertPanning; + _editor.ProjectCache.SetCustomData("CameraInvertPanningState", _invertPanning.ToString()); + } + + + private void OnViewpointChanged(ContextMenuButton button) + { + var orient = Quaternion.Euler((Float3)button.Tag); + OrientViewport(ref orient); + } + + private void OnFieldOfViewChanged(FloatValueBox control) + { + _fieldOfView = control.Value; + _editor.ProjectCache.SetCustomData("CameraFieldOfViewValue", _fieldOfView.ToString()); + } + + private void OnOrthographicModeToggled(Control control) + { + _isOrtho = !_isOrtho; + + if (_orthographicModeButton != null) + _orthographicModeButton.Checked = !_isOrtho; + + if (_isOrtho) + { + var orient = ViewOrientation; + OrientViewport(ref orient); + } + + _editor.ProjectCache.SetCustomData("CameraOrthographicState", _isOrtho.ToString()); + } + + private void OnOrthographicSizeChanged(FloatValueBox control) + { + _orthoSize = control.Value; + _editor.ProjectCache.SetCustomData("CameraOrthographicSizeValue", _orthoSize.ToString()); + } + + private void OnNearPlaneChanged(FloatValueBox control) + { + _nearPlane = control.Value; + _editor.ProjectCache.SetCustomData("CameraNearPlaneValue", _nearPlane.ToString()); + } + + private void OnFarPlaneChanged(FloatValueBox control) + { + _farPlane = control.Value; + _editor.ProjectCache.SetCustomData("CameraNearPlaneValue", _farPlane.ToString()); + } + /// /// Gets a value indicating whether this viewport is using mouse currently (eg. user moving objects). /// @@ -798,33 +1148,59 @@ namespace FlaxEditor.Viewport } } + private void OnCameraMovementProgressChanged() + { + // prevent NaN + if (Math.Abs(_minMovementSpeed - _maxMovementSpeed) < Mathf.Epsilon) + { + _speedStep = 0; + return; + } + + if (Math.Abs(_movementSpeed - _maxMovementSpeed) < Mathf.Epsilon) + { + _speedStep = _maxSpeedSteps; + return; + } + else if (Math.Abs(_movementSpeed - _minMovementSpeed) < Mathf.Epsilon) + { + _speedStep = 0; + return; + } + + // calculate current linear/eased progress + var progress = Mathf.Remap(_movementSpeed, _minMovementSpeed, _maxMovementSpeed, 0.0f, 1.0f); + + if (_useCameraEasing) + progress = Mathf.Pow(progress, 1.0f / _cameraEasingDegree); + + _speedStep = Mathf.RoundToInt(progress * _maxSpeedSteps); + } + /// /// Increases or decreases the camera movement speed. /// /// The stepping direction for speed adjustment. protected void AdjustCameraMoveSpeed(int step) { - int camValueIndex = -1; - for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++) - { - if (Mathf.NearEqual(EditorViewportCameraSpeedValues[i], _movementSpeed)) - { - camValueIndex = i; - break; - } - } - if (camValueIndex == -1) - return; + _speedStep = Mathf.Clamp(_speedStep + step, 0, _maxSpeedSteps); - if (step > 0) - MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Min(camValueIndex + 1, EditorViewportCameraSpeedValues.Length - 1)]; - else if (step < 0) - MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Max(camValueIndex - 1, 0)]; + // calculate new linear/eased progress + var progress = _useCameraEasing + ? Mathf.Pow((float)_speedStep / _maxSpeedSteps, _cameraEasingDegree) + : (float)_speedStep / _maxSpeedSteps; + + var speed = Mathf.Lerp(_minMovementSpeed, _maxMovementSpeed, progress); + MovementSpeed = (float)Math.Round(speed, 3); + _editor.ProjectCache.SetCustomData("CameraMovementSpeedValue", _movementSpeed.ToString()); } private void OnEditorOptionsChanged(EditorOptions options) { _mouseSensitivity = options.Viewport.MouseSensitivity; + _maxSpeedSteps = options.Viewport.TotalCameraSpeedSteps; + _cameraEasingDegree = options.Viewport.CameraEasingDegree; + OnCameraMovementProgressChanged(); } private void OnRenderBegin(RenderTask task, GPUContext context) @@ -863,7 +1239,7 @@ namespace FlaxEditor.Viewport } private FpsCounter _fpsCounter; - private ContextMenuButton _showFpsButon; + private ContextMenuButton _showFpsButton; /// /// Gets or sets a value indicating whether show or hide FPS counter. @@ -875,7 +1251,7 @@ namespace FlaxEditor.Viewport { _fpsCounter.Visible = value; _fpsCounter.Enabled = value; - _showFpsButon.Icon = value ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + _showFpsButton.Icon = value ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } @@ -1016,8 +1392,6 @@ namespace FlaxEditor.Viewport /// The parent window. protected virtual void OnControlMouseBegin(Window win) { - _wheelMovementChangeDeltaSum = 0; - // Hide cursor and start tracking mouse movement win.StartTrackingMouse(false); win.Cursor = CursorType.Hidden; @@ -1113,8 +1487,8 @@ namespace FlaxEditor.Viewport _camera.Update(deltaTime); useMovementSpeed = _camera.UseMovementSpeed; - if (_speedWidget != null) - _speedWidget.Parent.Visible = useMovementSpeed; + if (_cameraButton != null) + _cameraButton.Parent.Visible = useMovementSpeed; } // Get parent window @@ -1217,18 +1591,8 @@ namespace FlaxEditor.Viewport rmbWheel = useMovementSpeed && (_input.IsMouseRightDown || _isVirtualMouseRightDown) && wheelInUse; if (rmbWheel) { - const float step = 4.0f; - _wheelMovementChangeDeltaSum += _input.MouseWheelDelta * options.Viewport.MouseWheelSensitivity; - if (_wheelMovementChangeDeltaSum >= step) - { - _wheelMovementChangeDeltaSum -= step; - AdjustCameraMoveSpeed(1); - } - else if (_wheelMovementChangeDeltaSum <= -step) - { - _wheelMovementChangeDeltaSum += step; - AdjustCameraMoveSpeed(-1); - } + var step = _input.MouseWheelDelta * options.Viewport.MouseWheelSensitivity; + AdjustCameraMoveSpeed(step > 0.0f ? 1 : -1); } } @@ -1497,22 +1861,6 @@ namespace FlaxEditor.Viewport new CameraViewpoint("Bottom", new Float3(-90, 0, 0)) }; - private readonly float[] EditorViewportCameraSpeedValues = - { - 0.05f, - 0.1f, - 0.25f, - 0.5f, - 1.0f, - 2.0f, - 4.0f, - 6.0f, - 8.0f, - 16.0f, - 32.0f, - 64.0f, - }; - private struct ViewModeOptions { public readonly string Name; @@ -1568,24 +1916,6 @@ namespace FlaxEditor.Viewport new ViewModeOptions(ViewMode.GlobalIllumination, "Global Illumination"), }; - private void WidgetCamSpeedShowHide(Control cm) - { - if (cm.Visible == false) - return; - - var ccm = (ContextMenu)cm; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(MovementSpeed - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; - } - } - } - private void WidgetViewModeShowHideClicked(ContextMenuButton button) { if (button.Tag is ViewMode v) diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index 481bc3f1b..5ff5cdb6d 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -19,6 +19,7 @@ namespace FlaxEditor.Viewport.Widgets private bool _checked; private bool _autoCheck; private bool _isMosueDown; + private float _forcedTextWidth; /// /// Event fired when user toggles checked state. @@ -63,14 +64,16 @@ namespace FlaxEditor.Viewport.Widgets /// The text. /// The icon. /// The context menu. - /// if set to true will be automatic checked on mouse click. - public ViewportWidgetButton(string text, SpriteHandle icon, ContextMenu contextMenu = null, bool autoCheck = false) - : base(0, 0, CalculateButtonWidth(0, icon.IsValid), ViewportWidgetsContainer.WidgetsHeight) + /// If set to true will be automatic checked on mouse click. + /// Forces the text to be drawn with the specified width. + public ViewportWidgetButton(string text, SpriteHandle icon, ContextMenu contextMenu = null, bool autoCheck = false, float textWidth = 0.0f) + : base(0, 0, CalculateButtonWidth(textWidth, icon.IsValid), ViewportWidgetsContainer.WidgetsHeight) { _text = text; Icon = icon; _cm = contextMenu; _autoCheck = autoCheck; + _forcedTextWidth = textWidth; if (_cm != null) _cm.VisibleChanged += CmOnVisibleChanged; @@ -160,7 +163,7 @@ namespace FlaxEditor.Viewport.Widgets var style = Style.Current; if (style != null && style.FontMedium) - Width = CalculateButtonWidth(style.FontMedium.MeasureText(_text).X, Icon.IsValid); + Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid); } } } diff --git a/Source/Engine/Core/Math/TypeConverters/ColorConverter.cs b/Source/Engine/Core/Math/TypeConverters/ColorConverter.cs index afd34bbfd..65377acaa 100644 --- a/Source/Engine/Core/Math/TypeConverters/ColorConverter.cs +++ b/Source/Engine/Core/Math/TypeConverters/ColorConverter.cs @@ -13,9 +13,7 @@ namespace FlaxEngine.TypeConverters public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) - { return true; - } return base.CanConvertFrom(context, sourceType); } @@ -23,9 +21,7 @@ namespace FlaxEngine.TypeConverters public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (destinationType == typeof(string)) - { return false; - } return base.CanConvertTo(context, destinationType); } diff --git a/Source/Engine/Core/Math/TypeConverters/Double2Converter.cs b/Source/Engine/Core/Math/TypeConverters/Double2Converter.cs index 4eebbfce4..e0670df05 100644 --- a/Source/Engine/Core/Math/TypeConverters/Double2Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Double2Converter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Double2Converter : TypeConverter + internal class Double2Converter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Double2(double.Parse(v[0], culture), double.Parse(v[1], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/Double3Converter.cs b/Source/Engine/Core/Math/TypeConverters/Double3Converter.cs index 420e0016c..a66892ecb 100644 --- a/Source/Engine/Core/Math/TypeConverters/Double3Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Double3Converter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Double3Converter : TypeConverter + internal class Double3Converter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Double3(double.Parse(v[0], culture), double.Parse(v[1], culture), double.Parse(v[2], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/Double4Converter.cs b/Source/Engine/Core/Math/TypeConverters/Double4Converter.cs index fc1d9a7fe..d085217ef 100644 --- a/Source/Engine/Core/Math/TypeConverters/Double4Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Double4Converter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Double4Converter : TypeConverter + internal class Double4Converter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Double4(double.Parse(v[0], culture), double.Parse(v[1], culture), double.Parse(v[2], culture), double.Parse(v[3], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/Float2Converter.cs b/Source/Engine/Core/Math/TypeConverters/Float2Converter.cs index a41a0f4d5..4b2ffadf5 100644 --- a/Source/Engine/Core/Math/TypeConverters/Float2Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Float2Converter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Float2Converter : TypeConverter + internal class Float2Converter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Float2(float.Parse(v[0], culture), float.Parse(v[1], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/Float3Converter.cs b/Source/Engine/Core/Math/TypeConverters/Float3Converter.cs index aded4117e..3739c44ef 100644 --- a/Source/Engine/Core/Math/TypeConverters/Float3Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Float3Converter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Float3Converter : TypeConverter + internal class Float3Converter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Float3(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/Float4Converter.cs b/Source/Engine/Core/Math/TypeConverters/Float4Converter.cs index 58c76ac65..620f2c838 100644 --- a/Source/Engine/Core/Math/TypeConverters/Float4Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Float4Converter.cs @@ -7,15 +7,13 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Float4Converter : TypeConverter + internal class VectorConverter : TypeConverter { /// public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) - { return true; - } return base.CanConvertFrom(context, sourceType); } @@ -23,18 +21,32 @@ namespace FlaxEngine.TypeConverters public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (destinationType == typeof(string)) - { return false; - } return base.CanConvertTo(context, destinationType); } + internal static string[] GetParts(string str) + { + string[] v = str.Split(','); + if (v.Length == 1) + { + // When converting from ToString() + v = str.Split(' '); + for (int i = 0; i < v.Length; i++) + v[i] = v[i].Substring(v[i].IndexOf(':') + 1); + } + return v; + } + } + + internal class Float4Converter : VectorConverter + { /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Float4(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/Int2Converter.cs b/Source/Engine/Core/Math/TypeConverters/Int2Converter.cs index c4989c085..f528aa46b 100644 --- a/Source/Engine/Core/Math/TypeConverters/Int2Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Int2Converter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Int2Converter : TypeConverter + internal class Int2Converter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Int2(int.Parse(v[0], culture), int.Parse(v[1], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/Int3Converter.cs b/Source/Engine/Core/Math/TypeConverters/Int3Converter.cs index fe01f91fd..520f806d0 100644 --- a/Source/Engine/Core/Math/TypeConverters/Int3Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Int3Converter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Int3Converter : TypeConverter + internal class Int3Converter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Int3(int.Parse(v[0], culture), int.Parse(v[1], culture), int.Parse(v[2], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/Int4Converter.cs b/Source/Engine/Core/Math/TypeConverters/Int4Converter.cs index 2ce0fc202..e9a27dfda 100644 --- a/Source/Engine/Core/Math/TypeConverters/Int4Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Int4Converter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Int4Converter : TypeConverter + internal class Int4Converter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Int4(int.Parse(v[0], culture), int.Parse(v[1], culture), int.Parse(v[2], culture), int.Parse(v[3], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/QuaternionConverter.cs b/Source/Engine/Core/Math/TypeConverters/QuaternionConverter.cs index 23bb901be..5d9aa206b 100644 --- a/Source/Engine/Core/Math/TypeConverters/QuaternionConverter.cs +++ b/Source/Engine/Core/Math/TypeConverters/QuaternionConverter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class QuaternionConverter : TypeConverter + internal class QuaternionConverter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Quaternion(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/Vector2Converter.cs b/Source/Engine/Core/Math/TypeConverters/Vector2Converter.cs index 96d6beadc..acb5b5817 100644 --- a/Source/Engine/Core/Math/TypeConverters/Vector2Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Vector2Converter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Vector2Converter : TypeConverter + internal class Vector2Converter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Vector2(float.Parse(v[0], culture), float.Parse(v[1], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/Vector3Converter.cs b/Source/Engine/Core/Math/TypeConverters/Vector3Converter.cs index 23ee4df11..66ec831f0 100644 --- a/Source/Engine/Core/Math/TypeConverters/Vector3Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Vector3Converter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Vector3Converter : TypeConverter + internal class Vector3Converter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Vector3(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/TypeConverters/Vector4Converter.cs b/Source/Engine/Core/Math/TypeConverters/Vector4Converter.cs index c3b4d074b..f4781f45b 100644 --- a/Source/Engine/Core/Math/TypeConverters/Vector4Converter.cs +++ b/Source/Engine/Core/Math/TypeConverters/Vector4Converter.cs @@ -7,34 +7,14 @@ using System.Globalization; namespace FlaxEngine.TypeConverters { - internal class Vector4Converter : TypeConverter + internal class Vector4Converter : VectorConverter { - /// - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType == typeof(string)) - { - return true; - } - return base.CanConvertFrom(context, sourceType); - } - - /// - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string)) - { - return false; - } - return base.CanConvertTo(context, destinationType); - } - /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string str) { - string[] v = str.Split(','); + string[] v = GetParts(str); return new Vector4(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture)); } return base.ConvertFrom(context, culture, value); diff --git a/Source/Engine/Core/Math/Vector2.h b/Source/Engine/Core/Math/Vector2.h index 83deb009a..cea03ec10 100644 --- a/Source/Engine/Core/Math/Vector2.h +++ b/Source/Engine/Core/Math/Vector2.h @@ -646,6 +646,12 @@ inline Vector2Base operator/(typename TOtherFloat::Type a, const Vector2Ba return Vector2Base(a) / b; } +template +inline uint32 GetHash(const Vector2Base& key) +{ + return (*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y; +} + namespace Math { template diff --git a/Source/Engine/Core/Math/Vector3.h b/Source/Engine/Core/Math/Vector3.h index c0ebdf01d..01b55e9bd 100644 --- a/Source/Engine/Core/Math/Vector3.h +++ b/Source/Engine/Core/Math/Vector3.h @@ -977,6 +977,12 @@ inline Vector3Base operator/(typename TOtherFloat::Type a, const Vector3Ba return Vector3Base(a) / b; } +template +inline uint32 GetHash(const Vector3Base& key) +{ + return (((*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y) * 397) ^ *(uint32*)&key.Z; +} + namespace Math { template diff --git a/Source/Engine/Core/Math/Vector4.h b/Source/Engine/Core/Math/Vector4.h index 5c7b24c4a..1cc6d4db8 100644 --- a/Source/Engine/Core/Math/Vector4.h +++ b/Source/Engine/Core/Math/Vector4.h @@ -552,6 +552,12 @@ inline Vector4Base operator/(typename TOtherFloat::Type a, const Vector4Ba return Vector4Base(a) / b; } +template +inline uint32 GetHash(const Vector4Base& key) +{ + return (((((*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y) * 397) ^ *(uint32*)&key.Z) * 397) ^*(uint32*)&key.W; +} + namespace Math { template diff --git a/Source/Engine/Core/Types/Version.cpp b/Source/Engine/Core/Types/Version.cpp index 4a11a0af6..c0410d1cb 100644 --- a/Source/Engine/Core/Types/Version.cpp +++ b/Source/Engine/Core/Types/Version.cpp @@ -7,15 +7,15 @@ Version::Version(int32 major, int32 minor, int32 build, int32 revision) { _major = Math::Max(major, 0); _minor = Math::Max(minor, 0); - _build = Math::Max(build, 0); - _revision = Math::Max(revision, 0); + _build = Math::Max(build, -1); + _revision = Math::Max(revision, -1); } Version::Version(int32 major, int32 minor, int32 build) { _major = Math::Max(major, 0); _minor = Math::Max(minor, 0); - _build = Math::Max(build, 0); + _build = Math::Max(build, -1); _revision = -1; } diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 2f74e421c..fea1fadda 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -1302,7 +1302,8 @@ namespace FlaxEngine.Interop #if !USE_AOT internal bool TryGetDelegate(out Invoker.MarshalAndInvokeDelegate outDeleg, out object outDelegInvoke) { - if (invokeDelegate == null) + // Skip using in-built delegate for value types (eg. Transform) to properly handle instance value passing to method + if (invokeDelegate == null && !method.DeclaringType.IsValueType) { List methodTypes = new List(); if (!method.IsStatic) diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index 34d8e7661..f0056e26c 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -313,6 +313,8 @@ bool GPUDevice::Init() _res->TasksManager.SetExecutor(CreateTasksExecutor()); LOG(Info, "Total graphics memory: {0}", Utilities::BytesToText(TotalGraphicsMemory)); + if (!Limits.HasCompute) + LOG(Warning, "Compute Shaders are not supported"); return false; } diff --git a/Source/Engine/Graphics/Shaders/GPUShader.cpp b/Source/Engine/Graphics/Shaders/GPUShader.cpp index e76ee9996..86d983fd8 100644 --- a/Source/Engine/Graphics/Shaders/GPUShader.cpp +++ b/Source/Engine/Graphics/Shaders/GPUShader.cpp @@ -87,7 +87,11 @@ bool GPUShader::Create(MemoryReadStream& stream) GPUShaderProgramInitializer initializer; #if !BUILD_RELEASE initializer.Owner = this; + const StringView name = GetName(); +#else + const StringView name; #endif + const bool hasCompute = GPUDevice::Instance->Limits.HasCompute; for (int32 i = 0; i < shadersCount; i++) { const ShaderStage type = static_cast(stream.ReadByte()); @@ -117,10 +121,15 @@ bool GPUShader::Create(MemoryReadStream& stream) stream.ReadBytes(&initializer.Bindings, sizeof(ShaderBindings)); // Create shader program + if (type == ShaderStage::Compute && !hasCompute) + { + LOG(Warning, "Failed to create {} Shader program '{}' ({}).", ::ToString(type), String(initializer.Name), name); + continue; + } GPUShaderProgram* shader = CreateGPUShaderProgram(type, initializer, cache, cacheSize, stream); if (shader == nullptr) { - LOG(Error, "Failed to create {} Shader program '{}'.", ::ToString(type), String(initializer.Name)); + LOG(Error, "Failed to create {} Shader program '{}' ({}).", ::ToString(type), String(initializer.Name), name); return true; } diff --git a/Source/Engine/Graphics/Shaders/GPUShaderProgram.h b/Source/Engine/Graphics/Shaders/GPUShaderProgram.h index 09c45506f..31f6638dc 100644 --- a/Source/Engine/Graphics/Shaders/GPUShaderProgram.h +++ b/Source/Engine/Graphics/Shaders/GPUShaderProgram.h @@ -122,6 +122,19 @@ public: /// class GPUShaderProgramVS : public GPUShaderProgram { +public: + // Input element run-time data (see VertexShaderMeta::InputElement for compile-time data) + PACK_STRUCT(struct InputElement + { + byte Type; // VertexShaderMeta::InputType + byte Index; + byte Format; // PixelFormat + byte InputSlot; + uint32 AlignedByteOffset; // Fixed value or INPUT_LAYOUT_ELEMENT_ALIGN if auto + byte InputSlotClass; // INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA or INPUT_LAYOUT_ELEMENT_PER_INSTANCE_DATA + uint32 InstanceDataStepRate; // 0 if per-vertex + }); + public: /// /// Gets input layout description handle (platform dependent). diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp index b58684a4e..52192c634 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp @@ -15,32 +15,21 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const { case ShaderStage::Vertex: { - D3D11_INPUT_ELEMENT_DESC inputLayoutDesc[VERTEX_SHADER_MAX_INPUT_ELEMENTS]; - - // Temporary variables - byte Type, Format, Index, InputSlot, InputSlotClass; - uint32 AlignedByteOffset, InstanceDataStepRate; - - // Load Input Layout (it may be empty) + // Load Input Layout byte inputLayoutSize; stream.ReadByte(&inputLayoutSize); ASSERT(inputLayoutSize <= VERTEX_SHADER_MAX_INPUT_ELEMENTS); + D3D11_INPUT_ELEMENT_DESC inputLayoutDesc[VERTEX_SHADER_MAX_INPUT_ELEMENTS]; for (int32 a = 0; a < inputLayoutSize; a++) { // Read description - // TODO: maybe use struct and load at once? - stream.ReadByte(&Type); - stream.ReadByte(&Index); - stream.ReadByte(&Format); - stream.ReadByte(&InputSlot); - stream.ReadUint32(&AlignedByteOffset); - stream.ReadByte(&InputSlotClass); - stream.ReadUint32(&InstanceDataStepRate); + GPUShaderProgramVS::InputElement inputElement; + stream.Read(inputElement); // Get semantic name const char* semanticName = nullptr; // TODO: maybe use enum+mapping ? - switch (Type) + switch (inputElement.Type) { case 1: semanticName = "POSITION"; @@ -70,7 +59,7 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const semanticName = "BLENDWEIGHT"; break; default: - LOG(Fatal, "Invalid vertex shader element semantic type: {0}", Type); + LOG(Fatal, "Invalid vertex shader element semantic type: {0}", inputElement.Type); break; } @@ -78,12 +67,12 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const inputLayoutDesc[a] = { semanticName, - static_cast(Index), - static_cast(Format), - static_cast(InputSlot), - static_cast(AlignedByteOffset), - static_cast(InputSlotClass), - static_cast(InstanceDataStepRate) + static_cast(inputElement.Index), + static_cast(inputElement.Format), + static_cast(inputElement.InputSlot), + static_cast(inputElement.AlignedByteOffset), + static_cast(inputElement.InputSlotClass), + static_cast(inputElement.InstanceDataStepRate) }; } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.cpp index e1e716853..07352b674 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.cpp @@ -20,32 +20,21 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const { case ShaderStage::Vertex: { - D3D12_INPUT_ELEMENT_DESC inputLayout[VERTEX_SHADER_MAX_INPUT_ELEMENTS]; - - // Temporary variables - byte Type, Format, Index, InputSlot, InputSlotClass; - uint32 AlignedByteOffset, InstanceDataStepRate; - // Load Input Layout (it may be empty) byte inputLayoutSize; stream.ReadByte(&inputLayoutSize); ASSERT(inputLayoutSize <= VERTEX_SHADER_MAX_INPUT_ELEMENTS); + D3D12_INPUT_ELEMENT_DESC inputLayout[VERTEX_SHADER_MAX_INPUT_ELEMENTS]; for (int32 a = 0; a < inputLayoutSize; a++) { // Read description - // TODO: maybe use struct and load at once? - stream.ReadByte(&Type); - stream.ReadByte(&Index); - stream.ReadByte(&Format); - stream.ReadByte(&InputSlot); - stream.ReadUint32(&AlignedByteOffset); - stream.ReadByte(&InputSlotClass); - stream.ReadUint32(&InstanceDataStepRate); + GPUShaderProgramVS::InputElement inputElement; + stream.Read(inputElement); // Get semantic name const char* semanticName = nullptr; // TODO: maybe use enum+mapping ? - switch (Type) + switch (inputElement.Type) { case 1: semanticName = "POSITION"; @@ -75,7 +64,7 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const semanticName = "BLENDWEIGHT"; break; default: - LOG(Fatal, "Invalid vertex shader element semantic type: {0}", Type); + LOG(Fatal, "Invalid vertex shader element semantic type: {0}", inputElement.Type); break; } @@ -83,12 +72,12 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const inputLayout[a] = { semanticName, - static_cast(Index), - static_cast(Format), - static_cast(InputSlot), - static_cast(AlignedByteOffset), - static_cast(InputSlotClass), - static_cast(InstanceDataStepRate) + static_cast(inputElement.Index), + static_cast(inputElement.Format), + static_cast(inputElement.InputSlot), + static_cast(inputElement.AlignedByteOffset), + static_cast(inputElement.InputSlotClass), + static_cast(inputElement.InstanceDataStepRate) }; } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp index 5e583d713..852ce5bad 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp @@ -14,9 +14,9 @@ #include "Engine/Graphics/PixelFormatExtensions.h" #if PLATFORM_DESKTOP -#define VULKAN_UNIFORM_RING_BUFFER_SIZE 24 * 1024 * 1024 +#define VULKAN_UNIFORM_RING_BUFFER_SIZE (24 * 1024 * 1024) #else -#define VULKAN_UNIFORM_RING_BUFFER_SIZE 8 * 1024 * 1024 +#define VULKAN_UNIFORM_RING_BUFFER_SIZE (8 * 1024 * 1024) #endif UniformBufferUploaderVulkan::UniformBufferUploaderVulkan(GPUDeviceVulkan* device) @@ -153,10 +153,6 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons vertexBindingDescriptions[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; } - // Temporary variables - byte Type, Format, Index, InputSlot, InputSlotClass; - uint32 AlignedByteOffset, InstanceDataStepRate; - // Load Input Layout (it may be empty) byte inputLayoutSize; stream.ReadByte(&inputLayoutSize); @@ -167,32 +163,26 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons for (int32 a = 0; a < inputLayoutSize; a++) { // Read description - // TODO: maybe use struct and load at once? - stream.ReadByte(&Type); - stream.ReadByte(&Index); - stream.ReadByte(&Format); - stream.ReadByte(&InputSlot); - stream.ReadUint32(&AlignedByteOffset); - stream.ReadByte(&InputSlotClass); - stream.ReadUint32(&InstanceDataStepRate); + GPUShaderProgramVS::InputElement inputElement; + stream.Read(inputElement); - const auto size = PixelFormatExtensions::SizeInBytes((PixelFormat)Format); - if (AlignedByteOffset != INPUT_LAYOUT_ELEMENT_ALIGN) - offset = AlignedByteOffset; + const auto size = PixelFormatExtensions::SizeInBytes((PixelFormat)inputElement.Format); + if (inputElement.AlignedByteOffset != INPUT_LAYOUT_ELEMENT_ALIGN) + offset = inputElement.AlignedByteOffset; - auto& vertexBindingDescription = vertexBindingDescriptions[InputSlot]; - vertexBindingDescription.binding = InputSlot; + auto& vertexBindingDescription = vertexBindingDescriptions[inputElement.InputSlot]; + vertexBindingDescription.binding = inputElement.InputSlot; vertexBindingDescription.stride = Math::Max(vertexBindingDescription.stride, (uint32_t)(offset + size)); - vertexBindingDescription.inputRate = InputSlotClass == INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE; - ASSERT(InstanceDataStepRate == 0 || InstanceDataStepRate == 1); + vertexBindingDescription.inputRate = inputElement.InputSlotClass == INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE; + ASSERT(inputElement.InstanceDataStepRate == 0 || inputElement.InstanceDataStepRate == 1); auto& vertexAttributeDescription = vertexAttributeDescriptions[a]; vertexAttributeDescription.location = a; - vertexAttributeDescription.binding = InputSlot; - vertexAttributeDescription.format = RenderToolsVulkan::ToVulkanFormat((PixelFormat)Format); + vertexAttributeDescription.binding = inputElement.InputSlot; + vertexAttributeDescription.format = RenderToolsVulkan::ToVulkanFormat((PixelFormat)inputElement.Format); vertexAttributeDescription.offset = offset; - bindingsCount = Math::Max(bindingsCount, (uint32)InputSlot + 1); + bindingsCount = Math::Max(bindingsCount, (uint32)inputElement.InputSlot + 1); offset += size; } diff --git a/Source/Engine/Level/Prefabs/PrefabManager.cpp b/Source/Engine/Level/Prefabs/PrefabManager.cpp index cd9d893ec..de164343b 100644 --- a/Source/Engine/Level/Prefabs/PrefabManager.cpp +++ b/Source/Engine/Level/Prefabs/PrefabManager.cpp @@ -39,7 +39,7 @@ PrefabManagerService PrefabManagerServiceInstance; Actor* PrefabManager::SpawnPrefab(Prefab* prefab) { Actor* parent = Level::Scenes.Count() != 0 ? Level::Scenes.Get()[0] : nullptr; - return SpawnPrefab(prefab, Transform::Identity, parent, nullptr); + return SpawnPrefab(prefab, Transform(Vector3::Minimum), parent, nullptr); } Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Vector3& position) @@ -73,12 +73,12 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, const Transform Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent) { - return SpawnPrefab(prefab, Transform::Identity, parent, nullptr); + return SpawnPrefab(prefab, Transform(Vector3::Minimum), parent, nullptr); } Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary* objectsCache, bool withSynchronization) { - return SpawnPrefab(prefab, Transform::Identity, parent, objectsCache, withSynchronization); + return SpawnPrefab(prefab, Transform(Vector3::Minimum), parent, objectsCache, withSynchronization); } Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Actor* parent, Dictionary* objectsCache, bool withSynchronization) @@ -191,7 +191,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac parent->Children.Add(root); // Move root to the right location - if (transform != Transform::Identity) + if (transform.Translation != Vector3::Minimum) root->SetTransform(transform); // Link actors hierarchy diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 52845a0f8..784f28c98 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -1334,7 +1334,8 @@ void ParticlesSystem::Job(int32 index) auto emitter = particleSystem->Emitters[track.AsEmitter.Index].Get(); auto& data = instance.Emitters[track.AsEmitter.Index]; ASSERT(emitter && emitter->IsLoaded()); - ASSERT(emitter->Capacity != 0 && emitter->Graph.Layout.Size != 0); + if (emitter->Capacity == 0 || emitter->Graph.Layout.Size == 0) + continue; PROFILE_CPU_ASSET(emitter); // Calculate new time position diff --git a/Source/Engine/Physics/Actors/PhysicsColliderActor.cpp b/Source/Engine/Physics/Actors/PhysicsColliderActor.cpp index 02645943c..f2ab5d8a2 100644 --- a/Source/Engine/Physics/Actors/PhysicsColliderActor.cpp +++ b/Source/Engine/Physics/Actors/PhysicsColliderActor.cpp @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #include "PhysicsColliderActor.h" -#include "Engine/Scripting/Script.h" #include "RigidBody.h" PhysicsColliderActor::PhysicsColliderActor(const SpawnParams& params) diff --git a/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp b/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp index 684aeca19..5968bdd78 100644 --- a/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp +++ b/Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp @@ -117,9 +117,7 @@ void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, c { // Skip sending events to removed actors if (pairHeader.flags & (PxContactPairHeaderFlag::eREMOVED_ACTOR_0 | PxContactPairHeaderFlag::eREMOVED_ACTOR_1)) - { return; - } Collision c; PxContactPairExtraDataIterator j(pairHeader.extraDataStream, pairHeader.extraDataStreamSize); @@ -193,7 +191,7 @@ void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, c RemovedCollisions.Add(c); } } - ASSERT(!j.nextItemSet()); + //ASSERT(!j.nextItemSet()); } void SimulationEventCallback::onTrigger(PxTriggerPair* pairs, PxU32 count) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index c5bc44ab9..474a92072 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -451,6 +451,7 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri default: break; } + flags |= MB_TASKMODAL; // Show dialog int result = MessageBoxW(parent ? static_cast(parent->GetNativePtr()) : nullptr, String(text).GetText(), String(caption).GetText(), flags); diff --git a/Source/Engine/Renderer/DepthOfFieldPass.cpp b/Source/Engine/Renderer/DepthOfFieldPass.cpp index cd7930f5c..707a441e6 100644 --- a/Source/Engine/Renderer/DepthOfFieldPass.cpp +++ b/Source/Engine/Renderer/DepthOfFieldPass.cpp @@ -204,7 +204,7 @@ void DepthOfFieldPass::Render(RenderContext& renderContext, GPUTexture*& frame, { DepthOfFieldSettings& dofSettings = renderContext.List->Settings.DepthOfField; const bool useDoF = EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::DepthOfField) && dofSettings.Enabled; - if (!useDoF || _platformSupportsDoF || checkIfSkipPass()) + if (!useDoF || !_platformSupportsDoF || checkIfSkipPass()) return; auto device = GPUDevice::Instance; auto context = device->GetMainContext(); diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index efd147917..679ead4e3 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -1270,7 +1270,11 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp // Invoke the method MObject* exception = nullptr; +#if USE_NETCORE // NetCore uses the same path for both virtual and non-virtual calls + MObject* resultObject = mMethod->Invoke(mInstance, params, &exception); +#else MObject* resultObject = withInterfaces ? mMethod->InvokeVirtual((MObject*)mInstance, params, &exception) : mMethod->Invoke(mInstance, params, &exception); +#endif if (exception) { MException ex(exception); diff --git a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp index 291910b11..c0fda4d6e 100644 --- a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp +++ b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp @@ -460,16 +460,15 @@ bool ShaderCompiler::WriteCustomDataVS(ShaderCompilationContext* context, Shader auto& element = layout[a]; if (!layoutVisible[a]) continue; - - // TODO: serialize whole struct? - - output->WriteByte(static_cast(element.Type)); - output->WriteByte(element.Index); - output->WriteByte(static_cast(element.Format)); - output->WriteByte(element.InputSlot); - output->WriteUint32(element.AlignedByteOffset); - output->WriteByte(element.InputSlotClass); - output->WriteUint32(element.InstanceDataStepRate); + GPUShaderProgramVS::InputElement data; + data.Type = static_cast(element.Type); + data.Index = element.Index; + data.Format = static_cast(element.Format); + data.InputSlot = element.InputSlot; + data.AlignedByteOffset = element.AlignedByteOffset; + data.InputSlotClass = element.InputSlotClass; + data.InstanceDataStepRate = element.InstanceDataStepRate; + output->Write(data); } return false; diff --git a/Source/Platforms/iOS/Binaries/Project/FlaxGame.xcodeproj/project.pbxproj b/Source/Platforms/iOS/Binaries/Project/FlaxGame.xcodeproj/project.pbxproj index 1dd5d4e01..3b8889487 100644 --- a/Source/Platforms/iOS/Binaries/Project/FlaxGame.xcodeproj/project.pbxproj +++ b/Source/Platforms/iOS/Binaries/Project/FlaxGame.xcodeproj/project.pbxproj @@ -222,7 +222,7 @@ ${PBXResourcesGroup} GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ${HeaderSearchPaths}; + HEADER_SEARCH_PATHS = "${HeaderSearchPaths}"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -275,7 +275,7 @@ ${PBXResourcesGroup} GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ${HeaderSearchPaths}; + HEADER_SEARCH_PATHS = "${HeaderSearchPaths}"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; diff --git a/Source/ThirdParty/volk/volk.Build.cs b/Source/ThirdParty/volk/volk.Build.cs index 2f4cfa0d6..67f7593d9 100644 --- a/Source/ThirdParty/volk/volk.Build.cs +++ b/Source/ThirdParty/volk/volk.Build.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.Collections.Generic; using System.IO; using Flax.Build; using Flax.Build.NativeCpp; @@ -62,4 +63,12 @@ public class volk : ThirdPartyModule Log.ErrorOnce("Missing VulkanSDK.", ref _missingSDKError); } } + + /// + public override void GetFilesToDeploy(List files) + { + base.GetFilesToDeploy(files); + + files.Add(Path.Combine(FolderPath, "volk.h")); + } } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 90e96d075..10e4d5846 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -3135,14 +3135,17 @@ namespace Flax.Build.Bindings contents.AppendLine("#pragma once"); contents.AppendLine(); contents.AppendLine($"#define {binaryModuleNameUpper}_NAME \"{binaryModuleName}\""); - if (version.Build == -1) + if (version.Build <= 0) contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION Version({version.Major}, {version.Minor})"); - else + else if (version.Revision <= 0) contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION Version({version.Major}, {version.Minor}, {version.Build})"); + else + contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION Version({version.Major}, {version.Minor}, {version.Build}, {version.Revision})"); contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION_TEXT \"{version}\""); contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION_MAJOR {version.Major}"); contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION_MINOR {version.Minor}"); contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION_BUILD {version.Build}"); + contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION_REVISION {version.Revision}"); contents.AppendLine($"#define {binaryModuleNameUpper}_COMPANY \"{project.Company}\""); contents.AppendLine($"#define {binaryModuleNameUpper}_COPYRIGHT \"{project.Copyright}\""); contents.AppendLine(); diff --git a/Source/Tools/Flax.Build/ProjectInfo.cs b/Source/Tools/Flax.Build/ProjectInfo.cs index 7830f59c1..ec615fc49 100644 --- a/Source/Tools/Flax.Build/ProjectInfo.cs +++ b/Source/Tools/Flax.Build/ProjectInfo.cs @@ -14,9 +14,9 @@ namespace Flax.Build /// /// Writes the JSON representation of the object. /// - /// The to write to. + /// The to write to. /// The value. - /// The calling serializer. + /// The calling serializer. public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options) { writer.WriteStringValue(value.ToString()); @@ -25,73 +25,68 @@ namespace Flax.Build /// /// Reads the JSON representation of the object. /// - /// The to read from. - /// Type of the object. - /// The existing property value of the JSON that is being converted. - /// The calling serializer. + /// The to read from. + /// Type of the object. + /// The serializer options. /// The object value. public override Version? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) - { return null; - } - else + + if (reader.TokenType == JsonTokenType.StartObject) { - if (reader.TokenType == JsonTokenType.StartObject) + try { - try + reader.Read(); + var values = new Dictionary(); + while (reader.TokenType == JsonTokenType.PropertyName) { + var key = reader.GetString(); reader.Read(); - Dictionary values = new Dictionary(); - while (reader.TokenType == JsonTokenType.PropertyName) - { - var key = reader.GetString(); - reader.Read(); - var val = reader.GetInt32(); - reader.Read(); - values.Add(key, val); - } + var val = reader.GetInt32(); + reader.Read(); + values.Add(key, val); + } - int major = 0, minor = 0, build = 0; - values.TryGetValue("Major", out major); - values.TryGetValue("Minor", out minor); - values.TryGetValue("Build", out build); + values.TryGetValue("Major", out var major); + values.TryGetValue("Minor", out var minor); + if (!values.TryGetValue("Build", out var build)) + build = -1; + if (!values.TryGetValue("Revision", out var revision)) + revision = -1; - Version v = new Version(major, minor, build); - return v; - } - catch (Exception ex) - { - throw new Exception(String.Format("Error parsing version string: {0}", reader.GetString()), ex); - } + if (build <= 0) + return new Version(major, minor); + if (revision <= 0) + return new Version(major, minor, build); + return new Version(major, minor, build, revision); } - else if (reader.TokenType == JsonTokenType.String) + catch (Exception ex) { - try - { - Version v = new Version((string)reader.GetString()!); - return v; - } - catch (Exception ex) - { - throw new Exception(String.Format("Error parsing version string: {0}", reader.GetString()), ex); - } - } - else - { - throw new Exception(String.Format("Unexpected token or value when parsing version. Token: {0}, Value: {1}", reader.TokenType, reader.GetString())); + throw new Exception(string.Format("Error parsing version string: {0}", reader.GetString()), ex); } } + + if (reader.TokenType == JsonTokenType.String) + { + try + { + return new Version((string)reader.GetString()!); + } + catch (Exception ex) + { + throw new Exception(string.Format("Error parsing version string: {0}", reader.GetString()), ex); + } + } + throw new Exception(string.Format("Unexpected token or value when parsing version. Token: {0}, Value: {1}", reader.TokenType, reader.GetString())); } /// /// Determines whether this instance can convert the specified object type. /// /// Type of the object. - /// - /// true if this instance can convert the specified object type; otherwise, false. - /// + /// true if this instance can convert the specified object type; otherwise, false. public override bool CanConvert(Type objectType) { return objectType == typeof(Version); @@ -318,7 +313,7 @@ namespace Flax.Build Log.Verbose("Loading project file from \"" + path + "\"..."); var contents = File.ReadAllText(path); var project = JsonSerializer.Deserialize(contents.AsSpan(), - new JsonSerializerOptions() { Converters = { new FlaxVersionConverter() }, IncludeFields = true, TypeInfoResolver = ProjectInfoSourceGenerationContext.Default }); + new JsonSerializerOptions() { Converters = { new FlaxVersionConverter() }, IncludeFields = true, TypeInfoResolver = ProjectInfoSourceGenerationContext.Default }); project.ProjectPath = path; project.ProjectFolderPath = Path.GetDirectoryName(path);