/// <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; }