Ejemplo n.º 1
0
        /// <summary>
        /// Call this from InternalUpdateCameraState() to check for changed
        /// targets and update the target cache.  This is needed for tracking
        /// when a target object changes.
        /// </summary>
        protected void UpdateTargetCache()
        {
            var target = ResolveFollow(Follow);

            FollowTargetChanged       = target != m_CachedFollowTarget;
            m_CachedFollowTarget      = target;
            m_CachedFollowTargetVcam  = null;
            m_CachedFollowTargetGroup = null;
            if (m_CachedFollowTarget != null)
            {
                m_CachedFollowTargetVcam  = target.GetComponent <CinemachineVirtualCameraBase>();
                m_CachedFollowTargetGroup = target.GetComponent <ICinemachineTargetGroup>();
            }

            target = ResolveLookAt(LookAt);
            LookAtTargetChanged       = target != m_CachedLookAtTarget;
            m_CachedLookAtTarget      = target;
            m_CachedLookAtTargetVcam  = null;
            m_CachedLookAtTargetGroup = null;
            if (target != null)
            {
                m_CachedLookAtTargetVcam  = target.GetComponent <CinemachineVirtualCameraBase>();
                m_CachedLookAtTargetGroup = target.GetComponent <ICinemachineTargetGroup>();
            }
        }
Ejemplo n.º 2
0
        static Bounds GetScreenSpaceGroupBoundingBox(
            ICinemachineTargetGroup group, ref Vector3 pos, Quaternion orientation)
        {
            Matrix4x4 observer = Matrix4x4.TRS(pos, orientation, Vector3.one);
            Vector2   minAngles, maxAngles, zRange;

            group.GetViewSpaceAngularBounds(observer, out minAngles, out maxAngles, out zRange);

            Quaternion q                 = Quaternion.identity.ApplyCameraRotation((minAngles + maxAngles) / 2, Vector3.up);
            Vector3    localPosAdustment = q * new Vector3(0, 0, (zRange.y + zRange.x) / 2);

            localPosAdustment.z = 0;
            pos      = observer.MultiplyPoint3x4(localPosAdustment);
            observer = Matrix4x4.TRS(pos, orientation, Vector3.one);
            group.GetViewSpaceAngularBounds(observer, out minAngles, out maxAngles, out zRange);

            float   zSize  = zRange.y - zRange.x;
            float   z      = zRange.x + (zSize / 2);
            Vector2 angles = new Vector2(89.5f, 89.5f);

            if (zRange.x > 0)
            {
                angles = Vector2.Max(maxAngles, UnityVectorExtensions.Abs(minAngles));
                angles = Vector2.Min(angles, new Vector2(89.5f, 89.5f));
            }
            angles *= Mathf.Deg2Rad;
            return(new Bounds(new Vector3(0, 0, z),
                              new Vector3(Mathf.Tan(angles.y) * z * 2, Mathf.Tan(angles.x) * z * 2, zSize)));
        }
        static Bounds GetScreenSpaceGroupBoundingBox(
            ICinemachineTargetGroup group, ref Vector3 pos, Quaternion orientation)
        {
            var observer = Matrix4x4.TRS(pos, orientation, Vector3.one);

            group.GetViewSpaceAngularBounds(observer, out var minAngles, out var maxAngles, out var zRange);
            var shift = (minAngles + maxAngles) / 2;

            var q = Quaternion.identity.ApplyCameraRotation(new Vector2(-shift.x, shift.y), Vector3.up);

            pos      = q * new Vector3(0, 0, (zRange.y + zRange.x) / 2);
            pos.z    = 0;
            pos      = observer.MultiplyPoint3x4(pos);
            observer = Matrix4x4.TRS(pos, orientation, Vector3.one);
            group.GetViewSpaceAngularBounds(observer, out minAngles, out maxAngles, out zRange);

            // For width and height (in camera space) of the bounding box, we use the values at the center of the box.
            // This is an arbitrary choice.  The gizmo drawer will take this into account when displaying
            // the frustum bounds of the group
            var     d      = zRange.y + zRange.x;
            Vector2 angles = new Vector2(89.5f, 89.5f);

            if (zRange.x > 0)
            {
                angles = Vector2.Max(maxAngles, UnityVectorExtensions.Abs(minAngles));
                angles = Vector2.Min(angles, new Vector2(89.5f, 89.5f));
            }
            angles *= Mathf.Deg2Rad;
            return(new Bounds(
                       new Vector3(0, 0, d / 2),
                       new Vector3(Mathf.Tan(angles.y) * d, Mathf.Tan(angles.x) * d, zRange.y - zRange.x)));
        }
