/// <summary>Applies the composer rules and orients the camera accordingly</summary>
        /// <param name="curState">The current camera state</param>
        /// <param name="deltaTime">Used for calculating damping.  If less than
        /// zero, then target will snap to the center of the dead zone.</param>
        public override void MutateCameraState(ref CameraState curState, float deltaTime)
        {
            // Can't do anything without a group to look at
            CinemachineTargetGroup group = TargetGroup;

            if (group == null)
            {
                base.MutateCameraState(ref curState, deltaTime);
                return;
            }

            if (!IsValid || !curState.HasLookAt)
            {
                m_prevTargetHeight = 0;
                return;
            }

            curState.ReferenceLookAt = GetLookAtPointAndSetTrackedPoint(group.transform.position);
            Vector3 currentOffset   = TrackedPoint - curState.RawPosition;
            float   currentDistance = currentOffset.magnitude;

            if (currentDistance < Epsilon)
            {
                return;  // navel-gazing, get outa here
            }
            //UnityEngine.Profiling.Profiler.BeginSample("CinemachineGroupComposer.MutateCameraState");

            // Get the camera axis
            Vector3 fwd = currentOffset.AlmostZero() ? Vector3.forward : currentOffset.normalized;

            // Get the bounding box from that POV in view space, and find its width
            Bounds bounds = group.BoundingBox;

            LastBoundsMatrix = Matrix4x4.TRS(
                bounds.center - (fwd * bounds.extents.magnitude),
                Quaternion.LookRotation(fwd, curState.ReferenceUp), Vector3.one);
            LastBounds = group.GetViewSpaceBoundingBox(LastBoundsMatrix);
            float   targetHeight = GetTargetHeight(LastBounds);
            Vector3 targetPos    = LastBoundsMatrix.MultiplyPoint3x4(LastBounds.center);

            // Apply damping
            if (deltaTime >= 0)
            {
                float delta = targetHeight - m_prevTargetHeight;
                delta        = Damper.Damp(delta, m_FrameDamping, deltaTime);
                targetHeight = m_prevTargetHeight + delta;
            }
            m_prevTargetHeight = targetHeight;

            // Move the camera
            if (!curState.Lens.Orthographic && m_AdjustmentMode != AdjustmentMode.ZoomOnly)
            {
                // What distance would be needed to get the target height, at the current FOV
                float currentFOV     = curState.Lens.FieldOfView;
                float targetDistance = targetHeight / (2f * Mathf.Tan(currentFOV * Mathf.Deg2Rad / 2f));

                // target the near surface of the bounding box
                float cameraDistance = targetDistance + LastBounds.extents.z;

                // Clamp to respect min/max distance settings
                cameraDistance = Mathf.Clamp(
                    cameraDistance, currentDistance - m_MaxDollyIn, currentDistance + m_MaxDollyOut);
                cameraDistance = Mathf.Clamp(cameraDistance, m_MinimumDistance, m_MaximumDistance);

                // Apply
                curState.PositionCorrection += targetPos - fwd * cameraDistance - curState.RawPosition;
            }

            // Apply zoom
            if (curState.Lens.Orthographic || m_AdjustmentMode != AdjustmentMode.DollyOnly)
            {
                float nearBoundsDistance = (TrackedPoint - curState.CorrectedPosition).magnitude
                                           - LastBounds.extents.z;
                float currentFOV = 179;
                if (nearBoundsDistance > Epsilon)
                {
                    currentFOV = 2f * Mathf.Atan(targetHeight / (2 * nearBoundsDistance)) * Mathf.Rad2Deg;
                }

                LensSettings lens = curState.Lens;
                lens.FieldOfView      = Mathf.Clamp(currentFOV, m_MinimumFOV, m_MaximumFOV);
                lens.OrthographicSize = Mathf.Clamp(targetHeight / 2, m_MinimumOrthoSize, m_MaximumOrthoSize);
                curState.Lens         = lens;
            }

            // Now compose normally
            base.MutateCameraState(ref curState, deltaTime);
            //UnityEngine.Profiling.Profiler.EndSample();
        }
