// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System.Collections.Generic; using FlaxEditor.Gizmo; using FlaxEditor.SceneGraph; using FlaxEngine; namespace FlaxEditor.Viewport.Cameras { /// /// Implementation of that simulated the first-person camera which can fly though the scene. /// /// [HideInEditor] public class FPSCamera : ViewportCamera { private Transform _startMove; private Transform _endMove; private float _moveStartTime = -1; /// /// Gets a value indicating whether this viewport is animating movement. /// public bool IsAnimatingMove => _moveStartTime > Mathf.Epsilon; /// /// The target point location. It's used to orbit around it whe user clicks Alt+LMB. /// public Vector3 TargetPoint = new Vector3(-200); /// /// Sets view. /// /// The view position. /// The view direction. public void SetView(Vector3 position, Vector3 direction) { if (IsAnimatingMove) return; // Rotate and move Viewport.ViewPosition = position; Viewport.ViewDirection = direction; } /// /// Sets view. /// /// The view position. /// The view rotation. public void SetView(Vector3 position, Quaternion orientation) { if (IsAnimatingMove) return; // Rotate and move Viewport.ViewPosition = position; Viewport.ViewOrientation = orientation; } /// /// Start animating viewport movement to the target transformation. /// /// The target position. /// The target orientation. public void MoveViewport(Vector3 position, Quaternion orientation) { MoveViewport(new Transform(position, orientation)); } /// /// Start animating viewport movement to the target transformation. /// /// The target transform. public void MoveViewport(Transform target) { _startMove = Viewport.ViewTransform; _endMove = target; _moveStartTime = Time.UnscaledGameTime; } /// /// Moves the viewport to visualize the actor. /// /// The actor to preview. public void ShowActor(Actor actor) { BoundingSphere sphere; Editor.GetActorEditorSphere(actor, out sphere); ShowSphere(ref sphere); } /// /// Moves the viewport to visualize selected actors. /// /// The actors to show. public void ShowActors(List actors) { if (actors.Count == 0) return; BoundingSphere mergesSphere = BoundingSphere.Empty; for (int i = 0; i < actors.Count; i++) { if (actors[i] is ActorNode actor) { BoundingSphere sphere; Editor.GetActorEditorSphere(actor.Actor, out sphere); BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere); } } ShowSphere(ref mergesSphere); } private void ShowSphere(ref BoundingSphere sphere) { // Calculate view transform Quaternion orientation = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f); Vector3 position = sphere.Center - Vector3.Forward * orientation * (sphere.Radius * 2.5f); // Move viewport TargetPoint = sphere.Center; MoveViewport(position, orientation); } /// public override void Update(float deltaTime) { // Update animated movement if (IsAnimatingMove) { // Calculate linear progress float animationDuration = 0.5f; float time = Time.UnscaledGameTime; float progress = (time - _moveStartTime) / animationDuration; // Check for end if (progress >= 1.0f) { // Animation has been finished _moveStartTime = -1; } // Animate camera float a = Mathf.Saturate(progress); a = a * a * a; Transform targetTransform = Transform.Lerp(_startMove, _endMove, a); targetTransform.Scale = Vector3.Zero; Viewport.ViewPosition = targetTransform.Translation; Viewport.ViewOrientation = targetTransform.Orientation; } } /// public override void UpdateView(float dt, ref Vector3 moveDelta, ref Vector2 mouseDelta, out bool centerMouse) { centerMouse = true; if (IsAnimatingMove) return; Viewport.GetInput(out var input); Viewport.GetPrevInput(out var prevInput); var mainViewport = Viewport as MainEditorGizmoViewport; bool isUsingGizmo = mainViewport != null && mainViewport.TransformGizmo.ActiveAxis != TransformGizmo.Axis.None; // Get current view properties float yaw = Viewport.Yaw; float pitch = Viewport.Pitch; var position = Viewport.ViewPosition; var rotation = Viewport.ViewOrientation; // Compute base vectors for camera movement var forward = Vector3.Forward * rotation; var up = Vector3.Up * rotation; var right = Vector3.Cross(forward, up); // Dolly if (input.IsPanning || input.IsMoving || input.IsRotating) { Vector3 move; Vector3.Transform(ref moveDelta, ref rotation, out move); position += move; } // Pan if (input.IsPanning) { var panningSpeed = 0.8f; position -= right * (mouseDelta.X * panningSpeed); position -= up * (mouseDelta.Y * panningSpeed); } // Move if (input.IsMoving) { // Move camera over XZ plane var projectedForward = Vector3.Normalize(new Vector3(forward.X, 0, forward.Z)); position -= projectedForward * mouseDelta.Y; yaw += mouseDelta.X; } // Rotate or orbit if (input.IsRotating || (input.IsOrbiting && !isUsingGizmo && prevInput.IsOrbiting)) { yaw += mouseDelta.X; pitch += mouseDelta.Y; } // Zoom in/out if (input.IsZooming && !input.IsRotating) { position += forward * (Viewport.MouseWheelZoomSpeedFactor * input.MouseWheelDelta * 25.0f); if (input.IsAltDown) { position += forward * (Viewport.MouseSpeed * 40 * Viewport.MouseDeltaRight.ValuesSum); } } // Move camera with the gizmo if (input.IsOrbiting && isUsingGizmo) { centerMouse = false; Viewport.ViewPosition += mainViewport.TransformGizmo.LastDelta.Translation; return; } // Update view Viewport.Yaw = yaw; Viewport.Pitch = pitch; if (input.IsOrbiting) { float orbitRadius = Vector3.Distance(ref position, ref TargetPoint); Vector3 localPosition = Viewport.ViewDirection * (-1 * orbitRadius); Viewport.ViewPosition = TargetPoint + localPosition; } else { TargetPoint += position - Viewport.ViewPosition; Viewport.ViewPosition = position; } } } }