// David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2020 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once #include // The spherical linear interpolation (slerp) of unit-length quaternions // q0 and q1 for t in [0,1] and theta in (0,pi) is // slerp(t,q0,q1) = [sin((1-t)*theta)*q0 + sin(theta)*q1]/sin(theta) // where theta is the angle between q0 and q1 [cos(theta) = Dot(q0,q1)]. // This function is a parameterization of the great spherical arc between // q0 and q1 on the unit hypersphere. Moreover, the parameterization is // one of normalized arclength--a particle traveling along the arc through // time t does so with constant speed. // // Read the comments in GteChebyshevRatio.h regarding estimates for the // ratio sin(t*theta)/sin(theta). // // When using slerp in animations involving sequences of quaternions, it is // typical that the quaternions are preprocessed so that consecutive ones // form an acute angle A in [0,pi/2]. Other preprocessing can help with // performance. See the function comments in the SLERP class. namespace WwiseGTE { template class SLERP { public: // The angle between q0 and q1 is in [0,pi). There are no angle // restrictions and nothing is precomputed. template inline static Quaternion Estimate(Real t, Quaternion const& q0, Quaternion const& q1) { static_assert(1 <= N && N <= 16, "Invalid degree."); Real cs = Dot(q0, q1); Real sign; if (cs >= (Real)0) { sign = (Real)1; } else { cs = -cs; sign = (Real)-1; } Real f0, f1; ChebyshevRatio::template GetEstimate(t, (Real)1 - cs, f0, f1); return q0 * f0 + q1 * (sign * f1); } // The angle between q0 and q1 must be in [0,pi/2]. The suffix R is // for 'Restricted'. The preprocessing code is // Quaternion q[n]; // assuming initialized // for (i0 = 0, i1 = 1; i1 < n; i0 = i1++) // { // cosA = Dot(q[i0], q[i1]); // if (cosA < 0) // { // q[i1] = -q[i1]; // now Dot(q[i0], q[i]1) >= 0 // } // } template inline static Quaternion EstimateR(Real t, Quaternion const& q0, Quaternion const& q1) { static_assert(1 <= N && N <= 16, "Invalid degree."); Real f0, f1; ChebyshevRatio::template GetEstimate(t, (Real)1 - Dot(q0, q1), f0, f1); return q0 * f0 + q1 * f1; } // The angle between q0 and q1 must be in [0,pi/2]. The suffix R is // for 'Restricted' and the suffix P is for 'Preprocessed'. The // preprocessing code is // Quaternion q[n]; // assuming initialized // Real cosA[n-1], omcosA[n-1]; // to be precomputed // for (i0 = 0, i1 = 1; i1 < n; i0 = i1++) // { // cs = Dot(q[i0], q[i1]); // if (cosA[i0] < 0) // { // q[i1] = -q[i1]; // cs = -cs; // } // // // for Quaterion::SlerpRP // cosA[i0] = cs; // // // for SLERP::EstimateRP // omcosA[i0] = 1 - cs; // } template inline static Quaternion EstimateRP(Real t, Quaternion const& q0, Quaternion const& q1, Real omcosA) { static_assert(1 <= N && N <= 16, "Invalid degree."); Real f0, f1; ChebyshevRatio::template GetEstimate(t, omcosA, f0, f1); return q0 * f0 + q1 * f1; } // The angle between q0 and q1 is A and must be in [0,pi/2]. // Quaternion qh is slerp(1/2,q0,q1) = (q0+q1)/|q0+q1|, so the angle // between q0 and qh is A/2 and the angle between qh and q1 is A/2. // The preprocessing code is // Quaternion q[n]; // assuming initialized // Quaternion qh[n-1]; // to be precomputed // Real omcosAH[n-1]; // to be precomputed // for (i0 = 0, i1 = 1; i1 < n; i0 = i1++) // { // cosA = Dot(q[i0], q[i1]); // if (cosA < 0) // { // q[i1] = -q[i1]; // cosA = -cosA; // } // cosAH = sqrt((1 + cosA)/2); // qh[i0] = (q0 + q1) / (2 * cosAH[i0]); // omcosAH[i0] = 1 - cosAH; // } template inline static Quaternion EstimateRPH(Real t, Quaternion const& q0, Quaternion const& q1, Quaternion const& qh, Real omcosAH) { static_assert(1 <= N && N <= 16, "Invalid degree."); Real f0, f1; Real twoT = t * (Real)2; if (twoT <= (Real)1) { ChebyshevRatio::template GetEstimate(twoT, omcosAH, f0, f1); return q0 * f0 + qh * f1; } else { ChebyshevRatio::template GetEstimate(twoT - (Real)1, omcosAH, f0, f1); return qh * f0 + q1 * f1; } } }; }