2072 lines
79 KiB
C#
2072 lines
79 KiB
C#
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using FlaxEditor.Content.Settings;
|
|
using FlaxEditor.GUI.ContextMenu;
|
|
using FlaxEditor.GUI.Input;
|
|
using FlaxEditor.Options;
|
|
using FlaxEditor.Viewport.Cameras;
|
|
using FlaxEditor.Viewport.Widgets;
|
|
using FlaxEngine;
|
|
using FlaxEngine.GUI;
|
|
using JsonSerializer = FlaxEngine.Json.JsonSerializer;
|
|
|
|
namespace FlaxEditor.Viewport
|
|
{
|
|
/// <summary>
|
|
/// Editor viewports base class.
|
|
/// </summary>
|
|
/// <seealso cref="FlaxEngine.GUI.RenderOutputControl" />
|
|
public class EditorViewport : RenderOutputControl
|
|
{
|
|
/// <summary>
|
|
/// Gathered input data.
|
|
/// </summary>
|
|
public struct Input
|
|
{
|
|
/// <summary>
|
|
/// The is panning state.
|
|
/// </summary>
|
|
public bool IsPanning;
|
|
|
|
/// <summary>
|
|
/// The is rotating state.
|
|
/// </summary>
|
|
public bool IsRotating;
|
|
|
|
/// <summary>
|
|
/// The is moving state.
|
|
/// </summary>
|
|
public bool IsMoving;
|
|
|
|
/// <summary>
|
|
/// The is zooming state.
|
|
/// </summary>
|
|
public bool IsZooming;
|
|
|
|
/// <summary>
|
|
/// The is orbiting state.
|
|
/// </summary>
|
|
public bool IsOrbiting;
|
|
|
|
/// <summary>
|
|
/// The is control down flag.
|
|
/// </summary>
|
|
public bool IsControlDown;
|
|
|
|
/// <summary>
|
|
/// The is shift down flag.
|
|
/// </summary>
|
|
public bool IsShiftDown;
|
|
|
|
/// <summary>
|
|
/// The is alt down flag.
|
|
/// </summary>
|
|
public bool IsAltDown;
|
|
|
|
/// <summary>
|
|
/// The is alt down flag cached from the previous input. Used to make <see cref="IsControllingMouse"/> consistent when user releases Alt while orbiting with Alt+LMB.
|
|
/// </summary>
|
|
public bool WasAltDownBefore;
|
|
|
|
/// <summary>
|
|
/// The is mouse right down flag.
|
|
/// </summary>
|
|
public bool IsMouseRightDown;
|
|
|
|
/// <summary>
|
|
/// The is mouse middle down flag.
|
|
/// </summary>
|
|
public bool IsMouseMiddleDown;
|
|
|
|
/// <summary>
|
|
/// The is mouse left down flag.
|
|
/// </summary>
|
|
public bool IsMouseLeftDown;
|
|
|
|
/// <summary>
|
|
/// The mouse wheel delta.
|
|
/// </summary>
|
|
public float MouseWheelDelta;
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether use is controlling mouse.
|
|
/// </summary>
|
|
public bool IsControllingMouse => IsMouseMiddleDown || IsMouseRightDown || ((IsAltDown || WasAltDownBefore) && IsMouseLeftDown) || Mathf.Abs(MouseWheelDelta) > 0.1f;
|
|
|
|
/// <summary>
|
|
/// Gathers input from the specified window.
|
|
/// </summary>
|
|
/// <param name="window">The window.</param>
|
|
/// <param name="useMouse">True if use mouse input, otherwise will skip mouse.</param>
|
|
/// <param name="prevInput">Previous input state.</param>
|
|
public void Gather(Window window, bool useMouse, ref Input prevInput)
|
|
{
|
|
IsControlDown = window.GetKey(KeyboardKeys.Control);
|
|
IsShiftDown = window.GetKey(KeyboardKeys.Shift);
|
|
IsAltDown = window.GetKey(KeyboardKeys.Alt);
|
|
WasAltDownBefore = prevInput.WasAltDownBefore || prevInput.IsAltDown;
|
|
|
|
IsMouseRightDown = useMouse && window.GetMouseButton(MouseButton.Right);
|
|
IsMouseMiddleDown = useMouse && window.GetMouseButton(MouseButton.Middle);
|
|
IsMouseLeftDown = useMouse && window.GetMouseButton(MouseButton.Left);
|
|
|
|
if (WasAltDownBefore && !IsMouseLeftDown && !IsAltDown)
|
|
WasAltDownBefore = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the data.
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
IsControlDown = false;
|
|
IsShiftDown = false;
|
|
IsAltDown = false;
|
|
WasAltDownBefore = false;
|
|
|
|
IsMouseRightDown = false;
|
|
IsMouseMiddleDown = false;
|
|
IsMouseLeftDown = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The FPS camera filtering frames count (how much frames we want to keep in the buffer to calculate the avg. delta currently hardcoded).
|
|
/// </summary>
|
|
public const int FpsCameraFilteringFrames = 3;
|
|
|
|
/// <summary>
|
|
/// The camera settings widget.
|
|
/// </summary>
|
|
protected ViewportWidgetsContainer _cameraWidget;
|
|
|
|
/// <summary>
|
|
/// The camera settings widget button.
|
|
/// </summary>
|
|
protected ViewportWidgetButton _cameraButton;
|
|
|
|
/// <summary>
|
|
/// The orthographic mode widget button.
|
|
/// </summary>
|
|
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;
|
|
|
|
// Input
|
|
|
|
internal bool _disableInputUpdate;
|
|
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
|
|
private int _deltaFilteringStep;
|
|
private Float2 _startPos;
|
|
private Float2 _mouseDeltaLast;
|
|
private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames];
|
|
|
|
/// <summary>
|
|
/// The previous input (from the previous update).
|
|
/// </summary>
|
|
protected Input _prevInput;
|
|
|
|
/// <summary>
|
|
/// The input data (from the current frame).
|
|
/// </summary>
|
|
protected Input _input;
|
|
|
|
/// <summary>
|
|
/// The view mouse position.
|
|
/// </summary>
|
|
protected Float2 _viewMousePos;
|
|
|
|
/// <summary>
|
|
/// The mouse position delta.
|
|
/// </summary>
|
|
protected Float2 _mouseDelta;
|
|
|
|
// Camera
|
|
|
|
private ViewportCamera _camera;
|
|
private float _yaw;
|
|
private float _pitch;
|
|
private float _fieldOfView;
|
|
private float _nearPlane;
|
|
private float _farPlane;
|
|
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;
|
|
|
|
/// <summary>
|
|
/// Speed of the mouse.
|
|
/// </summary>
|
|
public float MouseSpeed = 1;
|
|
|
|
/// <summary>
|
|
/// Speed of the mouse wheel zooming.
|
|
/// </summary>
|
|
public float MouseWheelZoomSpeedFactor = 1;
|
|
|
|
/// <summary>
|
|
/// Format of the text for the camera move speed.
|
|
/// </summary>
|
|
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:#}";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the camera movement speed.
|
|
/// </summary>
|
|
public float MovementSpeed
|
|
{
|
|
get => _movementSpeed;
|
|
set
|
|
{
|
|
_movementSpeed = value;
|
|
|
|
if (_cameraButton != null)
|
|
_cameraButton.Text = string.Format(MovementSpeedTextFormat, _movementSpeed);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the minimum camera movement speed.
|
|
/// </summary>
|
|
public float MinMovementSpeed
|
|
{
|
|
get => _minMovementSpeed;
|
|
set => _minMovementSpeed = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the maximum camera movement speed.
|
|
/// </summary>
|
|
public float MaxMovementSpeed
|
|
{
|
|
get => _maxMovementSpeed;
|
|
set => _maxMovementSpeed = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the camera easing mode.
|
|
/// </summary>
|
|
public bool UseCameraEasing
|
|
{
|
|
get => _useCameraEasing;
|
|
set => _useCameraEasing = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the mouse movement position delta (user press and move).
|
|
/// </summary>
|
|
public Float2 MousePositionDelta => _mouseDelta;
|
|
|
|
/// <summary>
|
|
/// Camera's pitch angle clamp range (in degrees).
|
|
/// </summary>
|
|
public Float2 CamPitchAngles = new Float2(-88, 88);
|
|
|
|
/// <summary>
|
|
/// Gets the view transform.
|
|
/// </summary>
|
|
public Transform ViewTransform
|
|
{
|
|
get => new Transform(ViewPosition, ViewOrientation);
|
|
set
|
|
{
|
|
ViewPosition = value.Translation;
|
|
ViewOrientation = value.Orientation;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the view position.
|
|
/// </summary>
|
|
public Vector3 ViewPosition { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the view orientation.
|
|
/// </summary>
|
|
public Quaternion ViewOrientation
|
|
{
|
|
get => Quaternion.RotationYawPitchRoll(_yaw * Mathf.DegreesToRadians, _pitch * Mathf.DegreesToRadians, 0);
|
|
set => EulerAngles = value.EulerAngles;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the view direction vector.
|
|
/// </summary>
|
|
public Float3 ViewDirection
|
|
{
|
|
get => Float3.Forward * ViewOrientation;
|
|
set
|
|
{
|
|
var right = Float3.Cross(value, Float3.Up);
|
|
var up = Float3.Cross(right, value);
|
|
ViewOrientation = Quaternion.LookRotation(value, up);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the view ray (position and direction).
|
|
/// </summary>
|
|
public Ray ViewRay
|
|
{
|
|
get => new Ray(ViewPosition, ViewDirection);
|
|
set
|
|
{
|
|
ViewPosition = value.Position;
|
|
ViewDirection = value.Direction;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the bounding frustum of the current viewport camera.
|
|
/// </summary>
|
|
public BoundingFrustum ViewFrustum
|
|
{
|
|
get
|
|
{
|
|
Vector3 viewOrigin = Task.View.Origin;
|
|
Float3 position = ViewPosition - viewOrigin;
|
|
CreateViewMatrix(position, out var view);
|
|
CreateProjectionMatrix(out var projection);
|
|
Matrix.Multiply(ref view, ref projection, out var viewProjection);
|
|
return new BoundingFrustum(ref viewProjection);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the yaw angle (in degrees).
|
|
/// </summary>
|
|
public float Yaw
|
|
{
|
|
get => _yaw;
|
|
set => _yaw = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the pitch angle (in degrees).
|
|
/// </summary>
|
|
public float Pitch
|
|
{
|
|
get => _pitch;
|
|
set => _pitch = Mathf.Clamp(value, CamPitchAngles.X, CamPitchAngles.Y);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the absolute mouse position (normalized, not in pixels). Yaw is X, Pitch is Y.
|
|
/// </summary>
|
|
public Float2 YawPitch
|
|
{
|
|
get => new Float2(_yaw, _pitch);
|
|
set
|
|
{
|
|
Yaw = value.X;
|
|
Pitch = value.Y;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the euler angles (pitch, yaw, roll).
|
|
/// </summary>
|
|
public Float3 EulerAngles
|
|
{
|
|
get => new Float3(_pitch, _yaw, 0);
|
|
set
|
|
{
|
|
Pitch = value.X;
|
|
Yaw = value.Y;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this viewport has loaded dependant assets.
|
|
/// </summary>
|
|
public virtual bool HasLoadedAssets => true;
|
|
|
|
/// <summary>
|
|
/// The 'View' widget button context menu.
|
|
/// </summary>
|
|
public ContextMenu ViewWidgetButtonMenu;
|
|
|
|
/// <summary>
|
|
/// The 'View' widget 'Show' category context menu.
|
|
/// </summary>
|
|
public ContextMenu ViewWidgetShowMenu;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the viewport camera controller.
|
|
/// </summary>
|
|
public ViewportCamera ViewportCamera
|
|
{
|
|
get => _camera;
|
|
set
|
|
{
|
|
if (_camera != null)
|
|
_camera.Viewport = null;
|
|
|
|
_camera = value;
|
|
|
|
if (_camera != null)
|
|
_camera.Viewport = this;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the camera near clipping plane.
|
|
/// </summary>
|
|
public float NearPlane
|
|
{
|
|
get => _nearPlane;
|
|
set => _nearPlane = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the camera far clipping plane.
|
|
/// </summary>
|
|
public float FarPlane
|
|
{
|
|
get => _farPlane;
|
|
set => _farPlane = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the camera field of view (in degrees).
|
|
/// </summary>
|
|
public float FieldOfView
|
|
{
|
|
get => _fieldOfView;
|
|
set => _fieldOfView = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the camera orthographic size scale (if camera uses orthographic mode).
|
|
/// </summary>
|
|
public float OrthographicScale
|
|
{
|
|
get => _orthoSize;
|
|
set => _orthoSize = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the camera orthographic mode (otherwise uses perspective projection).
|
|
/// </summary>
|
|
public bool UseOrthographicProjection
|
|
{
|
|
get => _isOrtho;
|
|
set => _isOrtho = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets if the panning speed should be relative to the camera target.
|
|
/// </summary>
|
|
public bool RelativePanning
|
|
{
|
|
get => _relativePanning;
|
|
set => _relativePanning = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets if the panning direction is inverted.
|
|
/// </summary>
|
|
public bool InvertPanning
|
|
{
|
|
get => _invertPanning;
|
|
set => _invertPanning = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the camera panning speed.
|
|
/// </summary>
|
|
public float PanningSpeed
|
|
{
|
|
get => _panningSpeed;
|
|
set => _panningSpeed = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The input actions collection to processed during user input.
|
|
/// </summary>
|
|
public InputActionsContainer InputActions = new InputActionsContainer();
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="EditorViewport"/> class.
|
|
/// </summary>
|
|
/// <param name="task">The task.</param>
|
|
/// <param name="camera">The camera controller.</param>
|
|
/// <param name="useWidgets">Enable/disable viewport widgets.</param>
|
|
public EditorViewport(SceneRenderTask task, ViewportCamera camera, bool useWidgets)
|
|
: base(task)
|
|
{
|
|
_editor = Editor.Instance;
|
|
|
|
_mouseAccelerationScale = 0.1f;
|
|
_useMouseFiltering = false;
|
|
_useMouseAcceleration = false;
|
|
_camera = camera;
|
|
if (_camera != null)
|
|
_camera.Viewport = this;
|
|
|
|
AnchorPreset = AnchorPresets.StretchAll;
|
|
Offsets = Margin.Zero;
|
|
|
|
// Setup options
|
|
{
|
|
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
|
|
SetupViewportOptions();
|
|
}
|
|
|
|
// Initialize camera values from cache
|
|
if (_editor.ProjectCache.TryGetCustomData("CameraMovementSpeedValue", out float cachedFloat))
|
|
MovementSpeed = cachedFloat;
|
|
if (_editor.ProjectCache.TryGetCustomData("CameraMinMovementSpeedValue", out cachedFloat))
|
|
_minMovementSpeed = cachedFloat;
|
|
if (_editor.ProjectCache.TryGetCustomData("CameraMaxMovementSpeedValue", out cachedFloat))
|
|
_maxMovementSpeed = cachedFloat;
|
|
if (_editor.ProjectCache.TryGetCustomData("UseCameraEasingState", out bool cachedBool))
|
|
_useCameraEasing = cachedBool;
|
|
if (_editor.ProjectCache.TryGetCustomData("CameraPanningSpeedValue", out cachedFloat))
|
|
_panningSpeed = cachedFloat;
|
|
if (_editor.ProjectCache.TryGetCustomData("CameraInvertPanningState", out cachedBool))
|
|
_invertPanning = cachedBool;
|
|
if (_editor.ProjectCache.TryGetCustomData("CameraRelativePanningState", out cachedBool))
|
|
_relativePanning = cachedBool;
|
|
if (_editor.ProjectCache.TryGetCustomData("CameraOrthographicState", out cachedBool))
|
|
_isOrtho = cachedBool;
|
|
if (_editor.ProjectCache.TryGetCustomData("CameraOrthographicSizeValue", out cachedFloat))
|
|
_orthoSize = cachedFloat;
|
|
if (_editor.ProjectCache.TryGetCustomData("CameraFieldOfViewValue", out cachedFloat))
|
|
_fieldOfView = cachedFloat;
|
|
if (_editor.ProjectCache.TryGetCustomData("CameraNearPlaneValue", out cachedFloat))
|
|
_nearPlane = cachedFloat;
|
|
if (_editor.ProjectCache.TryGetCustomData("CameraFarPlaneValue", out cachedFloat))
|
|
_farPlane = cachedFloat;
|
|
|
|
OnCameraMovementProgressChanged();
|
|
|
|
if (useWidgets)
|
|
{
|
|
#region Camera settings widget
|
|
|
|
var largestText = "Relative Panning";
|
|
var textSize = Style.Current.FontMedium.MeasureText(largestText);
|
|
var xLocationForExtras = textSize.X + 5;
|
|
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 Settings",
|
|
Parent = _cameraWidget
|
|
};
|
|
_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 < CameraViewpointValues.Length; i++)
|
|
{
|
|
var co = CameraViewpointValues[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;
|
|
|
|
var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft);
|
|
ViewWidgetButtonMenu = new ContextMenu();
|
|
var viewModeButton = new ViewportWidgetButton("View", SpriteHandle.Invalid, ViewWidgetButtonMenu)
|
|
{
|
|
TooltipText = "View properties",
|
|
Parent = viewMode
|
|
};
|
|
viewMode.Parent = this;
|
|
|
|
// Show
|
|
{
|
|
ViewWidgetShowMenu = ViewWidgetButtonMenu.AddChildMenu("Show").ContextMenu;
|
|
|
|
// Show FPS
|
|
{
|
|
InitFpsCounter();
|
|
_showFpsButton = ViewWidgetShowMenu.AddButton("FPS Counter", () => ShowFpsCounter = !ShowFpsCounter);
|
|
_showFpsButton.CloseMenuOnClick = false;
|
|
}
|
|
}
|
|
|
|
// View Layers
|
|
{
|
|
var viewLayers = ViewWidgetButtonMenu.AddChildMenu("View Layers").ContextMenu;
|
|
viewLayers.AddButton("Copy layers", () => Clipboard.Text = JsonSerializer.Serialize(Task.View.RenderLayersMask));
|
|
viewLayers.AddButton("Paste layers", () =>
|
|
{
|
|
try
|
|
{
|
|
Task.ViewLayersMask = JsonSerializer.Deserialize<LayersMask>(Clipboard.Text);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
});
|
|
viewLayers.AddButton("Reset layers", () => Task.ViewLayersMask = LayersMask.Default).Icon = Editor.Instance.Icons.Rotate32;
|
|
viewLayers.AddButton("Disable layers", () => Task.ViewLayersMask = new LayersMask(0)).Icon = Editor.Instance.Icons.Rotate32;
|
|
viewLayers.AddSeparator();
|
|
var layers = LayersAndTagsSettings.GetCurrentLayers();
|
|
if (layers != null && layers.Length > 0)
|
|
{
|
|
for (int i = 0; i < layers.Length; i++)
|
|
{
|
|
var layer = layers[i];
|
|
var button = viewLayers.AddButton(layer);
|
|
button.CloseMenuOnClick = false;
|
|
button.Tag = 1 << i;
|
|
}
|
|
}
|
|
viewLayers.ButtonClicked += button =>
|
|
{
|
|
if (button.Tag != null)
|
|
{
|
|
int layerIndex = (int)button.Tag;
|
|
LayersMask mask = new LayersMask(layerIndex);
|
|
Task.ViewLayersMask ^= mask;
|
|
button.Icon = (Task.ViewLayersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
|
}
|
|
};
|
|
viewLayers.VisibleChanged += WidgetViewLayersShowHide;
|
|
}
|
|
|
|
// View Flags
|
|
{
|
|
var viewFlags = ViewWidgetButtonMenu.AddChildMenu("View Flags").ContextMenu;
|
|
viewFlags.AddButton("Copy flags", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewFlags));
|
|
viewFlags.AddButton("Paste flags", () =>
|
|
{
|
|
try
|
|
{
|
|
Task.ViewFlags = JsonSerializer.Deserialize<ViewFlags>(Clipboard.Text);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
});
|
|
viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32;
|
|
viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32;
|
|
viewFlags.AddSeparator();
|
|
for (int i = 0; i < ViewFlagsValues.Length; i++)
|
|
{
|
|
var v = ViewFlagsValues[i];
|
|
var button = viewFlags.AddButton(v.Name);
|
|
button.CloseMenuOnClick = false;
|
|
button.Tag = v.Mode;
|
|
}
|
|
viewFlags.ButtonClicked += button =>
|
|
{
|
|
if (button.Tag != null)
|
|
{
|
|
var v = (ViewFlags)button.Tag;
|
|
Task.ViewFlags ^= v;
|
|
button.Icon = (Task.ViewFlags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
|
}
|
|
};
|
|
viewFlags.VisibleChanged += WidgetViewFlagsShowHide;
|
|
}
|
|
|
|
// Debug View
|
|
{
|
|
var debugView = ViewWidgetButtonMenu.AddChildMenu("Debug View").ContextMenu;
|
|
debugView.AddButton("Copy view", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewMode));
|
|
debugView.AddButton("Paste view", () =>
|
|
{
|
|
try
|
|
{
|
|
Task.ViewMode = JsonSerializer.Deserialize<ViewMode>(Clipboard.Text);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
});
|
|
debugView.AddSeparator();
|
|
for (int i = 0; i < ViewModeValues.Length; i++)
|
|
{
|
|
ref var v = ref ViewModeValues[i];
|
|
if (v.Options != null)
|
|
{
|
|
var childMenu = debugView.AddChildMenu(v.Name).ContextMenu;
|
|
childMenu.ButtonClicked += WidgetViewModeShowHideClicked;
|
|
childMenu.VisibleChanged += WidgetViewModeShowHide;
|
|
for (int j = 0; j < v.Options.Length; j++)
|
|
{
|
|
ref var vv = ref v.Options[j];
|
|
var button = childMenu.AddButton(vv.Name);
|
|
button.CloseMenuOnClick = false;
|
|
button.Tag = vv.Mode;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var button = debugView.AddButton(v.Name);
|
|
button.CloseMenuOnClick = false;
|
|
button.Tag = v.Mode;
|
|
}
|
|
}
|
|
debugView.ButtonClicked += WidgetViewModeShowHideClicked;
|
|
debugView.VisibleChanged += WidgetViewModeShowHide;
|
|
}
|
|
|
|
ViewWidgetButtonMenu.AddSeparator();
|
|
|
|
// Brightness
|
|
{
|
|
var brightness = ViewWidgetButtonMenu.AddButton("Brightness");
|
|
brightness.CloseMenuOnClick = false;
|
|
var brightnessValue = new FloatValueBox(1.0f, xLocationForExtras, 2, 70.0f, 0.001f, 10.0f, 0.001f)
|
|
{
|
|
Parent = brightness
|
|
};
|
|
brightnessValue.ValueChanged += () => Brightness = brightnessValue.Value;
|
|
ViewWidgetButtonMenu.VisibleChanged += control => brightnessValue.Value = Brightness;
|
|
}
|
|
|
|
// Resolution
|
|
{
|
|
var resolution = ViewWidgetButtonMenu.AddButton("Resolution");
|
|
resolution.CloseMenuOnClick = false;
|
|
var resolutionValue = new FloatValueBox(1.0f, xLocationForExtras, 2, 70.0f, 0.1f, 4.0f, 0.001f)
|
|
{
|
|
Parent = resolution
|
|
};
|
|
resolutionValue.ValueChanged += () => ResolutionScale = resolutionValue.Value;
|
|
ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale;
|
|
}
|
|
|
|
#endregion View mode widget
|
|
}
|
|
|
|
InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Top").Orientation)));
|
|
InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation)));
|
|
InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Front").Orientation)));
|
|
InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Back").Orientation)));
|
|
InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Right").Orientation)));
|
|
InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Left").Orientation)));
|
|
InputActions.Add(options => options.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown);
|
|
InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1));
|
|
InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1));
|
|
InputActions.Add(options => options.ToggleOrthographic, () => OnOrthographicModeToggled(null));
|
|
|
|
// Link for task event
|
|
task.Begin += OnRenderBegin;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the viewport options to the default values.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
private void OnCameraEasingToggled(Control control)
|
|
{
|
|
_useCameraEasing = !_useCameraEasing;
|
|
|
|
OnCameraMovementProgressChanged();
|
|
_editor.ProjectCache.SetCustomData("UseCameraEasingState", _useCameraEasing);
|
|
}
|
|
|
|
private void OnPanningSpeedChanged(FloatValueBox control)
|
|
{
|
|
_panningSpeed = control.Value;
|
|
_editor.ProjectCache.SetCustomData("CameraPanningSpeedValue", _panningSpeed);
|
|
}
|
|
|
|
private void OnRelativePanningToggled(Control control)
|
|
{
|
|
_relativePanning = !_relativePanning;
|
|
_editor.ProjectCache.SetCustomData("CameraRelativePanningState", _relativePanning);
|
|
}
|
|
|
|
private void OnInvertPanningToggled(Control control)
|
|
{
|
|
_invertPanning = !_invertPanning;
|
|
_editor.ProjectCache.SetCustomData("CameraInvertPanningState", _invertPanning);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
private void OnOrthographicSizeChanged(FloatValueBox control)
|
|
{
|
|
_orthoSize = control.Value;
|
|
_editor.ProjectCache.SetCustomData("CameraOrthographicSizeValue", _orthoSize);
|
|
}
|
|
|
|
private void OnNearPlaneChanged(FloatValueBox control)
|
|
{
|
|
_nearPlane = control.Value;
|
|
_editor.ProjectCache.SetCustomData("CameraNearPlaneValue", _nearPlane);
|
|
}
|
|
|
|
private void OnFarPlaneChanged(FloatValueBox control)
|
|
{
|
|
_farPlane = control.Value;
|
|
_editor.ProjectCache.SetCustomData("CameraFarPlaneValue", _farPlane);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this viewport is using mouse currently (eg. user moving objects).
|
|
/// </summary>
|
|
protected virtual bool IsControllingMouse => false;
|
|
|
|
/// <summary>
|
|
/// Orients the viewport.
|
|
/// </summary>
|
|
/// <param name="orientation">The orientation.</param>
|
|
protected void OrientViewport(Quaternion orientation)
|
|
{
|
|
var quat = orientation;
|
|
OrientViewport(ref quat);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Orients the viewport.
|
|
/// </summary>
|
|
/// <param name="orientation">The orientation.</param>
|
|
protected virtual void OrientViewport(ref Quaternion orientation)
|
|
{
|
|
if (ViewportCamera is FPSCamera fpsCamera)
|
|
{
|
|
var pos = ViewPosition + Float3.Backward * orientation * 2000.0f;
|
|
fpsCamera.MoveViewport(pos, orientation);
|
|
}
|
|
else
|
|
{
|
|
ViewportCamera.SetArcBallView(orientation, ViewPosition, 2000.0f);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Increases or decreases the camera movement speed.
|
|
/// </summary>
|
|
/// <param name="step">The stepping direction for speed adjustment.</param>
|
|
protected void AdjustCameraMoveSpeed(int step)
|
|
{
|
|
_speedStep = Mathf.Clamp(_speedStep + step, 0, _maxSpeedSteps);
|
|
|
|
// 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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
var sceneTask = (SceneRenderTask)task;
|
|
|
|
var view = sceneTask.View;
|
|
CopyViewData(ref view);
|
|
sceneTask.View = view;
|
|
}
|
|
|
|
#region FPS Counter
|
|
|
|
private class FpsCounter : Control
|
|
{
|
|
public FpsCounter(float x, float y)
|
|
: base(x, y, 64, 32)
|
|
{
|
|
}
|
|
|
|
public override void Draw()
|
|
{
|
|
base.Draw();
|
|
|
|
int fps = Engine.FramesPerSecond;
|
|
Color color = Color.Green;
|
|
if (fps < 13)
|
|
color = Color.Red;
|
|
else if (fps < 22)
|
|
color = Color.Yellow;
|
|
var text = string.Format("FPS: {0}", fps);
|
|
var font = Style.Current.FontMedium;
|
|
Render2D.DrawText(font, text, new Rectangle(Float2.One, Size), Color.Black);
|
|
Render2D.DrawText(font, text, new Rectangle(Float2.Zero, Size), color);
|
|
}
|
|
}
|
|
|
|
private FpsCounter _fpsCounter;
|
|
private ContextMenuButton _showFpsButton;
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether show or hide FPS counter.
|
|
/// </summary>
|
|
public bool ShowFpsCounter
|
|
{
|
|
get => _fpsCounter.Visible;
|
|
set
|
|
{
|
|
_fpsCounter.Visible = value;
|
|
_fpsCounter.Enabled = value;
|
|
_showFpsButton.Icon = value ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
|
}
|
|
}
|
|
|
|
private void InitFpsCounter()
|
|
{
|
|
_fpsCounter = new FpsCounter(10, ViewportWidgetsContainer.WidgetsHeight + 14)
|
|
{
|
|
Visible = false,
|
|
Enabled = false,
|
|
Parent = this
|
|
};
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Takes the screenshot of the current viewport.
|
|
/// </summary>
|
|
/// <param name="path">The output file path. Set null to use default value.</param>
|
|
public void TakeScreenshot(string path = null)
|
|
{
|
|
Screenshot.Capture(Task, path);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies the render view data to <see cref="RenderView"/> structure.
|
|
/// </summary>
|
|
/// <param name="view">The view.</param>
|
|
public void CopyViewData(ref RenderView view)
|
|
{
|
|
Vector3 position = ViewPosition;
|
|
LargeWorlds.UpdateOrigin(ref view.Origin, position);
|
|
view.Position = position - view.Origin;
|
|
view.Direction = ViewDirection;
|
|
view.Near = _nearPlane;
|
|
view.Far = _farPlane;
|
|
|
|
CreateProjectionMatrix(out view.Projection);
|
|
CreateViewMatrix(view.Position, out view.View);
|
|
|
|
view.UpdateCachedData();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the input state data.
|
|
/// </summary>
|
|
/// <param name="input">The input.</param>
|
|
public void GetInput(out Input input)
|
|
{
|
|
input = _input;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the input state data (from the previous update).
|
|
/// </summary>
|
|
/// <param name="input">The input.</param>
|
|
public void GetPrevInput(out Input input)
|
|
{
|
|
input = _prevInput;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the projection matrix.
|
|
/// </summary>
|
|
/// <param name="result">The result.</param>
|
|
protected virtual void CreateProjectionMatrix(out Matrix result)
|
|
{
|
|
if (_isOrtho)
|
|
{
|
|
Matrix.Ortho(Width * _orthoSize, Height * _orthoSize, _nearPlane, _farPlane, out result);
|
|
}
|
|
else
|
|
{
|
|
float aspect = Width / Height;
|
|
Matrix.PerspectiveFov(_fieldOfView * Mathf.DegreesToRadians, aspect, _nearPlane, _farPlane, out result);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the view matrix.
|
|
/// </summary>
|
|
/// <param name="position">The view position.</param>
|
|
/// <param name="result">The result.</param>
|
|
protected virtual void CreateViewMatrix(Float3 position, out Matrix result)
|
|
{
|
|
var direction = ViewDirection;
|
|
var target = position + direction;
|
|
var right = Float3.Normalize(Float3.Cross(Float3.Up, direction));
|
|
var up = Float3.Normalize(Float3.Cross(direction, right));
|
|
Matrix.LookAt(ref position, ref target, ref up, out result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the mouse ray.
|
|
/// </summary>
|
|
public Ray MouseRay
|
|
{
|
|
get
|
|
{
|
|
if (IsMouseOver)
|
|
return ConvertMouseToRay(ref _viewMousePos);
|
|
return new Ray(Vector3.Maximum, Vector3.Up);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts the mouse position to the ray (in world space of the viewport).
|
|
/// </summary>
|
|
/// <param name="mousePosition">The mouse position (in UI space of the viewport [0; Size]).</param>
|
|
/// <returns>The result ray.</returns>
|
|
public Ray ConvertMouseToRay(ref Float2 mousePosition)
|
|
{
|
|
var viewport = new FlaxEngine.Viewport(0, 0, Width, Height);
|
|
if (viewport.Width < Mathf.Epsilon || viewport.Height < Mathf.Epsilon)
|
|
return ViewRay;
|
|
|
|
Vector3 viewOrigin = Task.View.Origin;
|
|
Float3 position = ViewPosition - viewOrigin;
|
|
|
|
// Use different logic in orthographic projection
|
|
if (_isOrtho)
|
|
{
|
|
var screenPosition = new Float2(mousePosition.X / viewport.Width - 0.5f, -mousePosition.Y / viewport.Height + 0.5f);
|
|
var orientation = ViewOrientation;
|
|
var direction = Float3.Forward * orientation;
|
|
var rayOrigin = new Vector3(screenPosition.X * viewport.Width * _orthoSize, screenPosition.Y * viewport.Height * _orthoSize, 0);
|
|
rayOrigin = position + Vector3.Transform(rayOrigin, orientation);
|
|
rayOrigin += direction * _nearPlane;
|
|
rayOrigin += viewOrigin;
|
|
return new Ray(rayOrigin, direction);
|
|
}
|
|
|
|
// Create view frustum
|
|
CreateProjectionMatrix(out var p);
|
|
CreateViewMatrix(position, out var v);
|
|
Matrix.Multiply(ref v, ref p, out var ivp);
|
|
ivp.Invert();
|
|
|
|
// Create near and far points
|
|
var nearPoint = new Vector3(mousePosition, _nearPlane);
|
|
var farPoint = new Vector3(mousePosition, _farPlane);
|
|
viewport.Unproject(ref nearPoint, ref ivp, out nearPoint);
|
|
viewport.Unproject(ref farPoint, ref ivp, out farPoint);
|
|
|
|
return new Ray(nearPoint + viewOrigin, Vector3.Normalize(farPoint - nearPoint));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when mouse control begins.
|
|
/// </summary>
|
|
/// <param name="win">The parent window.</param>
|
|
protected virtual void OnControlMouseBegin(Window win)
|
|
{
|
|
// Hide cursor and start tracking mouse movement
|
|
win.StartTrackingMouse(false);
|
|
win.Cursor = CursorType.Hidden;
|
|
|
|
// Center mouse position if it's too close to the edge
|
|
var size = Size;
|
|
var center = Float2.Round(size * 0.5f);
|
|
if (Mathf.Abs(_viewMousePos.X - center.X) > center.X * 0.8f || Mathf.Abs(_viewMousePos.Y - center.Y) > center.Y * 0.8f)
|
|
{
|
|
_viewMousePos = center;
|
|
win.MousePosition = PointToWindow(_viewMousePos);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when mouse control ends.
|
|
/// </summary>
|
|
/// <param name="win">The parent window.</param>
|
|
protected virtual void OnControlMouseEnd(Window win)
|
|
{
|
|
// Restore cursor and stop tracking mouse movement
|
|
win.Cursor = CursorType.Default;
|
|
win.EndTrackingMouse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when left mouse button goes down (on press).
|
|
/// </summary>
|
|
protected virtual void OnLeftMouseButtonDown()
|
|
{
|
|
_startPos = _viewMousePos;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when left mouse button goes up (on release).
|
|
/// </summary>
|
|
protected virtual void OnLeftMouseButtonUp()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when right mouse button goes down (on press).
|
|
/// </summary>
|
|
protected virtual void OnRightMouseButtonDown()
|
|
{
|
|
_startPos = _viewMousePos;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when right mouse button goes up (on release).
|
|
/// </summary>
|
|
protected virtual void OnRightMouseButtonUp()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when middle mouse button goes down (on press).
|
|
/// </summary>
|
|
protected virtual void OnMiddleMouseButtonDown()
|
|
{
|
|
_startPos = _viewMousePos;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when middle mouse button goes up (on release).
|
|
/// </summary>
|
|
protected virtual void OnMiddleMouseButtonUp()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the view.
|
|
/// </summary>
|
|
/// <param name="dt">The delta time (in seconds).</param>
|
|
/// <param name="moveDelta">The move delta (scaled).</param>
|
|
/// <param name="mouseDelta">The mouse delta (scaled).</param>
|
|
/// <param name="centerMouse">True if center mouse after the update.</param>
|
|
protected virtual void UpdateView(float dt, ref Vector3 moveDelta, ref Float2 mouseDelta, out bool centerMouse)
|
|
{
|
|
centerMouse = true;
|
|
_camera?.UpdateView(dt, ref moveDelta, ref mouseDelta, out centerMouse);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Update(float deltaTime)
|
|
{
|
|
base.Update(deltaTime);
|
|
|
|
if (_disableInputUpdate)
|
|
return;
|
|
|
|
// Update camera
|
|
bool useMovementSpeed = false;
|
|
if (_camera != null)
|
|
{
|
|
_camera.Update(deltaTime);
|
|
useMovementSpeed = _camera.UseMovementSpeed;
|
|
|
|
if (_cameraButton != null)
|
|
_cameraButton.Parent.Visible = useMovementSpeed;
|
|
}
|
|
|
|
// Get parent window
|
|
var win = (WindowRootControl)Root;
|
|
|
|
// Get current mouse position in the view
|
|
{
|
|
// When the window is not focused, the position in window does not return sane values
|
|
Float2 pos = PointFromWindow(win.MousePosition);
|
|
if (!float.IsInfinity(pos.LengthSquared))
|
|
_viewMousePos = pos;
|
|
}
|
|
|
|
// Update input
|
|
var window = win.Window;
|
|
var canUseInput = window != null && window.IsFocused && window.IsForegroundWindow;
|
|
{
|
|
// Get input buttons and keys (skip if viewport has no focus or mouse is over a child control)
|
|
var isViewportControllingMouse = canUseInput && IsControllingMouse;
|
|
if (isViewportControllingMouse != _isViewportControllingMouse)
|
|
{
|
|
_isViewportControllingMouse = isViewportControllingMouse;
|
|
if (isViewportControllingMouse)
|
|
StartMouseCapture();
|
|
else
|
|
EndMouseCapture();
|
|
}
|
|
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
|
|
_prevInput = _input;
|
|
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
|
|
if (canUseInput && ContainsFocus && hit == null)
|
|
_input.Gather(win.Window, useMouse, ref _prevInput);
|
|
else
|
|
_input.Clear();
|
|
|
|
// Track controlling mouse state change
|
|
bool wasControllingMouse = _prevInput.IsControllingMouse;
|
|
_isControllingMouse = _input.IsControllingMouse;
|
|
|
|
// Simulate holding mouse right down for trackpad users
|
|
if ((_prevInput.IsMouseRightDown && !_input.IsMouseRightDown) || win.GetKeyDown(KeyboardKeys.Escape))
|
|
_isVirtualMouseRightDown = false; // Cancel when mouse right or escape is pressed
|
|
if (_wasVirtualMouseRightDown)
|
|
wasControllingMouse = true;
|
|
if (_isVirtualMouseRightDown)
|
|
_isControllingMouse = _isVirtualMouseRightDown;
|
|
|
|
if (wasControllingMouse != _isControllingMouse)
|
|
{
|
|
if (_isControllingMouse)
|
|
OnControlMouseBegin(win.Window);
|
|
else
|
|
OnControlMouseEnd(win.Window);
|
|
}
|
|
|
|
// Track mouse buttons state change
|
|
if (!_prevInput.IsMouseLeftDown && _input.IsMouseLeftDown)
|
|
OnLeftMouseButtonDown();
|
|
else if (_prevInput.IsMouseLeftDown && !_input.IsMouseLeftDown)
|
|
OnLeftMouseButtonUp();
|
|
|
|
if ((!_prevInput.IsMouseRightDown && _input.IsMouseRightDown) || (!_wasVirtualMouseRightDown && _isVirtualMouseRightDown))
|
|
OnRightMouseButtonDown();
|
|
else if ((_prevInput.IsMouseRightDown && !_input.IsMouseRightDown) || (_wasVirtualMouseRightDown && !_isVirtualMouseRightDown))
|
|
OnRightMouseButtonUp();
|
|
|
|
if (!_prevInput.IsMouseMiddleDown && _input.IsMouseMiddleDown)
|
|
OnMiddleMouseButtonDown();
|
|
else if (_prevInput.IsMouseMiddleDown && !_input.IsMouseMiddleDown)
|
|
OnMiddleMouseButtonUp();
|
|
|
|
_wasVirtualMouseRightDown = _isVirtualMouseRightDown;
|
|
}
|
|
|
|
// Get clamped delta time (more stable during lags)
|
|
var dt = Math.Min(Time.UnscaledDeltaTime, 1.0f);
|
|
|
|
// Check if update mouse
|
|
var size = Size;
|
|
var options = Editor.Instance.Options.Options;
|
|
if (_isControllingMouse)
|
|
{
|
|
var rmbWheel = false;
|
|
|
|
// Gather input
|
|
{
|
|
bool isAltDown = _input.IsAltDown;
|
|
bool lbDown = _input.IsMouseLeftDown;
|
|
bool mbDown = _input.IsMouseMiddleDown;
|
|
bool rbDown = _input.IsMouseRightDown || _isVirtualMouseRightDown;
|
|
bool wheelInUse = Math.Abs(_input.MouseWheelDelta) > Mathf.Epsilon;
|
|
|
|
_input.IsPanning = !isAltDown && mbDown && !rbDown;
|
|
_input.IsRotating = !isAltDown && !mbDown && rbDown;
|
|
_input.IsMoving = !isAltDown && mbDown && rbDown;
|
|
_input.IsZooming = wheelInUse && !(_input.IsShiftDown || (!ContainsFocus && FlaxEngine.Input.GetKey(KeyboardKeys.Shift)));
|
|
_input.IsOrbiting = isAltDown && lbDown && !mbDown && !rbDown;
|
|
|
|
// Control move speed with RMB+Wheel
|
|
rmbWheel = useMovementSpeed && (_input.IsMouseRightDown || _isVirtualMouseRightDown) && wheelInUse;
|
|
if (rmbWheel)
|
|
{
|
|
var step = _input.MouseWheelDelta * options.Viewport.MouseWheelSensitivity;
|
|
AdjustCameraMoveSpeed(step > 0.0f ? 1 : -1);
|
|
}
|
|
}
|
|
|
|
// Get input movement
|
|
var moveDelta = Vector3.Zero;
|
|
if (win.GetKey(options.Input.Forward.Key))
|
|
{
|
|
moveDelta += Vector3.Forward;
|
|
}
|
|
if (win.GetKey(options.Input.Backward.Key))
|
|
{
|
|
moveDelta += Vector3.Backward;
|
|
}
|
|
if (win.GetKey(options.Input.Right.Key))
|
|
{
|
|
moveDelta += Vector3.Right;
|
|
}
|
|
if (win.GetKey(options.Input.Left.Key))
|
|
{
|
|
moveDelta += Vector3.Left;
|
|
}
|
|
if (win.GetKey(options.Input.Up.Key))
|
|
{
|
|
moveDelta += Vector3.Up;
|
|
}
|
|
if (win.GetKey(options.Input.Down.Key))
|
|
{
|
|
moveDelta += Vector3.Down;
|
|
}
|
|
moveDelta.Normalize(); // normalize direction
|
|
moveDelta *= _movementSpeed;
|
|
|
|
// Speed up or speed down
|
|
if (_input.IsShiftDown)
|
|
moveDelta *= 4.0f;
|
|
if (_input.IsControlDown)
|
|
moveDelta *= 0.3f;
|
|
|
|
// Calculate smooth mouse delta not dependant on viewport size
|
|
var offset = _viewMousePos - _startPos;
|
|
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown)
|
|
{
|
|
offset = Float2.Zero;
|
|
}
|
|
|
|
var mouseDelta = Float2.Zero;
|
|
if (_useMouseFiltering)
|
|
{
|
|
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
|
|
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
|
|
_mouseDelta = offset;
|
|
|
|
// Update delta filtering buffer
|
|
_deltaFilteringBuffer[_deltaFilteringStep] = _mouseDelta;
|
|
_deltaFilteringStep++;
|
|
|
|
// If the step is too far, zero
|
|
if (_deltaFilteringStep == FpsCameraFilteringFrames)
|
|
_deltaFilteringStep = 0;
|
|
|
|
// Calculate filtered delta (avg)
|
|
for (int i = 0; i < FpsCameraFilteringFrames; i++)
|
|
mouseDelta += _deltaFilteringBuffer[i];
|
|
|
|
mouseDelta /= FpsCameraFilteringFrames;
|
|
}
|
|
else
|
|
{
|
|
_mouseDelta = offset;
|
|
mouseDelta = _mouseDelta;
|
|
}
|
|
|
|
if (_useMouseAcceleration)
|
|
{
|
|
// Accelerate the delta
|
|
var currentDelta = mouseDelta;
|
|
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
|
|
_mouseDeltaLast = currentDelta;
|
|
}
|
|
|
|
// Update
|
|
moveDelta *= dt * (60.0f * 4.0f);
|
|
mouseDelta *= 0.1833f * MouseSpeed * _mouseSensitivity;
|
|
if (options.Viewport.InvertMouseYAxisRotation)
|
|
mouseDelta *= new Float2(1, -1);
|
|
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
|
|
|
|
// Move mouse back to the root position
|
|
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown))
|
|
{
|
|
var center = PointToWindow(_startPos);
|
|
win.MousePosition = center;
|
|
}
|
|
|
|
// Change Ortho size on mouse scroll
|
|
if (_isOrtho && !rmbWheel)
|
|
{
|
|
var scroll = _input.MouseWheelDelta;
|
|
if (scroll > Mathf.Epsilon || scroll < -Mathf.Epsilon)
|
|
_orthoSize -= scroll * options.Viewport.MouseWheelSensitivity * 0.2f * _orthoSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
|
|
{
|
|
// Calculate smooth mouse delta not dependant on viewport size
|
|
var offset = _viewMousePos - _startPos;
|
|
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
|
|
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
|
|
_mouseDelta = offset;
|
|
_startPos = _viewMousePos;
|
|
}
|
|
else
|
|
{
|
|
_mouseDelta = Float2.Zero;
|
|
}
|
|
_mouseDeltaLast = Float2.Zero;
|
|
|
|
if (ContainsFocus)
|
|
{
|
|
_input.IsPanning = false;
|
|
_input.IsRotating = false;
|
|
_input.IsMoving = true;
|
|
_input.IsZooming = false;
|
|
_input.IsOrbiting = false;
|
|
|
|
// Get input movement
|
|
var moveDelta = Vector3.Zero;
|
|
var mouseDelta = Float2.Zero;
|
|
if (FlaxEngine.Input.GamepadsCount > 0)
|
|
{
|
|
// Gamepads handling
|
|
moveDelta += new Vector3(GetGamepadAxis(GamepadAxis.LeftStickX), 0, GetGamepadAxis(GamepadAxis.LeftStickY));
|
|
mouseDelta += new Float2(GetGamepadAxis(GamepadAxis.RightStickX), -GetGamepadAxis(GamepadAxis.RightStickY));
|
|
_input.IsRotating |= !mouseDelta.IsZero;
|
|
}
|
|
if (win.GetKey(KeyboardKeys.ArrowRight))
|
|
{
|
|
moveDelta += Vector3.Right;
|
|
}
|
|
if (win.GetKey(KeyboardKeys.ArrowLeft))
|
|
{
|
|
moveDelta += Vector3.Left;
|
|
}
|
|
if (win.GetKey(KeyboardKeys.ArrowUp))
|
|
{
|
|
moveDelta += Vector3.Up;
|
|
}
|
|
if (win.GetKey(KeyboardKeys.ArrowDown))
|
|
{
|
|
moveDelta += Vector3.Down;
|
|
}
|
|
moveDelta.Normalize();
|
|
moveDelta *= _movementSpeed;
|
|
if (FlaxEngine.Input.GamepadsCount > 0)
|
|
moveDelta *= Mathf.Remap(GetGamepadAxis(GamepadAxis.RightTrigger), 0, 1, 1, 4.0f);
|
|
|
|
// Update
|
|
moveDelta *= dt * (60.0f * 4.0f);
|
|
UpdateView(dt, ref moveDelta, ref mouseDelta, out _);
|
|
}
|
|
}
|
|
|
|
_input.MouseWheelDelta = 0;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool OnMouseDown(Float2 location, MouseButton button)
|
|
{
|
|
Focus();
|
|
|
|
base.OnMouseDown(location, button);
|
|
return true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool OnMouseWheel(Float2 location, float delta)
|
|
{
|
|
_input.MouseWheelDelta += delta;
|
|
|
|
return base.OnMouseWheel(location, delta);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnChildResized(Control control)
|
|
{
|
|
base.OnChildResized(control);
|
|
|
|
PerformLayout();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool OnKeyDown(KeyboardKeys key)
|
|
{
|
|
// Base
|
|
if (base.OnKeyDown(key))
|
|
return true;
|
|
|
|
// Custom input events
|
|
return InputActions.Process(Editor.Instance, this, key);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void PerformLayoutBeforeChildren()
|
|
{
|
|
base.PerformLayoutBeforeChildren();
|
|
|
|
ViewportWidgetsContainer.ArrangeWidgets(this);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Draw()
|
|
{
|
|
base.Draw();
|
|
|
|
// Add overlay during debugger breakpoint hang
|
|
if (Editor.Instance.Simulation.IsDuringBreakpointHang)
|
|
{
|
|
var bounds = new Rectangle(Float2.Zero, Size);
|
|
Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f));
|
|
Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnLostFocus()
|
|
{
|
|
base.OnLostFocus();
|
|
|
|
if (_isControllingMouse)
|
|
{
|
|
OnControlMouseEnd(RootWindow.Window);
|
|
_isControllingMouse = false;
|
|
_isVirtualMouseRightDown = false;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnDestroy()
|
|
{
|
|
Editor.Instance.Options.OptionsChanged -= OnEditorOptionsChanged;
|
|
|
|
base.OnDestroy();
|
|
}
|
|
|
|
private struct CameraViewpoint
|
|
{
|
|
public readonly string Name;
|
|
public readonly Float3 Orientation;
|
|
|
|
public CameraViewpoint(string name, Vector3 orientation)
|
|
{
|
|
Name = name;
|
|
Orientation = orientation;
|
|
}
|
|
}
|
|
|
|
private readonly CameraViewpoint[] CameraViewpointValues =
|
|
{
|
|
new CameraViewpoint("Front", new Float3(0, 180, 0)),
|
|
new CameraViewpoint("Back", new Float3(0, 0, 0)),
|
|
new CameraViewpoint("Left", new Float3(0, 90, 0)),
|
|
new CameraViewpoint("Right", new Float3(0, -90, 0)),
|
|
new CameraViewpoint("Top", new Float3(90, 0, 0)),
|
|
new CameraViewpoint("Bottom", new Float3(-90, 0, 0))
|
|
};
|
|
|
|
private struct ViewModeOptions
|
|
{
|
|
public readonly string Name;
|
|
public readonly ViewMode Mode;
|
|
public readonly ViewModeOptions[] Options;
|
|
|
|
public ViewModeOptions(ViewMode mode, string name)
|
|
{
|
|
Mode = mode;
|
|
Name = name;
|
|
Options = null;
|
|
}
|
|
|
|
public ViewModeOptions(string name, ViewModeOptions[] options)
|
|
{
|
|
Name = name;
|
|
Mode = ViewMode.Default;
|
|
Options = options;
|
|
}
|
|
}
|
|
|
|
private static readonly ViewModeOptions[] ViewModeValues =
|
|
{
|
|
new ViewModeOptions(ViewMode.Default, "Default"),
|
|
new ViewModeOptions(ViewMode.Unlit, "Unlit"),
|
|
new ViewModeOptions(ViewMode.NoPostFx, "No PostFx"),
|
|
new ViewModeOptions(ViewMode.Wireframe, "Wireframe"),
|
|
new ViewModeOptions(ViewMode.LightBuffer, "Light Buffer"),
|
|
new ViewModeOptions(ViewMode.Reflections, "Reflections Buffer"),
|
|
new ViewModeOptions(ViewMode.Depth, "Depth Buffer"),
|
|
new ViewModeOptions("GBuffer", new[]
|
|
{
|
|
new ViewModeOptions(ViewMode.Diffuse, "Diffuse"),
|
|
new ViewModeOptions(ViewMode.Metalness, "Metalness"),
|
|
new ViewModeOptions(ViewMode.Roughness, "Roughness"),
|
|
new ViewModeOptions(ViewMode.Specular, "Specular"),
|
|
new ViewModeOptions(ViewMode.SpecularColor, "Specular Color"),
|
|
new ViewModeOptions(ViewMode.SubsurfaceColor, "Subsurface Color"),
|
|
new ViewModeOptions(ViewMode.ShadingModel, "Shading Model"),
|
|
new ViewModeOptions(ViewMode.Emissive, "Emissive Light"),
|
|
new ViewModeOptions(ViewMode.Normals, "Normals"),
|
|
new ViewModeOptions(ViewMode.AmbientOcclusion, "Ambient Occlusion"),
|
|
}),
|
|
new ViewModeOptions(ViewMode.MotionVectors, "Motion Vectors"),
|
|
new ViewModeOptions(ViewMode.LightmapUVsDensity, "Lightmap UVs Density"),
|
|
new ViewModeOptions(ViewMode.VertexColors, "Vertex Colors"),
|
|
new ViewModeOptions(ViewMode.PhysicsColliders, "Physics Colliders"),
|
|
new ViewModeOptions(ViewMode.LODPreview, "LOD Preview"),
|
|
new ViewModeOptions(ViewMode.MaterialComplexity, "Material Complexity"),
|
|
new ViewModeOptions(ViewMode.QuadOverdraw, "Quad Overdraw"),
|
|
new ViewModeOptions(ViewMode.GlobalSDF, "Global SDF"),
|
|
new ViewModeOptions(ViewMode.GlobalSurfaceAtlas, "Global Surface Atlas"),
|
|
new ViewModeOptions(ViewMode.GlobalIllumination, "Global Illumination"),
|
|
};
|
|
|
|
private void WidgetViewModeShowHideClicked(ContextMenuButton button)
|
|
{
|
|
if (button.Tag is ViewMode v)
|
|
{
|
|
Task.ViewMode = v;
|
|
var cm = button.ParentContextMenu;
|
|
WidgetViewModeShowHide(cm);
|
|
var mainCM = ViewWidgetButtonMenu.GetChildMenu("Debug View").ContextMenu;
|
|
if (mainCM != null && cm != mainCM)
|
|
WidgetViewModeShowHide(mainCM);
|
|
}
|
|
}
|
|
|
|
private void WidgetViewModeShowHide(Control cm)
|
|
{
|
|
if (cm.Visible == false)
|
|
return;
|
|
|
|
var ccm = (ContextMenu)cm;
|
|
foreach (var e in ccm.Items)
|
|
{
|
|
if (e is ContextMenuButton b && b.Tag is ViewMode v)
|
|
b.Icon = Task.ViewMode == v ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
|
}
|
|
}
|
|
|
|
private struct ViewFlagOptions
|
|
{
|
|
public readonly ViewFlags Mode;
|
|
public readonly string Name;
|
|
|
|
public ViewFlagOptions(ViewFlags mode, string name)
|
|
{
|
|
Mode = mode;
|
|
Name = name;
|
|
}
|
|
}
|
|
|
|
private static readonly ViewFlagOptions[] ViewFlagsValues =
|
|
{
|
|
new ViewFlagOptions(ViewFlags.AntiAliasing, "Anti Aliasing"),
|
|
new ViewFlagOptions(ViewFlags.Shadows, "Shadows"),
|
|
new ViewFlagOptions(ViewFlags.EditorSprites, "Editor Sprites"),
|
|
new ViewFlagOptions(ViewFlags.Reflections, "Reflections"),
|
|
new ViewFlagOptions(ViewFlags.SSR, "Screen Space Reflections"),
|
|
new ViewFlagOptions(ViewFlags.AO, "Ambient Occlusion"),
|
|
new ViewFlagOptions(ViewFlags.GI, "Global Illumination"),
|
|
new ViewFlagOptions(ViewFlags.DirectionalLights, "Directional Lights"),
|
|
new ViewFlagOptions(ViewFlags.PointLights, "Point Lights"),
|
|
new ViewFlagOptions(ViewFlags.SpotLights, "Spot Lights"),
|
|
new ViewFlagOptions(ViewFlags.SkyLights, "Sky Lights"),
|
|
new ViewFlagOptions(ViewFlags.Sky, "Sky"),
|
|
new ViewFlagOptions(ViewFlags.Fog, "Fog"),
|
|
new ViewFlagOptions(ViewFlags.SpecularLight, "Specular Light"),
|
|
new ViewFlagOptions(ViewFlags.Decals, "Decals"),
|
|
new ViewFlagOptions(ViewFlags.CustomPostProcess, "Custom Post Process"),
|
|
new ViewFlagOptions(ViewFlags.Bloom, "Bloom"),
|
|
new ViewFlagOptions(ViewFlags.ToneMapping, "Tone Mapping"),
|
|
new ViewFlagOptions(ViewFlags.EyeAdaptation, "Eye Adaptation"),
|
|
new ViewFlagOptions(ViewFlags.CameraArtifacts, "Camera Artifacts"),
|
|
new ViewFlagOptions(ViewFlags.LensFlares, "Lens Flares"),
|
|
new ViewFlagOptions(ViewFlags.DepthOfField, "Depth of Field"),
|
|
new ViewFlagOptions(ViewFlags.MotionBlur, "Motion Blur"),
|
|
new ViewFlagOptions(ViewFlags.ContactShadows, "Contact Shadows"),
|
|
new ViewFlagOptions(ViewFlags.PhysicsDebug, "Physics Debug"),
|
|
new ViewFlagOptions(ViewFlags.LightsDebug, "Lights Debug"),
|
|
new ViewFlagOptions(ViewFlags.DebugDraw, "Debug Draw"),
|
|
};
|
|
|
|
private void WidgetViewFlagsShowHide(Control cm)
|
|
{
|
|
if (cm.Visible == false)
|
|
return;
|
|
var ccm = (ContextMenu)cm;
|
|
foreach (var e in ccm.Items)
|
|
{
|
|
if (e is ContextMenuButton b && b.Tag != null)
|
|
{
|
|
var v = (ViewFlags)b.Tag;
|
|
b.Icon = (Task.View.Flags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void WidgetViewLayersShowHide(Control cm)
|
|
{
|
|
if (cm.Visible == false)
|
|
return;
|
|
var ccm = (ContextMenu)cm;
|
|
var layersMask = Task.ViewLayersMask;
|
|
foreach (var e in ccm.Items)
|
|
{
|
|
if (e is ContextMenuButton b && b != null && b.Tag != null)
|
|
{
|
|
int layerIndex = (int)b.Tag;
|
|
LayersMask mask = new LayersMask(layerIndex);
|
|
b.Icon = (layersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
|
}
|
|
}
|
|
}
|
|
|
|
private float GetGamepadAxis(GamepadAxis axis)
|
|
{
|
|
var value = FlaxEngine.Input.GetGamepadAxis(InputGamepadIndex.All, axis);
|
|
var deadZone = 0.2f;
|
|
return value >= deadZone || value <= -deadZone ? value : 0.0f;
|
|
}
|
|
}
|
|
}
|