From 1f2d665654f74b1791a26d8ac17c7fdc2f99d299 Mon Sep 17 00:00:00 2001 From: Christopher Rothert Date: Wed, 4 Oct 2023 23:06:27 +0200 Subject: [PATCH] Add camera speed easing based on Mathf.InterpEaseInOut * change camera move speed method to use easing curve * add methods for inversion of Mathf.InterpEaseInOut (might have better alternative) * add setting for easing curve degree in ViewportOptions * update camera speed adjustments to use easing curve * remove unused camera speed values array * update some comments and namings --- Source/Editor/Options/ViewportOptions.cs | 7 ++ Source/Editor/Viewport/EditorViewport.cs | 139 +++++++++++++---------- 2 files changed, 83 insertions(+), 63 deletions(-) diff --git a/Source/Editor/Options/ViewportOptions.cs b/Source/Editor/Options/ViewportOptions.cs index 94e3ee1fc..26caa2694 100644 --- a/Source/Editor/Options/ViewportOptions.cs +++ b/Source/Editor/Options/ViewportOptions.cs @@ -46,6 +46,13 @@ namespace FlaxEditor.Options [EditorDisplay("Defaults"), EditorOrder(112), Tooltip("The default maximum movement speed for the viewport camera.")] public float MaxMovementSpeed { get; set; } = 64f; + /// + /// 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("Defaults"), EditorOrder(120), Tooltip("The default degree to which the camera will be eased when using camera flight in the editor window.")] + public float CameraEasingDegree { get; set; } = 3.0f; + /// /// Gets or sets the default near clipping plane distance for the viewport camera. /// diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index a5abfd2b7..51b3a2d78 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -182,11 +182,14 @@ namespace FlaxEditor.Viewport private float _farPlane; private float _orthoSize; private bool _isOrtho; - private float _wheelMovementChangeDeltaSum = 0; + private float _cameraEasingDegree; private float _panningSpeed; private bool _relativePanning; private bool _invertPanning; + private float _linearMovementProgress; + private float _easedMovementProgress; + /// /// Speed of the mouse. /// @@ -499,6 +502,8 @@ namespace FlaxEditor.Viewport if (_editor.ProjectCache.TryGetCustomData("CameraFarPlaneValue", out cachedState)) _farPlane = float.Parse(cachedState); + OnCameraMovementProgressChanged(); + if (useWidgets) { #region Camera settings widget @@ -530,6 +535,7 @@ namespace FlaxEditor.Viewport 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) @@ -564,7 +570,7 @@ namespace FlaxEditor.Viewport }; cameraCM.VisibleChanged += control => maxCamSpeedValue.Value = _maxMovementSpeed; - // Panning Speed + // Panning speed { var panningSpeed = cameraCM.AddButton("Panning Speed"); panningSpeed.CloseMenuOnClick = false; @@ -581,7 +587,7 @@ namespace FlaxEditor.Viewport }; } - // Relative Panning + // Relative panning { var relativePanning = cameraCM.AddButton("Relative Panning"); relativePanning.CloseMenuOnClick = false; @@ -601,7 +607,7 @@ namespace FlaxEditor.Viewport cameraCM.VisibleChanged += control => relativePanningValue.Checked = _relativePanning; } - // Invert Panning + // Invert panning { var invertPanning = cameraCM.AddButton("Invert Panning"); invertPanning.CloseMenuOnClick = false; @@ -616,7 +622,7 @@ namespace FlaxEditor.Viewport cameraCM.AddSeparator(); - // Camera Viewpoints + // Camera viewpoints { var cameraView = cameraCM.AddChildMenu("Viewpoints").ContextMenu; for (int i = 0; i < EditorViewportCameraViewpointValues.Length; i++) @@ -629,7 +635,7 @@ namespace FlaxEditor.Viewport cameraView.ButtonClicked += OnViewpointChanged; } - // Orthographic + // Orthographic mode { var ortho = cameraCM.AddButton("Orthographic"); ortho.CloseMenuOnClick = false; @@ -649,7 +655,7 @@ namespace FlaxEditor.Viewport cameraCM.VisibleChanged += control => orthoValue.Checked = _isOrtho; } - // Field of View + // Field of view { var fov = cameraCM.AddButton("Field Of View"); fov.CloseMenuOnClick = false; @@ -666,7 +672,7 @@ namespace FlaxEditor.Viewport }; } - // Ortho Scale + // Orthographic scale { var orthoSize = cameraCM.AddButton("Ortho Scale"); orthoSize.CloseMenuOnClick = false; @@ -683,7 +689,7 @@ namespace FlaxEditor.Viewport }; } - // Near Plane + // Near plane { var nearPlane = cameraCM.AddButton("Near Plane"); nearPlane.CloseMenuOnClick = false; @@ -696,7 +702,7 @@ namespace FlaxEditor.Viewport cameraCM.VisibleChanged += control => nearPlaneValue.Value = _nearPlane; } - // Far Plane + // Far plane { var farPlane = cameraCM.AddButton("Far Plane"); farPlane.CloseMenuOnClick = false; @@ -711,7 +717,7 @@ namespace FlaxEditor.Viewport cameraCM.AddSeparator(); - //Reset Button + // Reset button { var reset = cameraCM.AddButton("Reset to default"); reset.ButtonClicked += button => @@ -752,7 +758,7 @@ namespace FlaxEditor.Viewport } } - // View Flags + // View flags { var viewFlags = ViewWidgetButtonMenu.AddChildMenu("View Flags").ContextMenu; viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32; @@ -776,7 +782,7 @@ namespace FlaxEditor.Viewport viewFlags.VisibleChanged += WidgetViewFlagsShowHide; } - // Debug View + // Debug view { var debugView = ViewWidgetButtonMenu.AddChildMenu("Debug View").ContextMenu; for (int i = 0; i < EditorViewportViewModeValues.Length; i++) @@ -839,8 +845,8 @@ namespace FlaxEditor.Viewport 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.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown); - InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1)); - InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1)); + InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(Editor.Instance.Options.Options.Viewport.MouseWheelSensitivity * 0.01f)); + InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(Editor.Instance.Options.Options.Viewport.MouseWheelSensitivity * -0.01f)); // Link for task event task.Begin += OnRenderBegin; @@ -873,6 +879,7 @@ namespace FlaxEditor.Viewport var value = Mathf.Clamp(control.Value, _minMovementSpeed, _maxMovementSpeed); MovementSpeed = value; + OnCameraMovementProgressChanged(); _editor.ProjectCache.SetCustomData("CameraMovementSpeedValue", _movementSpeed.ToString()); } @@ -884,6 +891,7 @@ namespace FlaxEditor.Viewport if (_movementSpeed < value) _movementSpeed = value; + OnCameraMovementProgressChanged(); _editor.ProjectCache.SetCustomData("CameraMinMovementSpeedValue", _minMovementSpeed.ToString()); } @@ -895,6 +903,7 @@ namespace FlaxEditor.Viewport if (_movementSpeed > value) _movementSpeed = value; + OnCameraMovementProgressChanged(); _editor.ProjectCache.SetCustomData("CameraMaxMovementSpeedValue", _maxMovementSpeed.ToString()); } @@ -992,33 +1001,63 @@ namespace FlaxEditor.Viewport } } + /// + /// The inverse of .
+ /// Interpolate between A and B, applying a reverse ease in function. Exponent controls the degree of the curve. + ///
+ private float InterpInverseEaseIn(float a, float b, float alpha, float exponent) + { + return 0.5f * Mathf.Pow((2.0f * (alpha - a) / (b - a)), 1.0f / exponent); + } + + /// + /// The inverse of .
+ /// Interpolate between A and B, applying a reverse ease out function. Exponent controls the degree of the curve. + ///
+ private float InterpInverseEaseOut(float a, float b, float alpha, float exponent) + { + return 1.0f - InterpInverseEaseIn(a, b, 1.0f - alpha, exponent); + } + + /// + /// The inverse of .
+ /// Interpolate between A and B, applying a reverse ease in/out function. Exponent controls the degree of the curve. + ///
+ private float InterpInverseEaseInOut(float a, float b, float alpha, float exponent) + { + if (alpha <= 0.0f) + return a; + if (alpha >= 1.0f) + return b; + + return (alpha < 0.5f) ? InterpInverseEaseIn(a, b, alpha, exponent) : InterpInverseEaseOut(a, b, alpha, exponent); + } + + private void OnCameraMovementProgressChanged() + { + _linearMovementProgress = Math.Abs(_minMovementSpeed - _maxMovementSpeed) < Mathf.Epsilon + ? 0.0f + : Mathf.Remap(_movementSpeed, _minMovementSpeed, _maxMovementSpeed, 0.0f, 1.0f); + + _easedMovementProgress = InterpInverseEaseInOut(0.0f, 1.0f, _linearMovementProgress, _cameraEasingDegree); + } + /// /// Increases or decreases the camera movement speed. /// - /// The stepping direction for speed adjustment. - protected void AdjustCameraMoveSpeed(int step) + /// The difference in camera speed adjustment as a fraction of 1. + protected void AdjustCameraMoveSpeed(float speedDelta) { - int camValueIndex = -1; - for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++) - { - if (Mathf.NearEqual(EditorViewportCameraSpeedValues[i], _movementSpeed)) - { - camValueIndex = i; - break; - } - } - if (camValueIndex == -1) - return; - - if (step > 0) - MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Min(camValueIndex + 1, EditorViewportCameraSpeedValues.Length - 1)]; - else if (step < 0) - MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Max(camValueIndex - 1, 0)]; + _easedMovementProgress = Mathf.Clamp(_easedMovementProgress + speedDelta, 0.0f, 1.0f); + var easedSpeed = Mathf.InterpEaseInOut(_minMovementSpeed, _maxMovementSpeed, _easedMovementProgress, _cameraEasingDegree); + MovementSpeed = Mathf.Round(easedSpeed * 100) / 100; } private void OnEditorOptionsChanged(EditorOptions options) { _mouseSensitivity = options.Viewport.MouseSensitivity; + _cameraEasingDegree = options.Viewport.CameraEasingDegree; + OnCameraMovementProgressChanged(); } private void OnRenderBegin(RenderTask task, GPUContext context) @@ -1210,8 +1249,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; @@ -1406,18 +1443,10 @@ 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); - } + // speed delta can be adjusted through mouse wheel sensitivity in editor + // with default sensitivity (1.0), it takes about ~100 scrolls from min to max + var camSpeedDelta = _input.MouseWheelDelta * options.Viewport.MouseWheelSensitivity * 0.01f; + AdjustCameraMoveSpeed(camSpeedDelta); } } @@ -1686,22 +1715,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;