Ejemplo n.º 1
0
        /// <summary>
        /// Performs a camera focus operation on the currently selected game objects. The
        /// method uses the current focs settings to perform the focus operation.
        /// </summary>
        /// <remarks>
        /// The method has no effect if there are no objects currently selected.
        /// </remarks>
        public void FocusOnSelection()
        {
            // Focussing on the default object is not selected in constant and smooth focus mode
            if (EditorObjectSelection.Instance.NumberOfSelectedObjects == 0 &&
                (_focusSettings.FocusMode == EditorCameraFocusMode.ConstantSpeed || _focusSettings.FocusMode == EditorCameraFocusMode.Smooth)
                )
            {
                return;
            }

            // Focus the camera based on the chosen focus method
            if (_focusSettings.FocusMode == EditorCameraFocusMode.Instant)
            {
                //Select the default focus object
                bool selectedDefaultFocusObject = false;
                if (EditorObjectSelection.Instance.NumberOfSelectedObjects == 0)
                {
                    selectedDefaultFocusObject = EditorObjectSelection.Instance.AddObjectAndItsChildrenToSelection(_focusSettings.DefaultFocusObject);
                }

                //EditorObjectSelection.Instance.AddObjectToSelection(_focusSettings.DefaultFocusObject, false);
                // Get the focus info
                EditorCameraFocusOperationInfo focusOpInfo = EditorCameraFocus.GetFocusOperationInfo(Camera, _focusSettings);

                // Note: We will also adjust the ortho size to make sure things are all well when
                //       switching from a perspective to orthographic camera.
                Camera.orthographicSize   = focusOpInfo.OrthoCameraHalfVerticalSize;
                Camera.transform.position = focusOpInfo.CameraDestinationPosition;

                // The camera was focused
                _wasFocused     = true;
                _lastFocusPoint = focusOpInfo.FocusPoint;
                CalculateOrbitOffsetAlongLook(focusOpInfo);

                // Deselect the default focus object
                if (selectedDefaultFocusObject)
                {
                    /// Undo select all
                    EditorUndoRedoSystem.Instance.OnUndo();
                }
            }
            else
            if (_focusSettings.FocusMode == EditorCameraFocusMode.ConstantSpeed)
            {
                StopCoroutine("StartConstantFocusOnSelection");
                StopCoroutine("StartSmoothZoom");
                StopCoroutine("StartSmoothPan");

                StartCoroutine("StartConstantFocusOnSelection");
            }
            else
            if (_focusSettings.FocusMode == EditorCameraFocusMode.Smooth)
            {
                StopCoroutine("StartSmoothFocusOnSelection");
                StopCoroutine("StartSmoothZoom");
                StopCoroutine("StartSmoothPan");

                StartCoroutine("StartSmoothFocusOnSelection");
            }
        }
        /// <summary>
        /// Returns an instance of the 'EditorCameraFocusOperationInfo' which holds data that can be
        /// used to perform a camera focus operation.
        /// </summary>
        /// <param name="camera">
        /// The camera which must be involved in the focus operation.
        /// </param>
        /// <param name="focusSettings">
        /// All calculations will be performed based on these settings.
        /// </param>
        public static EditorCameraFocusOperationInfo GetFocusOperationInfo(Camera camera, EditorCameraFocusSettings focusSettings)
        {
            // Retrieve the selection world space AABB
            EditorObjectSelection objectSelection = EditorObjectSelection.Instance;
            Bounds selectionWorldAABB             = objectSelection.GetWorldBox().ToBounds();

            // We will establish the camera destination position by moving the camera along the reverse of its look vector
            // starting from the center of the world AABB by a distance equal to the maximum AABB size component.
            float maxAABBComponent = selectionWorldAABB.size.x;

            if (maxAABBComponent < selectionWorldAABB.size.y)
            {
                maxAABBComponent = selectionWorldAABB.size.y;
            }
            if (maxAABBComponent < selectionWorldAABB.size.z)
            {
                maxAABBComponent = selectionWorldAABB.size.z;
            }

            // Construct the focus operation info and return it to the caller
            EditorCameraFocusOperationInfo focusOpInfo = new EditorCameraFocusOperationInfo();

            focusOpInfo.CameraDestinationPosition = selectionWorldAABB.center - camera.transform.forward * maxAABBComponent * focusSettings.FocusDistanceScale;
            focusOpInfo.FocusPoint = selectionWorldAABB.center;

            // Now we need to calculate the ortho size that the camera should have to achieve the focus effect.
            // We will calculate the size in such a way that a 1 unit cube fits inside a volume of height = 1.
            // In this case our cube side length is equal to 'maxAABBComponent'. So we will have to make sure
            // that fits.
            // Note: We multiply by 'focusSettings.FocusDistanceScale' because the further away from the focus
            //       point the camera is, the bigger the view volume.
            focusOpInfo.OrthoCameraHalfVerticalSize = maxAABBComponent * 0.5f * focusSettings.FocusDistanceScale;

            return(focusOpInfo);
        }
        /// <summary>
        /// Starts a smooth focus operation on the current object selection.
        /// </summary>
        private IEnumerator StartSmoothFocusOnSelection()
        {
            // Store needed data
            EditorCameraFocusOperationInfo focusOpInfo = EditorCameraFocus.GetFocusOperationInfo(Camera, _focusSettings);

            _lastFocusPoint = focusOpInfo.FocusPoint;
            Vector3   cameraDestinationPoint = focusOpInfo.CameraDestinationPosition;
            Transform cameraTransform        = Camera.transform;

            // If the distance to travel is small enough, we can exit
            if ((cameraDestinationPoint - cameraTransform.position).magnitude < 1e-4f)
            {
                yield break;
            }

            // We will need this to modify the position using 'Vector3.SmoothDamp'
            Vector3 velocity = Vector3.zero;

            // We will need this to modify the camera ortho size using 'Mathf.SmoothDamp'.
            float orthoSizeVelocity = 0.0f;

            // The first iteration of the 'while' loop will perform the first focus step. We will
            // set this to true here just in case the focus operation is cancelled by another camera
            // operation. This will allow the user to orbit the camera even if it wasn't focused 100%.
            _wasFocused = true;

            while (true)
            {
                // Calculate the new position
                cameraTransform.position = Vector3.SmoothDamp(cameraTransform.position, cameraDestinationPoint, ref velocity, _focusSettings.SmoothFocusTime);

                // Calculate the new camera ortho size
                SetOrthoSize(Mathf.SmoothDamp(Camera.orthographicSize, focusOpInfo.OrthoCameraHalfVerticalSize, ref orthoSizeVelocity, _focusSettings.SmoothFocusTime));

                // Recalculate the orbit focus to ensure proper orbit if the focus operation is not completed 100%.
                CalculateOrbitOffsetAlongLook(focusOpInfo);

                // If the position is close enough to the target position and the camera ortho size
                // is close enough to the target size, we can exit the loop.
                if ((cameraTransform.position - cameraDestinationPoint).magnitude < 1e-3f &&
                    Mathf.Abs(Camera.orthographicSize - focusOpInfo.OrthoCameraHalfVerticalSize) < 1e-3f)
                {
                    // Clamp to make sure we got the correct values and then exit the loop
                    cameraTransform.position = cameraDestinationPoint;
                    Camera.orthographicSize  = focusOpInfo.OrthoCameraHalfVerticalSize;

                    break;
                }

                yield return(null);
            }
        }
        /// <summary>
        /// Performs a camera focus operation on the currently selected game objects. The
        /// method uses the current focs settings to perform the focus operation.
        /// </summary>
        /// <remarks>
        /// The method has no effect if there are no objects currently selected.
        /// </remarks>
        public void FocusOnSelection()
        {
            // No objects selected?
            if (EditorObjectSelection.Instance.NumberOfSelectedObjects == 0)
            {
                return;
            }

            // Focus the camera based on the chosen focus method
            if (_focusSettings.FocusMode == EditorCameraFocusMode.Instant)
            {
                // Get the focus info
                EditorCameraFocusOperationInfo focusOpInfo = EditorCameraFocus.GetFocusOperationInfo(Camera, _focusSettings);

                // Note: We will also adjust the ortho size to make sure things are all well when
                //       switching from a perspective to orthographic camera.
                Camera.orthographicSize   = focusOpInfo.OrthoCameraHalfVerticalSize;
                Camera.transform.position = focusOpInfo.CameraDestinationPosition;

                // The camera was focused
                _wasFocused     = true;
                _lastFocusPoint = focusOpInfo.FocusPoint;
                CalculateOrbitOffsetAlongLook(focusOpInfo);
            }
            else
            if (_focusSettings.FocusMode == EditorCameraFocusMode.ConstantSpeed)
            {
                StopCoroutine("StartConstantFocusOnSelection");
                StopCoroutine("StartSmoothZoom");
                StopCoroutine("StartSmoothPan");

                StartCoroutine("StartConstantFocusOnSelection");
            }
            else
            if (_focusSettings.FocusMode == EditorCameraFocusMode.Smooth)
            {
                StopCoroutine("StartSmoothFocusOnSelection");
                StopCoroutine("StartSmoothZoom");
                StopCoroutine("StartSmoothPan");

                StartCoroutine("StartSmoothFocusOnSelection");
            }
        }
        /// <summary>
        /// Starts a constant focus operation on the current object selection.
        /// </summary>
        private IEnumerator StartConstantFocusOnSelection()
        {
            // Store needed data
            EditorCameraFocusOperationInfo focusOpInfo = EditorCameraFocus.GetFocusOperationInfo(Camera, _focusSettings);

            _lastFocusPoint = focusOpInfo.FocusPoint;
            Vector3   cameraDestinationPoint = focusOpInfo.CameraDestinationPosition;
            float     cameraSpeed            = _focusSettings.ConstantFocusSpeed;
            Transform cameraTransform        = Camera.transform;

            // Calculate the vector which is used to move the camera from its current position to the destination position.
            // We will normalize this vector so that we can move along it with the required speed.
            Vector3 fromCamPosToDestination = cameraDestinationPoint - cameraTransform.position;
            float   distanceToTravel        = fromCamPosToDestination.magnitude; // Needed later inside the 'while' loop

            if (distanceToTravel < 1e-4f)
            {
                yield break;                                                 // No focus necessary?
            }
            fromCamPosToDestination.Normalize();

            // We will need this to know how much we travelled
            Vector3 initialCameraPosition = cameraTransform.position;

            // We will need this to adjust the ortho camera size
            float initialCameraOrthoSize = Camera.orthographicSize;

            // The first iteration of the 'while' loop will perform the first focus step. We will
            // set this to true here just in case the focus operation is cancelled by another camera
            // operation. This will allow the user to orbit the camera even if it wasn't focused 100%.
            _wasFocused = true;

            while (true)
            {
                // Move the camera along the direction vector with the desired speed
                cameraTransform.position += (fromCamPosToDestination * cameraSpeed * Time.deltaTime);

                // Calculate the new camera ortho size. This is done by lerping between the initial
                // ortho size and the target size. The interpolation factor is the ratio between how
                // much we travelled so far and the total distance that we have to travel.
                float distanceTraveledSoFar = (cameraTransform.position - initialCameraPosition).magnitude;
                SetOrthoSize(Mathf.Lerp(initialCameraOrthoSize, focusOpInfo.OrthoCameraHalfVerticalSize, distanceTraveledSoFar / distanceToTravel));

                // Recalculate the orbit focus to ensure proper orbit if the focus operation is not completed 100%.
                CalculateOrbitOffsetAlongLook(focusOpInfo);

                // Check if the camera has reached the destination position or if it went past it. It is very
                // probable that most of the times, the camera will move further away than the target position.
                // When that happens, we will clamp the camera position and exit the coroutine. In order to detect
                // this situation, we will perform a dot product between the camera move direction vector and
                // the vector which unites the current camera position with the target position. When this dot
                // product is < 0, it means that the camera has moved too far away and we will clamp its position
                // and exit. This could also probably be done using a variable which holds the accumulated travel
                // distance. When this distance becomes >= than the original length of 'fromCamPosToDestination',
                // we can exit.
                if (Vector3.Dot(fromCamPosToDestination, cameraDestinationPoint - cameraTransform.position) <= 0.0f &&
                    Mathf.Abs(Camera.orthographicSize - focusOpInfo.OrthoCameraHalfVerticalSize) < 1e-3f)
                {
                    cameraTransform.position = cameraDestinationPoint;
                    break;
                }

                yield return(null);
            }
        }
 /// <summary>
 /// Given a focus operation info instance, the method calculates the orbit offset
 /// along the camera look vector which can be used to perform orbit operations.
 /// </summary>
 private void CalculateOrbitOffsetAlongLook(EditorCameraFocusOperationInfo focusOpInfo)
 {
     _orbitOffsetAlongLook = (Camera.transform.position - focusOpInfo.FocusPoint).magnitude;
 }