void PositionCamera(ref CameraState curState, float deltaTime)
        {
            var targetPos     = FollowTargetPosition;
            var prevTargetPos = deltaTime >= 0 ? PreviousFollowTargetPosition : targetPos;

            // Compute damped target pos (compute in camera space)
            var dampedTargetPos = Quaternion.Inverse(curState.RawOrientation)
                                  * (targetPos - prevTargetPos);

            if (deltaTime >= 0)
            {
                dampedTargetPos = VirtualCamera.DetachedFollowTargetDamp(
                    dampedTargetPos, Damping, deltaTime);
            }
            dampedTargetPos = prevTargetPos + curState.RawOrientation * dampedTargetPos;

            // Get target rotation (worldspace)
            var fwd = Vector3.forward;
            var up  = Vector3.up;
            var followTargetRotation = FollowTargetRotation;
            var followTargetForward  = followTargetRotation * fwd;
            var angle = UnityVectorExtensions.SignedAngle(
                fwd, followTargetForward.ProjectOntoPlane(up), up);
            var previousHeadingAngle = deltaTime >= 0 ? PreviousHeadingAngle : angle;
            var deltaHeading         = angle - previousHeadingAngle;

            PreviousHeadingAngle = angle;

            // Bypass user-sourced rotation
            dampedTargetPos = targetPos
                              + Quaternion.AngleAxis(deltaHeading, up) * (dampedTargetPos - targetPos);
            PreviousFollowTargetPosition = dampedTargetPos;

            GetRigPositions(out Vector3 root, out Vector3 shoulder, out Vector3 hand);

            // 1. Check if pivot itself is colliding with something, if yes, then move the pivot
            // closer to the player. The radius is bigger here than in step 2, to avoid problems
            // next to walls. Where the preferred distance would be pulled completely to the
            // player, using a bigger radius, this won't happen.
            hand = PullTowardsStartOnCollision(in root, in hand, in CameraCollisionFilter, CameraRadius * 1.05f);

            // 2. Try to place the camera to the preferred distance
            var camPos = hand - (followTargetForward * CameraDistance);

            camPos = PullTowardsStartOnCollision(in hand, in camPos, in CameraCollisionFilter, CameraRadius);

            curState.RawPosition     = camPos;
            curState.RawOrientation  = FollowTargetRotation;
            curState.ReferenceLookAt = camPos + 1000.0f * (FollowTargetRotation * Vector3.forward);
            curState.ReferenceUp     = up;
        }
        /// <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)
        {
            if (!IsValid)
            {
                return;
            }

            Vector3 dampedPos = FollowTargetPosition;

            if (deltaTime >= 0)
            {
                dampedPos = m_PreviousTargetPosition + VirtualCamera.DetachedFollowTargetDamp(
                    dampedPos - m_PreviousTargetPosition, m_Damping, deltaTime);
            }
            m_PreviousTargetPosition = dampedPos;
            curState.RawPosition     = dampedPos;
        }
