/// <summary>This is a slerp that mimics a camera operator's movement in that /// it chooses a path that avoids the lower hemisphere, as defined by /// the up param</summary> /// <param name="vA">First direction</param> /// <param name="vB">Second direction</param> /// <param name="t">Interpolation amoun t</param> /// <param name="up">Defines the up direction</param> public static Vector3 SlerpWithReferenceUp( Vector3 vA, Vector3 vB, float t, Vector3 up) { float dA = vA.magnitude; float dB = vB.magnitude; if (dA < Epsilon || dB < Epsilon) { return(Vector3.Lerp(vA, vB, t)); } Vector3 dirA = vA / dA; Vector3 dirB = vB / dB; Quaternion qA = Quaternion.LookRotation(dirA, up); Quaternion qB = Quaternion.LookRotation(dirB, up); Quaternion q = UnityQuaternionExtensions.SlerpWithReferenceUp(qA, qB, t, up); Vector3 dir = q * Vector3.forward; return(dir * Mathf.Lerp(dA, dB, t)); }
/// <summary>Intelligently blend the contents of two states.</summary> /// <param name="stateA">The first state, corresponding to t=0</param> /// <param name="stateB">The second state, corresponding to t=1</param> /// <param name="t">How much to interpolate. Internally clamped to 0..1</param> /// <returns>Linearly interpolated CameraState</returns> public static CameraState Lerp(CameraState stateA, CameraState stateB, float t) { t = Mathf.Clamp01(t); float adjustedT = t; CameraState state = new CameraState(); state.Lens = LensSettings.Lerp(stateA.Lens, stateB.Lens, t); state.ReferenceUp = Vector3.Slerp(stateA.ReferenceUp, stateB.ReferenceUp, t); state.RawPosition = Vector3.Lerp(stateA.RawPosition, stateB.RawPosition, t); state.ShotQuality = Mathf.Lerp(stateA.ShotQuality, stateB.ShotQuality, t); state.PositionCorrection = Vector3.Lerp( stateA.PositionCorrection, stateB.PositionCorrection, t); // GML todo: is this right? Can it introduce a roll? state.OrientationCorrection = Quaternion.Slerp( stateA.OrientationCorrection, stateB.OrientationCorrection, t); Vector3 dirTarget = Vector3.zero; if (!stateA.HasLookAt || !stateB.HasLookAt) { state.ReferenceLookAt = kNoPoint; // can't interpolate if undefined } else { // Re-interpolate FOV to preserve target composition, if possible float fovA = stateA.Lens.FieldOfView; float fovB = stateB.Lens.FieldOfView; if (!state.Lens.Orthographic && !Mathf.Approximately(fovA, fovB)) { LensSettings lens = state.Lens; lens.FieldOfView = state.InterpolateFOV( fovA, fovB, Mathf.Max((stateA.ReferenceLookAt - stateA.CorrectedPosition).magnitude, stateA.Lens.NearClipPlane), Mathf.Max((stateB.ReferenceLookAt - stateB.CorrectedPosition).magnitude, stateB.Lens.NearClipPlane), t); state.Lens = lens; // Make sure we preserve the screen composition through FOV changes adjustedT = Mathf.Abs((lens.FieldOfView - fovA) / (fovB - fovA)); } // Linear interpolation of lookAt target point state.ReferenceLookAt = Vector3.Lerp( stateA.ReferenceLookAt, stateB.ReferenceLookAt, adjustedT); // If orientations are different, use LookAt to blend them float angle = Quaternion.Angle(stateA.RawOrientation, stateB.RawOrientation); if (angle > UnityVectorExtensions.Epsilon) { dirTarget = state.ReferenceLookAt - state.CorrectedPosition; } } // Clever orientation interpolation if (dirTarget.AlmostZero()) { // Don't know what we're looking at - can only slerp state.RawOrientation = UnityQuaternionExtensions.SlerpWithReferenceUp( stateA.RawOrientation, stateB.RawOrientation, t, state.ReferenceUp); } else { // Rotate while preserving our lookAt target dirTarget = dirTarget.normalized; if ((dirTarget - state.ReferenceUp).AlmostZero() || (dirTarget + state.ReferenceUp).AlmostZero()) { // Looking up or down at the pole state.RawOrientation = UnityQuaternionExtensions.SlerpWithReferenceUp( stateA.RawOrientation, stateB.RawOrientation, t, state.ReferenceUp); } else { // Put the target in the center state.RawOrientation = Quaternion.LookRotation(dirTarget, state.ReferenceUp); // Blend the desired offsets from center Vector2 deltaA = -stateA.RawOrientation.GetCameraRotationToTarget( stateA.ReferenceLookAt - stateA.CorrectedPosition, stateA.ReferenceUp); Vector2 deltaB = -stateB.RawOrientation.GetCameraRotationToTarget( stateB.ReferenceLookAt - stateB.CorrectedPosition, stateB.ReferenceUp); state.RawOrientation = state.RawOrientation.ApplyCameraRotation( Vector2.Lerp(deltaA, deltaB, adjustedT), state.ReferenceUp); } } // Accumulate the custom blendables and apply the weights for (int i = 0; i < stateA.NumCustomBlendables; ++i) { CustomBlendable b = stateA.GetCustomBlendable(i); b.m_Weight *= (1 - t); if (b.m_Weight > UnityVectorExtensions.Epsilon) { state.AddCustomBlendable(b); } } for (int i = 0; i < stateB.NumCustomBlendables; ++i) { CustomBlendable b = stateB.GetCustomBlendable(i); b.m_Weight *= t; if (b.m_Weight > UnityVectorExtensions.Epsilon) { state.AddCustomBlendable(b); } } return(state); }