Ejemplo n.º 4
0
        Vector3 ComputeGroupBounds(ICinemachineTargetGroup group, ref CameraState curState)
        {
            Vector3 cameraPos = curState.RawPosition;
            Vector3 fwd       = curState.RawOrientation * Vector3.forward;

            // Get the bounding box from camera's direction in view space
            LastBoundsMatrix = Matrix4x4.TRS(cameraPos, curState.RawOrientation, Vector3.one);
            Bounds  b           = group.GetViewSpaceBoundingBox(LastBoundsMatrix);
            Vector3 groupCenter = LastBoundsMatrix.MultiplyPoint3x4(b.center);
            float   boundsDepth = b.extents.z;

            if (!curState.Lens.Orthographic)
            {
                // Parallax might change bounds - refine
                float d = (Quaternion.Inverse(curState.RawOrientation) * (groupCenter - cameraPos)).z;
                cameraPos = groupCenter - fwd * (Mathf.Max(d, boundsDepth) + boundsDepth);

                // Will adjust cameraPos
                b = GetScreenSpaceGroupBoundingBox(group, ref cameraPos, curState.RawOrientation);
                LastBoundsMatrix = Matrix4x4.TRS(cameraPos, curState.RawOrientation, Vector3.one);
                groupCenter      = LastBoundsMatrix.MultiplyPoint3x4(b.center);
            }
            LastBounds = b;
            return(groupCenter - fwd * boundsDepth);
        }
 void UpdateFollowTargetCache()
 {
     mCachedFollowTargetVcam  = null;
     mCachedFollowTargetGroup = null;
     mCachedFollowTarget      = FollowTarget;
     if (mCachedFollowTarget != null)
     {
         mCachedFollowTargetVcam  = mCachedFollowTarget.GetComponent <CinemachineVirtualCameraBase>();
         mCachedFollowTargetGroup = mCachedFollowTarget.GetComponent <ICinemachineTargetGroup>();
     }
 }
Ejemplo n.º 6
0
        /// <param name="observer">Point of view</param>
        /// <param name="newFwd">New forward direction to use when interpreting the return value</param>
        /// <returns>Bounding box in a slightly rotated version of observer, as specified by newFwd</returns>
        static Bounds GetScreenSpaceGroupBoundingBox(
            ICinemachineTargetGroup group, Matrix4x4 observer, out Vector3 newFwd)
        {
            Vector2 minAngles, maxAngles, zRange;

            group.GetViewSpaceAngularBounds(observer, out minAngles, out maxAngles, out zRange);
            Vector2 shift = (minAngles + maxAngles) / 2;

            newFwd = Quaternion.identity.ApplyCameraRotation(shift, Vector3.up) * Vector3.forward;
            newFwd = observer.MultiplyVector(newFwd);

            float   d      = (zRange.y + zRange.x);
            Vector2 angles = (maxAngles - shift) * Mathf.Deg2Rad;

            angles = Vector2.Min(angles, new Vector2(89.5f, 89.5f));
            return(new Bounds(
                       new Vector3(0, 0, d / 2),
                       new Vector3(Mathf.Tan(angles.y) * d, Mathf.Tan(angles.x) * d, zRange.y - zRange.x)));
        }
        /// <param name="observer">Point of view</param>
        /// <param name="newFwd">New forward direction to use when interpreting the return value</param>
        /// <returns>Bounding box in a slightly rotated version of observer, as specified by newFwd</returns>
        static Bounds GetScreenSpaceGroupBoundingBox(
            ICinemachineTargetGroup group, Matrix4x4 observer, out Vector3 newFwd)
        {
            group.GetViewSpaceAngularBounds(observer, out var minAngles, out var maxAngles, out var zRange);
            var shift = (minAngles + maxAngles) / 2;

            newFwd = Quaternion.identity.ApplyCameraRotation(new Vector2(-shift.x, shift.y), Vector3.up) * Vector3.forward;
            newFwd = observer.MultiplyVector(newFwd);

            // For width and height (in camera space) of the bounding box, we use the values at the center of the box.
            // This is an arbitrary choice.  The gizmo drawer will take this into account when displaying
            // the frustum bounds of the group
            var d      = zRange.y + zRange.x;
            var angles = Vector2.Min(maxAngles - shift, new Vector2(89.5f, 89.5f)) * Mathf.Deg2Rad;

            return(new Bounds(
                       new Vector3(0, 0, d / 2),
                       new Vector3(Mathf.Tan(angles.y) * d, Mathf.Tan(angles.x) * d, zRange.y - zRange.x)));
        }
