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/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..8fd28ebfd 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,23 @@ 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 +234,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 +454,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 +472,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 +495,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 +509,296 @@ 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 +815,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 +941,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"); @@ -731,25 +964,7 @@ namespace FlaxEditor.Viewport resolutionValue.ValueChanged += () => ResolutionScale = resolutionValue.Value; 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 +981,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 +1142,58 @@ 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 +1232,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 +1244,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 +1385,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 +1480,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 +1584,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 +1854,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 +1909,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); } } }