Ejemplo n.º 3
0
        /// <summary>Orients the camera to match the Follow target's orientation</summary>
        /// <param name="curState">The current camera state</param>
        /// <param name="deltaTime">Not used.</param>
        public override void MutateCameraState(ref CameraState curState, float deltaTime)
        {
            if (!IsValid)
            {
                return;
            }

            Quaternion dampedOrientation = FollowTargetRotation;

            if (deltaTime >= 0)
            {
                float t = VirtualCamera.DetachedFollowTargetDamp(1, m_Damping, deltaTime);
                dampedOrientation = Quaternion.Slerp(
                    m_PreviousReferenceOrientation, FollowTargetRotation, t);
            }
            m_PreviousReferenceOrientation = dampedOrientation;
            curState.RawOrientation        = dampedOrientation;
        }
        void PositionCamera(ref CameraState curState, float deltaTime)
        {
            var up            = curState.ReferenceUp;
            var targetPos     = FollowTargetPosition;
            var targetRot     = FollowTargetRotation;
            var targetForward = targetRot * Vector3.forward;
            var heading       = GetHeading(targetRot, up);

            if (deltaTime < 0)
            {
                // No damping - reset damping state info
                m_DampingCorrection = Vector3.zero;
#if CINEMACHINE_PHYSICS
                m_CamPosCollisionCorrection = 0;
#endif
            }
            else
            {
                // Damping correction is applied to the shoulder offset - stretching the rig
                m_DampingCorrection += Quaternion.Inverse(heading) * (m_PreviousFollowTargetPosition - targetPos);
                m_DampingCorrection -= VirtualCamera.DetachedFollowTargetDamp(m_DampingCorrection, Damping, deltaTime);
            }

            m_PreviousFollowTargetPosition = targetPos;
            var root = targetPos;
            GetRawRigPositions(root, targetRot, heading, out _, out Vector3 hand);

            // Place the camera at the correct distance from the hand
            var camPos = hand - (targetForward * (CameraDistance - m_DampingCorrection.z));

#if CINEMACHINE_PHYSICS
            // Check if hand is colliding with something, if yes, then move the hand
            // closer to the player. The radius is slightly enlarged, to avoid problems
            // next to walls
            float dummy        = 0;
            var   collidedHand = ResolveCollisions(root, hand, -1, CameraRadius * 1.05f, ref dummy);
            camPos = ResolveCollisions(
                collidedHand, camPos, deltaTime, CameraRadius, ref m_CamPosCollisionCorrection);
#endif
            // Set state
            curState.RawPosition    = camPos;
            curState.RawOrientation = targetRot; // not necessary, but left in to avoid breaking scenes that depend on this
        }
        void PositionCamera(ref CameraState curState, float deltaTime)
        {
            var up            = curState.ReferenceUp;
            var targetPos     = FollowTargetPosition;
            var targetRot     = FollowTargetRotation;
            var targetForward = targetRot * Vector3.forward;
            var heading       = GetHeading(targetForward, up);

            if (deltaTime < 0)
            {
                // No damping - reset damping state info
                m_DampingCorrection = Vector3.zero;
            }
            else
            {
                // Damping correction is applied to the shoulder offset - stretching the rig
                m_DampingCorrection += Quaternion.Inverse(heading) * (m_PreviousFollowTargetPosition - targetPos);
                m_DampingCorrection -= VirtualCamera.DetachedFollowTargetDamp(m_DampingCorrection, Damping, deltaTime);
            }

            m_PreviousFollowTargetPosition = targetPos;
            var root = targetPos;

            GetRawRigPositions(root, targetRot, heading, out _, out Vector3 hand);

            // Check if hand is colliding with something, if yes, then move the hand
            // closer to the player. The radius is slightly enlarged, to avoid problems
            // next to walls
            var collidedHand = ResolveCollisions(root, hand, CameraRadius * 1.05f);

            // Place the camera at the correct distance from the hand
            Vector3 camPos = hand - (targetForward * CameraDistance);

            camPos = ResolveCollisions(collidedHand, camPos, CameraRadius);

            // Set state
            curState.RawPosition    = camPos;
            curState.RawOrientation = targetRot;
            curState.ReferenceUp    = up;
        }
        /// <summary>Positions the virtual camera according to the transposer rules.</summary>
        /// <param name="deltaTime">Used for damping.  If less than 0, no damping is done.</param>
        /// <param name="up">Current camera up</param>
        /// <param name="desiredCameraOffset">Where we want to put the camera relative to the follow target</param>
        /// <param name="outTargetPosition">Resulting camera position</param>
        /// <param name="outTargetOrient">Damped target orientation</param>
        protected void TrackTarget(
            float deltaTime, Vector3 up, Vector3 desiredCameraOffset,
            out Vector3 outTargetPosition, out Quaternion outTargetOrient)
        {
            var  targetOrientation = GetReferenceOrientation(up);
            var  dampedOrientation = targetOrientation;
            bool prevStateValid    = deltaTime >= 0 && VirtualCamera.PreviousStateIsValid;

            if (prevStateValid)
            {
                if (m_AngularDampingMode == AngularDampingMode.Quaternion &&
                    m_BindingMode == BindingMode.LockToTarget)
                {
                    float t = VirtualCamera.DetachedFollowTargetDamp(1, m_AngularDamping, deltaTime);
                    dampedOrientation = Quaternion.Slerp(
                        m_PreviousReferenceOrientation, targetOrientation, t);
                }
                else
                {
                    var relative = (Quaternion.Inverse(m_PreviousReferenceOrientation)
                                    * targetOrientation).eulerAngles;
                    for (int i = 0; i < 3; ++i)
                    {
                        if (Mathf.Abs(relative[i]) < 0.01f) // correct for precision drift
                        {
                            relative[i] = 0;
                        }
                        else if (relative[i] > 180)
                        {
                            relative[i] -= 360;
                        }
                    }
                    relative          = VirtualCamera.DetachedFollowTargetDamp(relative, AngularDamping, deltaTime);
                    dampedOrientation = m_PreviousReferenceOrientation * Quaternion.Euler(relative);
                }
            }
            m_PreviousReferenceOrientation = dampedOrientation;

            var targetPosition  = FollowTargetPosition;
            var currentPosition = m_PreviousTargetPosition;
            var previousOffset  = prevStateValid ? m_PreviousOffset : desiredCameraOffset;
            var offsetDelta     = desiredCameraOffset - previousOffset;

            if (offsetDelta.sqrMagnitude > 0.01f)
            {
                var q = UnityVectorExtensions.SafeFromToRotation(
                    m_PreviousOffset.ProjectOntoPlane(up),
                    desiredCameraOffset.ProjectOntoPlane(up), up);
                currentPosition = targetPosition + q * (m_PreviousTargetPosition - targetPosition);
            }
            m_PreviousOffset = desiredCameraOffset;

            // Adjust for damping, which is done in camera-offset-local coords
            var positionDelta = targetPosition - currentPosition;

            if (prevStateValid)
            {
                Quaternion dampingSpace;
                if (desiredCameraOffset.AlmostZero())
                {
                    dampingSpace = VcamState.RawOrientation;
                }
                else
                {
                    dampingSpace = Quaternion.LookRotation(dampedOrientation * desiredCameraOffset, up);
                }
                var localDelta = Quaternion.Inverse(dampingSpace) * positionDelta;
                localDelta    = VirtualCamera.DetachedFollowTargetDamp(localDelta, Damping, deltaTime);
                positionDelta = dampingSpace * localDelta;
            }
            currentPosition += positionDelta;

            outTargetPosition = m_PreviousTargetPosition = currentPosition;
            outTargetOrient   = dampedOrientation;
        }
Ejemplo n.º 7
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;
        }