Ejemplo n.º 8
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
            ICinemachineTargetGroup group = LookAtTargetGroup;

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

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

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

            // Get the bounding box from camera's POV in view space
            Vector3        up          = curState.ReferenceUp;
            var            cameraPos   = curState.RawPosition;
            BoundingSphere s           = group.Sphere;
            Vector3        groupCenter = s.position;
            Vector3        fwd         = groupCenter - cameraPos;
            float          d           = fwd.magnitude;

            if (d < Epsilon)
            {
                return;  // navel-gazing, get outa here
            }
            // Approximate looking at the group center
            fwd /= d;
            LastBoundsMatrix = Matrix4x4.TRS(
                cameraPos, Quaternion.LookRotation(fwd, up), Vector3.one);

            // Correction for the actual center
            Bounds b;

            if (isOrthographic)
            {
                b                = group.GetViewSpaceBoundingBox(LastBoundsMatrix);
                groupCenter      = LastBoundsMatrix.MultiplyPoint3x4(b.center);
                fwd              = (groupCenter - cameraPos).normalized;
                LastBoundsMatrix = Matrix4x4.TRS(cameraPos, Quaternion.LookRotation(fwd, up), Vector3.one);
                b                = group.GetViewSpaceBoundingBox(LastBoundsMatrix);
                LastBounds       = b;
            }
            else
            {
                b = GetScreenSpaceGroupBoundingBox(group, LastBoundsMatrix, out fwd);
                LastBoundsMatrix = Matrix4x4.TRS(cameraPos, Quaternion.LookRotation(fwd, up), Vector3.one);
                LastBounds       = b;
                groupCenter      = cameraPos + fwd * b.center.z;
                fwd = (groupCenter - cameraPos).normalized;
            }

            // Adjust bounds for framing size, and get target height
            float boundsDepth  = b.extents.z;
            float targetHeight = GetTargetHeight(b.size / m_GroupFramingSize);

            if (isOrthographic)
            {
                targetHeight = Mathf.Clamp(targetHeight / 2, m_MinimumOrthoSize, m_MaximumOrthoSize);

                // ApplyDamping
                if (deltaTime >= 0)
                {
                    targetHeight = m_prevFOV + Damper.Damp(targetHeight - m_prevFOV, m_FrameDamping, deltaTime);
                }
                m_prevFOV = targetHeight;

                LensSettings lens = curState.Lens;
                lens.OrthographicSize = Mathf.Clamp(targetHeight, m_MinimumOrthoSize, m_MaximumOrthoSize);
                curState.Lens         = lens;
            }
            else
            {
                // Adjust height for perspective - we want the height at the near surface
                float z = b.center.z;
                if (z > boundsDepth)
                {
                    targetHeight = Mathf.Lerp(0, targetHeight, (z - boundsDepth) / z);
                }

                // Move the camera
                if (canMoveCamera)
                {
                    // What distance from near edge would be needed to get the adjusted
                    // target height, at the current FOV
                    float targetDistance = boundsDepth
                                           + targetHeight / (2f * Mathf.Tan(curState.Lens.FieldOfView * Mathf.Deg2Rad / 2f));

                    // Clamp to respect min/max distance settings to the near surface of the bounds
                    targetDistance = Mathf.Clamp(
                        targetDistance, boundsDepth + m_MinimumDistance, boundsDepth + m_MaximumDistance);

                    // Clamp to respect min/max camera movement
                    float targetDelta = targetDistance - Vector3.Distance(curState.RawPosition, groupCenter);
                    targetDelta = Mathf.Clamp(targetDelta, -m_MaxDollyIn, m_MaxDollyOut);

                    // ApplyDamping
                    if (deltaTime >= 0)
                    {
                        float delta = targetDelta - m_prevFramingDistance;
                        delta       = Damper.Damp(delta, m_FrameDamping, deltaTime);
                        targetDelta = m_prevFramingDistance + delta;
                    }
                    m_prevFramingDistance        = targetDelta;
                    curState.PositionCorrection -= fwd * targetDelta;
                    cameraPos -= fwd * targetDelta;
                }

                // Apply zoom
                if (m_AdjustmentMode != AdjustmentMode.DollyOnly)
                {
                    float nearBoundsDistance = (groupCenter - cameraPos).magnitude - boundsDepth;
                    float targetFOV          = 179;
                    if (nearBoundsDistance > Epsilon)
                    {
                        targetFOV = 2f * Mathf.Atan(targetHeight / (2 * nearBoundsDistance)) * Mathf.Rad2Deg;
                    }
                    targetFOV = Mathf.Clamp(targetFOV, m_MinimumFOV, m_MaximumFOV);

                    // ApplyDamping
                    if (deltaTime >= 0 && m_prevFOV != 0)
                    {
                        targetFOV = m_prevFOV + Damper.Damp(targetFOV - m_prevFOV, m_FrameDamping, deltaTime);
                    }
                    m_prevFOV = targetFOV;

                    LensSettings lens = curState.Lens;
                    lens.FieldOfView = targetFOV;
                    curState.Lens    = lens;
                }
            }
            // Now compose normally
            curState.ReferenceLookAt = GetLookAtPointAndSetTrackedPoint(
                groupCenter, curState.ReferenceUp, deltaTime);
            base.MutateCameraState(ref curState, deltaTime);
        }
        /// <summary>Get the property names to exclude in the inspector.</summary>
        /// <param name="excluded">Add the names to this list</param>
        protected override void GetExcludedPropertiesInInspector(List <string> excluded)
        {
            base.GetExcludedPropertiesInInspector(excluded);
            if (Target.m_UnlimitedSoftZone)
            {
                excluded.Add(FieldPath(x => x.m_SoftZoneWidth));
                excluded.Add(FieldPath(x => x.m_SoftZoneHeight));
                excluded.Add(FieldPath(x => x.m_BiasX));
                excluded.Add(FieldPath(x => x.m_BiasY));
            }
            ICinemachineTargetGroup group = Target.AbstractFollowTargetGroup;

            if (group == null || Target.m_GroupFramingMode == CinemachineFramingTransposer.FramingMode.None)
            {
                excluded.Add(FieldPath(x => x.m_GroupFramingSize));
                excluded.Add(FieldPath(x => x.m_AdjustmentMode));
                excluded.Add(FieldPath(x => x.m_MaxDollyIn));
                excluded.Add(FieldPath(x => x.m_MaxDollyOut));
                excluded.Add(FieldPath(x => x.m_MinimumDistance));
                excluded.Add(FieldPath(x => x.m_MaximumDistance));
                excluded.Add(FieldPath(x => x.m_MinimumFOV));
                excluded.Add(FieldPath(x => x.m_MaximumFOV));
                excluded.Add(FieldPath(x => x.m_MinimumOrthoSize));
                excluded.Add(FieldPath(x => x.m_MaximumOrthoSize));
                if (group == null)
                {
                    excluded.Add(FieldPath(x => x.m_GroupFramingMode));
                }
            }
            else
            {
                CinemachineBrain brain = CinemachineCore.Instance.FindPotentialTargetBrain(Target.VirtualCamera);
                bool             ortho = brain != null ? brain.OutputCamera.orthographic : false;
                if (ortho)
                {
                    excluded.Add(FieldPath(x => x.m_AdjustmentMode));
                    excluded.Add(FieldPath(x => x.m_MaxDollyIn));
                    excluded.Add(FieldPath(x => x.m_MaxDollyOut));
                    excluded.Add(FieldPath(x => x.m_MinimumDistance));
                    excluded.Add(FieldPath(x => x.m_MaximumDistance));
                    excluded.Add(FieldPath(x => x.m_MinimumFOV));
                    excluded.Add(FieldPath(x => x.m_MaximumFOV));
                }
                else
                {
                    excluded.Add(FieldPath(x => x.m_MinimumOrthoSize));
                    excluded.Add(FieldPath(x => x.m_MaximumOrthoSize));
                    switch (Target.m_AdjustmentMode)
                    {
                    case CinemachineFramingTransposer.AdjustmentMode.DollyOnly:
                        excluded.Add(FieldPath(x => x.m_MinimumFOV));
                        excluded.Add(FieldPath(x => x.m_MaximumFOV));
                        break;

                    case CinemachineFramingTransposer.AdjustmentMode.ZoomOnly:
                        excluded.Add(FieldPath(x => x.m_MaxDollyIn));
                        excluded.Add(FieldPath(x => x.m_MaxDollyOut));
                        excluded.Add(FieldPath(x => x.m_MinimumDistance));
                        excluded.Add(FieldPath(x => x.m_MaximumDistance));
                        break;

                    default:
                        break;
                    }
                }
            }
        }