Example #2
0
        /// <summary>Applies the composer rules and orients the camera accordingly</summary>
        /// <param name="curState">The current camera state</param>
        /// <param name="deltaTime">Used for calculating damping.  If less than
        /// zero, then target will snap to the center of the dead zone.</param>
        public override void MutateCameraState(ref CameraState curState, float deltaTime)
        {
            // Can't do anything without a group to look at
            CinemachineTargetGroup group = LookAtTargetGroup;

            if (group == null)
            {
                base.MutateCameraState(ref curState, deltaTime);
                return;
            }

            if (!IsValid || !curState.HasLookAt)
            {
                m_prevTargetHeight = 0;
                m_prevCameraOffset = Vector3.zero;
                return;
            }

            bool canMoveCamera
                = !curState.Lens.Orthographic && m_AdjustmentMode != AdjustmentMode.ZoomOnly;

            // Get the bounding box from camera's POV in view space
            Vector3        observerPosition = curState.RawPosition;
            BoundingSphere s               = group.Sphere;
            Vector3        groupCenter     = s.position;
            Vector3        currentOffset   = groupCenter - observerPosition;
            float          currentDistance = currentOffset.magnitude;

            if (currentDistance < Epsilon)
            {
                return;  // navel-gazing, get outa here
            }
            Vector3 fwd = currentOffset / currentDistance;

            LastBoundsMatrix = Matrix4x4.TRS(observerPosition,
                                             Quaternion.LookRotation(fwd, curState.ReferenceUp), Vector3.one);
            Bounds b;

            if (curState.Lens.Orthographic)
            {
                b = group.GetViewSpaceBoundingBox(LastBoundsMatrix);
                Vector3 sizeDelta = new Vector3(b.center.x, b.center.y, 0);
                b.size    += sizeDelta.Abs() * 2;
                b.center   = new Vector3(0, 0, b.center.z);
                LastBounds = b;
            }
            else
            {
                if (canMoveCamera)
                {
                    // Get an upper bound on the distance
                    b           = group.GetViewSpaceBoundingBox(LastBoundsMatrix);
                    groupCenter = LastBoundsMatrix.MultiplyPoint3x4(b.center);

                    // Now try to get closer
                    float distance = GetTargetHeight(b)
                                     / (2f * Mathf.Tan(curState.Lens.FieldOfView * Mathf.Deg2Rad / 2f));
                    Vector3 nearCenter = b.center; nearCenter.z -= b.extents.z;
                    nearCenter = LastBoundsMatrix.MultiplyPoint3x4(nearCenter);
                    Vector3 newFwd = (groupCenter - nearCenter).normalized;
                    if (!newFwd.AlmostZero())
                    {
                        fwd = newFwd;
                    }
                    observerPosition = nearCenter - (fwd * distance);
                    LastBoundsMatrix = Matrix4x4.TRS(observerPosition,
                                                     Quaternion.LookRotation(fwd, curState.ReferenceUp), Vector3.one);
                }

                b = GetScreenSpaceGroupBoundingBox(group, LastBoundsMatrix, out fwd);
                LastBoundsMatrix = Matrix4x4.TRS(observerPosition,
                                                 Quaternion.LookRotation(fwd, curState.ReferenceUp), Vector3.one);
                LastBounds      = b;
                groupCenter     = LastBoundsMatrix.MultiplyPoint3x4(b.center);
                currentOffset   = groupCenter - curState.RawPosition;
                currentDistance = currentOffset.magnitude;
            }

            // Adjust bounds for framing size
            Vector3 extents = b.extents / m_GroupFramingSize;

            extents.z = Mathf.Min(b.extents.z, extents.z);
            b.extents = extents;

            // Apply damping
            float targetHeight = GetTargetHeight(b);

            if (deltaTime >= 0)
            {
                float delta = targetHeight - m_prevTargetHeight;
                delta        = Damper.Damp(delta, m_FrameDamping, deltaTime);
                targetHeight = m_prevTargetHeight + delta;
            }
            m_prevTargetHeight = targetHeight;

            // Move the camera
            if (canMoveCamera)
            {
                // What distance would be needed to get the target height, at the current FOV
                float depth            = b.extents.z;
                float d                = (groupCenter - observerPosition).magnitude;
                float nearTargetHeight = targetHeight * (d - depth) / d;
                float targetDistance   = nearTargetHeight
                                         / (2f * Mathf.Tan(curState.Lens.FieldOfView * Mathf.Deg2Rad / 2f));

                // Clamp to respect min/max distance settings to the near surface of the bounds
                float cameraDistance = targetDistance;
                cameraDistance  = Mathf.Clamp(cameraDistance, currentDistance - m_MaxDollyIn, currentDistance + m_MaxDollyOut);
                cameraDistance -= depth;
                cameraDistance  = Mathf.Clamp(cameraDistance, m_MinimumDistance, m_MaximumDistance);
                cameraDistance += depth;

                // Apply
                Vector3 newCamOffset
                    = (groupCenter - (fwd * (cameraDistance + depth))) - curState.RawPosition;
                if (deltaTime >= 0)
                {
                    Vector3 delta = newCamOffset - m_prevCameraOffset;
                    delta        = Damper.Damp(delta, m_FrameDamping, deltaTime);
                    newCamOffset = m_prevCameraOffset + delta;
                }
                m_prevCameraOffset           = newCamOffset;
                curState.PositionCorrection += newCamOffset;
            }

            // Apply zoom
            if (curState.Lens.Orthographic || m_AdjustmentMode != AdjustmentMode.DollyOnly)
            {
                float nearBoundsDistance = (groupCenter - curState.CorrectedPosition).magnitude;
                float currentFOV         = 179;
                if (nearBoundsDistance > Epsilon)
                {
                    currentFOV = 2f * Mathf.Atan(targetHeight / (2 * nearBoundsDistance)) * Mathf.Rad2Deg;
                }

                LensSettings lens = curState.Lens;
                lens.FieldOfView      = Mathf.Clamp(currentFOV, m_MinimumFOV, m_MaximumFOV);
                lens.OrthographicSize = Mathf.Clamp(targetHeight / 2, m_MinimumOrthoSize, m_MaximumOrthoSize);
                curState.Lens         = lens;
            }

            // Now compose normally
            curState.ReferenceLookAt = GetLookAtPointAndSetTrackedPoint(groupCenter);
            base.MutateCameraState(ref curState, deltaTime);
        }