SlerpEstimate.h 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/Quaternion.h>
  9. // The spherical linear interpolation (slerp) of unit-length quaternions
  10. // q0 and q1 for t in [0,1] and theta in (0,pi) is
  11. // slerp(t,q0,q1) = [sin((1-t)*theta)*q0 + sin(theta)*q1]/sin(theta)
  12. // where theta is the angle between q0 and q1 [cos(theta) = Dot(q0,q1)].
  13. // This function is a parameterization of the great spherical arc between
  14. // q0 and q1 on the unit hypersphere. Moreover, the parameterization is
  15. // one of normalized arclength--a particle traveling along the arc through
  16. // time t does so with constant speed.
  17. //
  18. // Read the comments in GteChebyshevRatio.h regarding estimates for the
  19. // ratio sin(t*theta)/sin(theta).
  20. //
  21. // When using slerp in animations involving sequences of quaternions, it is
  22. // typical that the quaternions are preprocessed so that consecutive ones
  23. // form an acute angle A in [0,pi/2]. Other preprocessing can help with
  24. // performance. See the function comments in the SLERP class.
  25. namespace WwiseGTE
  26. {
  27. template <typename Real>
  28. class SLERP
  29. {
  30. public:
  31. // The angle between q0 and q1 is in [0,pi). There are no angle
  32. // restrictions and nothing is precomputed.
  33. template <int N>
  34. inline static Quaternion<Real> Estimate(Real t, Quaternion<Real> const& q0, Quaternion<Real> const& q1)
  35. {
  36. static_assert(1 <= N && N <= 16, "Invalid degree.");
  37. Real cs = Dot(q0, q1);
  38. Real sign;
  39. if (cs >= (Real)0)
  40. {
  41. sign = (Real)1;
  42. }
  43. else
  44. {
  45. cs = -cs;
  46. sign = (Real)-1;
  47. }
  48. Real f0, f1;
  49. ChebyshevRatio<Real>::template GetEstimate<N>(t, (Real)1 - cs, f0, f1);
  50. return q0 * f0 + q1 * (sign * f1);
  51. }
  52. // The angle between q0 and q1 must be in [0,pi/2]. The suffix R is
  53. // for 'Restricted'. The preprocessing code is
  54. // Quaternion<Real> q[n]; // assuming initialized
  55. // for (i0 = 0, i1 = 1; i1 < n; i0 = i1++)
  56. // {
  57. // cosA = Dot(q[i0], q[i1]);
  58. // if (cosA < 0)
  59. // {
  60. // q[i1] = -q[i1]; // now Dot(q[i0], q[i]1) >= 0
  61. // }
  62. // }
  63. template <int N>
  64. inline static Quaternion<Real> EstimateR(Real t, Quaternion<Real> const& q0, Quaternion<Real> const& q1)
  65. {
  66. static_assert(1 <= N && N <= 16, "Invalid degree.");
  67. Real f0, f1;
  68. ChebyshevRatio<Real>::template GetEstimate<N>(t, (Real)1 - Dot(q0, q1), f0, f1);
  69. return q0 * f0 + q1 * f1;
  70. }
  71. // The angle between q0 and q1 must be in [0,pi/2]. The suffix R is
  72. // for 'Restricted' and the suffix P is for 'Preprocessed'. The
  73. // preprocessing code is
  74. // Quaternion<Real> q[n]; // assuming initialized
  75. // Real cosA[n-1], omcosA[n-1]; // to be precomputed
  76. // for (i0 = 0, i1 = 1; i1 < n; i0 = i1++)
  77. // {
  78. // cs = Dot(q[i0], q[i1]);
  79. // if (cosA[i0] < 0)
  80. // {
  81. // q[i1] = -q[i1];
  82. // cs = -cs;
  83. // }
  84. //
  85. // // for Quaterion<T>::SlerpRP
  86. // cosA[i0] = cs;
  87. //
  88. // // for SLERP<T>::EstimateRP
  89. // omcosA[i0] = 1 - cs;
  90. // }
  91. template <int N>
  92. inline static Quaternion<Real> EstimateRP(Real t, Quaternion<Real> const& q0, Quaternion<Real> const& q1,
  93. Real omcosA)
  94. {
  95. static_assert(1 <= N && N <= 16, "Invalid degree.");
  96. Real f0, f1;
  97. ChebyshevRatio<Real>::template GetEstimate<N>(t, omcosA, f0, f1);
  98. return q0 * f0 + q1 * f1;
  99. }
  100. // The angle between q0 and q1 is A and must be in [0,pi/2].
  101. // Quaternion qh is slerp(1/2,q0,q1) = (q0+q1)/|q0+q1|, so the angle
  102. // between q0 and qh is A/2 and the angle between qh and q1 is A/2.
  103. // The preprocessing code is
  104. // Quaternion<Real> q[n]; // assuming initialized
  105. // Quaternion<Real> qh[n-1]; // to be precomputed
  106. // Real omcosAH[n-1]; // to be precomputed
  107. // for (i0 = 0, i1 = 1; i1 < n; i0 = i1++)
  108. // {
  109. // cosA = Dot(q[i0], q[i1]);
  110. // if (cosA < 0)
  111. // {
  112. // q[i1] = -q[i1];
  113. // cosA = -cosA;
  114. // }
  115. // cosAH = sqrt((1 + cosA)/2);
  116. // qh[i0] = (q0 + q1) / (2 * cosAH[i0]);
  117. // omcosAH[i0] = 1 - cosAH;
  118. // }
  119. template <int N>
  120. inline static Quaternion<Real> EstimateRPH(Real t, Quaternion<Real> const& q0, Quaternion<Real> const& q1,
  121. Quaternion<Real> const& qh, Real omcosAH)
  122. {
  123. static_assert(1 <= N && N <= 16, "Invalid degree.");
  124. Real f0, f1;
  125. Real twoT = t * (Real)2;
  126. if (twoT <= (Real)1)
  127. {
  128. ChebyshevRatio<Real>::template GetEstimate<N>(twoT, omcosAH, f0, f1);
  129. return q0 * f0 + qh * f1;
  130. }
  131. else
  132. {
  133. ChebyshevRatio<Real>::template GetEstimate<N>(twoT - (Real)1, omcosAH, f0, f1);
  134. return qh * f0 + q1 * f1;
  135. }
  136. }
  137. };
  138. }