Ejemplo n.º 10
0
        /// <summary>Positions the virtual camera according to the transposer rules.</summary>
        /// <param name="curState">The current camera state</param>
        /// <param name="deltaTime">Used for damping.  If less than 0, no damping is done.</param>
        public override void MutateCameraState(ref CameraState curState, float deltaTime)
        {
            LensSettings lens = curState.Lens;
            Vector3      followTargetPosition = FollowTargetPosition + (FollowTargetRotation * m_TrackedObjectOffset);
            bool         previousStateIsValid = deltaTime >= 0 && VirtualCamera.PreviousStateIsValid;

            if (!previousStateIsValid || VirtualCamera.FollowTargetChanged)
            {
                m_Predictor.Reset();
            }
            if (!previousStateIsValid)
            {
                m_PreviousCameraPosition = curState.RawPosition;
                m_prevFOV      = lens.Orthographic ? lens.OrthographicSize : lens.FieldOfView;
                m_prevRotation = curState.RawOrientation;
                if (!InheritingPosition && m_CenterOnActivate)
                {
                    m_PreviousCameraPosition = FollowTargetPosition
                                               + (curState.RawOrientation * Vector3.back) * m_CameraDistance;
                }
            }
            if (!IsValid)
            {
                InheritingPosition = false;
                return;
            }

            var verticalFOV = lens.FieldOfView;

            // Compute group bounds and adjust follow target for group framing
            ICinemachineTargetGroup group = AbstractFollowTargetGroup;
            bool isGroupFraming           = group != null && m_GroupFramingMode != FramingMode.None && !group.IsEmpty;

            if (isGroupFraming)
            {
                followTargetPosition = ComputeGroupBounds(group, ref curState);
            }

            TrackedPoint = followTargetPosition;
            if (m_LookaheadTime > Epsilon)
            {
                m_Predictor.Smoothing = m_LookaheadSmoothing;
                m_Predictor.AddPosition(followTargetPosition, deltaTime, m_LookaheadTime);
                var delta = m_Predictor.PredictPositionDelta(m_LookaheadTime);
                if (m_LookaheadIgnoreY)
                {
                    delta = delta.ProjectOntoPlane(curState.ReferenceUp);
                }
                var p = followTargetPosition + delta;
                if (isGroupFraming)
                {
                    var b = LastBounds;
                    b.center  += LastBoundsMatrix.MultiplyPoint3x4(delta);
                    LastBounds = b;
                }
                TrackedPoint = p;
            }

            if (!curState.HasLookAt)
            {
                curState.ReferenceLookAt = followTargetPosition;
            }

            // Adjust the desired depth for group framing
            float targetDistance = m_CameraDistance;
            bool  isOrthographic = lens.Orthographic;
            float targetHeight   = isGroupFraming ? GetTargetHeight(LastBounds.size / m_GroupFramingSize) : 0;

            targetHeight = Mathf.Max(targetHeight, kMinimumGroupSize);
            if (!isOrthographic && isGroupFraming)
            {
                // Adjust height for perspective - we want the height at the near surface
                float boundsDepth = LastBounds.extents.z;
                float z           = LastBounds.center.z;
                if (z > boundsDepth)
                {
                    targetHeight = Mathf.Lerp(0, targetHeight, (z - boundsDepth) / z);
                }

                if (m_AdjustmentMode != AdjustmentMode.ZoomOnly)
                {
                    // What distance from near edge would be needed to get the adjusted
                    // target height, at the current FOV
                    targetDistance = targetHeight / (2f * Mathf.Tan(verticalFOV * Mathf.Deg2Rad / 2f));

                    // Clamp to respect min/max distance settings to the near surface of the bounds
                    targetDistance = Mathf.Clamp(targetDistance, m_MinimumDistance, m_MaximumDistance);

                    // Clamp to respect min/max camera movement
                    float targetDelta = targetDistance - m_CameraDistance;
                    targetDelta    = Mathf.Clamp(targetDelta, -m_MaxDollyIn, m_MaxDollyOut);
                    targetDistance = m_CameraDistance + targetDelta;
                }
            }

            // Optionally allow undamped camera orientation change
            Quaternion localToWorld = curState.RawOrientation;

            if (previousStateIsValid && m_TargetMovementOnly)
            {
                var q = localToWorld * Quaternion.Inverse(m_prevRotation);
                m_PreviousCameraPosition = TrackedPoint + q * (m_PreviousCameraPosition - TrackedPoint);
            }
            m_prevRotation = localToWorld;

            // Work in camera-local space
            Vector3    camPosWorld  = m_PreviousCameraPosition;
            Quaternion worldToLocal = Quaternion.Inverse(localToWorld);
            Vector3    cameraPos    = worldToLocal * camPosWorld;
            Vector3    targetPos    = (worldToLocal * TrackedPoint) - cameraPos;
            Vector3    lookAtPos    = targetPos;

            // Move along camera z
            Vector3 cameraOffset = Vector3.zero;
            float   cameraMin    = Mathf.Max(kMinimumCameraDistance, targetDistance - m_DeadZoneDepth / 2);
            float   cameraMax    = Mathf.Max(cameraMin, targetDistance + m_DeadZoneDepth / 2);
            float   targetZ      = Mathf.Min(targetPos.z, lookAtPos.z);

            if (targetZ < cameraMin)
            {
                cameraOffset.z = targetZ - cameraMin;
            }
            if (targetZ > cameraMax)
            {
                cameraOffset.z = targetZ - cameraMax;
            }

            // Move along the XY plane
            float screenSize = lens.Orthographic
                ? lens.OrthographicSize
                : Mathf.Tan(0.5f * verticalFOV * Mathf.Deg2Rad) * (targetZ - cameraOffset.z);
            Rect softGuideOrtho = ScreenToOrtho(SoftGuideRect, screenSize, lens.Aspect);

            if (!previousStateIsValid)
            {
                // No damping or hard bounds, just snap to central bounds, skipping the soft zone
                Rect rect = softGuideOrtho;
                if (m_CenterOnActivate && !InheritingPosition)
                {
                    rect = new Rect(rect.center, Vector2.zero); // Force to center
                }
                cameraOffset += OrthoOffsetToScreenBounds(targetPos, rect);
            }
            else
            {
                // Move it through the soft zone, with damping
                cameraOffset += OrthoOffsetToScreenBounds(targetPos, softGuideOrtho);
                cameraOffset  = VirtualCamera.DetachedFollowTargetDamp(
                    cameraOffset, new Vector3(m_XDamping, m_YDamping, m_ZDamping), deltaTime);

                // Make sure the real target (not the lookahead one) is still in the frame
                if (!m_UnlimitedSoftZone &&
                    (deltaTime < 0 || VirtualCamera.FollowTargetAttachment > 1 - Epsilon))
                {
                    Rect hardGuideOrtho = ScreenToOrtho(HardGuideRect, screenSize, lens.Aspect);
                    var  realTargetPos  = (worldToLocal * followTargetPosition) - cameraPos;
                    cameraOffset += OrthoOffsetToScreenBounds(
                        realTargetPos - cameraOffset, hardGuideOrtho);
                }
            }
            curState.RawPosition     = localToWorld * (cameraPos + cameraOffset);
            m_PreviousCameraPosition = curState.RawPosition;

            // Adjust lens for group framing
            if (isGroupFraming)
            {
                if (isOrthographic)
                {
                    targetHeight = Mathf.Clamp(targetHeight / 2, m_MinimumOrthoSize, m_MaximumOrthoSize);

                    // Apply Damping
                    if (previousStateIsValid)
                    {
                        targetHeight = m_prevFOV + VirtualCamera.DetachedFollowTargetDamp(
                            targetHeight - m_prevFOV, m_ZDamping, deltaTime);
                    }
                    m_prevFOV = targetHeight;

                    lens.OrthographicSize = Mathf.Clamp(targetHeight, m_MinimumOrthoSize, m_MaximumOrthoSize);
                    curState.Lens         = lens;
                }
                else if (m_AdjustmentMode != AdjustmentMode.DollyOnly)
                {
                    var localTarget = Quaternion.Inverse(curState.RawOrientation)
                                      * (followTargetPosition - curState.RawPosition);
                    float nearBoundsDistance = localTarget.z;
                    float targetFOV          = 179;
                    if (nearBoundsDistance > Epsilon)
                    {
                        targetFOV = 2f * Mathf.Atan(targetHeight / (2 * nearBoundsDistance)) * Mathf.Rad2Deg;
                    }
                    targetFOV = Mathf.Clamp(targetFOV, m_MinimumFOV, m_MaximumFOV);

                    // ApplyDamping
                    if (previousStateIsValid)
                    {
                        targetFOV = m_prevFOV + VirtualCamera.DetachedFollowTargetDamp(
                            targetFOV - m_prevFOV, m_ZDamping, deltaTime);
                    }
                    m_prevFOV = targetFOV;

                    lens.FieldOfView = targetFOV;
                    curState.Lens    = lens;
                }
            }
            InheritingPosition = false;
        }
        /// <summary>Positions the virtual camera according to the transposer rules.</summary>
        /// <param name="curState">The current camera state</param>
        /// <param name="deltaTime">Used for damping.  If less than 0, no damping is done.</param>
        public override void MutateCameraState(ref CameraState curState, float deltaTime)
        {
            Vector3 followTargetPosition = FollowTargetPosition;

            if (deltaTime < 0)
            {
                m_Predictor.Reset();
                m_PreviousCameraPosition = curState.RawPosition;
                m_prevFOV = 0;
                if (!InheritingPosition && m_CenterOnActivate)
                {
                    m_PreviousCameraPosition = FollowTargetPosition
                                               + (curState.RawOrientation * Vector3.back) * m_CameraDistance;
                }
            }
            if (!IsValid)
            {
                InheritingPosition = false;
                return;
            }

            // Compute group bounds and adjust follow target for group framing
            ICinemachineTargetGroup group = FollowTargetGroup;
            bool isGroupFraming           = group != null && m_GroupFramingMode != FramingMode.None;

            if (isGroupFraming)
            {
                followTargetPosition = ComputeGroupBounds(group, ref curState);
            }

            TrackedPoint = followTargetPosition;
            if (m_LookaheadTime > Epsilon)
            {
                m_Predictor.Smoothing = m_LookaheadSmoothing;
                m_Predictor.AddPosition(followTargetPosition, deltaTime, m_LookaheadTime);
                var delta = m_Predictor.PredictPositionDelta(m_LookaheadTime);
                if (m_LookaheadIgnoreY)
                {
                    delta = delta.ProjectOntoPlane(curState.ReferenceUp);
                }
                var p = followTargetPosition + delta;
                if (isGroupFraming)
                {
                    var b = LastBounds;
                    b.center  += p - followTargetPosition;
                    LastBounds = b;
                }
                TrackedPoint = p;
            }

            if (!curState.HasLookAt)
            {
                curState.ReferenceLookAt = followTargetPosition;
            }

            // Adjust the desired depth for group framing
            float targetDistance = m_CameraDistance;
            bool  isOrthographic = curState.Lens.Orthographic;
            float targetHeight   = isGroupFraming ? GetTargetHeight(LastBounds.size / m_GroupFramingSize) : 0;

            if (!isOrthographic && isGroupFraming)
            {
                // Adjust height for perspective - we want the height at the near surface
                float boundsDepth = LastBounds.extents.z;
                float z           = LastBounds.center.z;
                if (z > boundsDepth)
                {
                    targetHeight = Mathf.Lerp(0, targetHeight, (z - boundsDepth) / z);
                }

                if (m_AdjustmentMode != AdjustmentMode.ZoomOnly)
                {
                    // What distance from near edge would be needed to get the adjusted
                    // target height, at the current FOV
                    targetDistance = targetHeight / (2f * Mathf.Tan(curState.Lens.FieldOfView * Mathf.Deg2Rad / 2f));

                    // Clamp to respect min/max distance settings to the near surface of the bounds
                    targetDistance = Mathf.Clamp(targetDistance, m_MinimumDistance, m_MaximumDistance);

                    // Clamp to respect min/max camera movement
                    float targetDelta = targetDistance - m_CameraDistance;
                    targetDelta    = Mathf.Clamp(targetDelta, -m_MaxDollyIn, m_MaxDollyOut);
                    targetDistance = m_CameraDistance + targetDelta;
                }
            }

            // Work in camera-local space
            Vector3    camPosWorld  = m_PreviousCameraPosition;
            Quaternion localToWorld = curState.RawOrientation;
            Quaternion worldToLocal = Quaternion.Inverse(localToWorld);
            Vector3    cameraPos    = worldToLocal * camPosWorld;
            Vector3    targetPos    = (worldToLocal * TrackedPoint) - cameraPos;
            Vector3    lookAtPos    = targetPos;

            // Move along camera z
            Vector3 cameraOffset = Vector3.zero;
            float   cameraMin    = Mathf.Max(kMinimumCameraDistance, targetDistance - m_DeadZoneDepth / 2);
            float   cameraMax    = Mathf.Max(cameraMin, targetDistance + m_DeadZoneDepth / 2);
            float   targetZ      = Mathf.Min(targetPos.z, lookAtPos.z);

            if (targetZ < cameraMin)
            {
                cameraOffset.z = targetZ - cameraMin;
            }
            if (targetZ > cameraMax)
            {
                cameraOffset.z = targetZ - cameraMax;
            }

            // Move along the XY plane
            float screenSize = curState.Lens.Orthographic
                ? curState.Lens.OrthographicSize
                : Mathf.Tan(0.5f * curState.Lens.FieldOfView * Mathf.Deg2Rad)
                               * (targetZ - cameraOffset.z);
            Rect softGuideOrtho = ScreenToOrtho(SoftGuideRect, screenSize, curState.Lens.Aspect);

            if (deltaTime < 0)
            {
                // No damping or hard bounds, just snap to central bounds, skipping the soft zone
                Rect rect = softGuideOrtho;
                if (m_CenterOnActivate && !InheritingPosition)
                {
                    rect = new Rect(rect.center, Vector2.zero); // Force to center
                }
                cameraOffset += OrthoOffsetToScreenBounds(targetPos, rect);
            }
            else
            {
                // Move it through the soft zone
                cameraOffset += OrthoOffsetToScreenBounds(targetPos, softGuideOrtho);

                // Find where it intersects the hard zone
                Vector3 hard = Vector3.zero;
                if (!m_UnlimitedSoftZone)
                {
                    Rect hardGuideOrtho = ScreenToOrtho(HardGuideRect, screenSize, curState.Lens.Aspect);
                    hard = OrthoOffsetToScreenBounds(targetPos, hardGuideOrtho);
                    float t = Mathf.Max(hard.x / (cameraOffset.x + Epsilon), hard.y / (cameraOffset.y + Epsilon));
                    hard = cameraOffset * t;
                }
                // Apply damping, but only to the portion of the move that's inside the hard zone
                cameraOffset = hard + Damper.Damp(
                    cameraOffset - hard, new Vector3(m_XDamping, m_YDamping, m_ZDamping), deltaTime);

                // If we have lookahead, make sure the real target is still in the frame
                if (!m_UnlimitedSoftZone && !(TrackedPoint - curState.ReferenceLookAt).AlmostZero())
                {
                    Rect hardGuideOrtho = ScreenToOrtho(HardGuideRect, screenSize, curState.Lens.Aspect);
                    cameraOffset += OrthoOffsetToScreenBounds(lookAtPos - cameraOffset, hardGuideOrtho);
                }
            }
            curState.RawPosition = m_PreviousCameraPosition = localToWorld * (cameraPos + cameraOffset);

            // Adjust lens for group framing
            if (isGroupFraming)
            {
                if (isOrthographic)
                {
                    targetHeight = Mathf.Clamp(targetHeight / 2, m_MinimumOrthoSize, m_MaximumOrthoSize);

                    // Apply Damping
                    if (deltaTime >= 0)
                    {
                        targetHeight = m_prevFOV + Damper.Damp(targetHeight - m_prevFOV, m_ZDamping, deltaTime);
                    }
                    m_prevFOV = targetHeight;

                    LensSettings lens = curState.Lens;
                    lens.OrthographicSize = Mathf.Clamp(targetHeight, m_MinimumOrthoSize, m_MaximumOrthoSize);
                    curState.Lens         = lens;
                }
                else if (m_AdjustmentMode != AdjustmentMode.DollyOnly)
                {
                    var localTarget = Quaternion.Inverse(curState.RawOrientation)
                                      * (followTargetPosition - curState.RawPosition);
                    float nearBoundsDistance = localTarget.z;
                    float targetFOV          = 179;
                    if (nearBoundsDistance > Epsilon)
                    {
                        targetFOV = 2f * Mathf.Atan(targetHeight / (2 * nearBoundsDistance)) * Mathf.Rad2Deg;
                    }
                    targetFOV = Mathf.Clamp(targetFOV, m_MinimumFOV, m_MaximumFOV);

                    // ApplyDamping
                    if (deltaTime >= 0 && m_prevFOV != 0)
                    {
                        targetFOV = m_prevFOV + Damper.Damp(targetFOV - m_prevFOV, m_ZDamping, deltaTime);
                    }
                    m_prevFOV = targetFOV;

                    LensSettings lens = curState.Lens;
                    lens.FieldOfView = targetFOV;
                    curState.Lens    = lens;
                }
            }
            InheritingPosition = false;
        }
Ejemplo n.º 12
0
 private void OnEnable()
 {
     _group = GetComponent <ICinemachineTargetGroup>();
 }