ChebyshevRatio.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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/Math.h>
  9. // Let f(t,A) = sin(t*A)/sin(A). The slerp of quaternions q0 and q1 is
  10. // slerp(t,q0,q1) = f(1-t,A)*q0 + f(t,A)*q1.
  11. // Let y = 1-cos(A); we allow A in [0,pi], so y in [0,1]. As a function of y,
  12. // a series representation for f(t,y) is
  13. // f(t,y) = sum_{i=0}^{infinity} c_{i}(t) y^{i}
  14. // where c_0(t) = t, c_{i}(t) = c_{i-1}(t)*(i^2 - t^2)/(i*(2*i+1)) for i >= 1.
  15. // The c_{i}(t) are polynomials in t of degree 2*i+1. The document
  16. // https://www.geometrictools/com/Documentation/FastAndAccurateSlerp.pdf
  17. // derives an approximation
  18. // g(t,y) = sum_{i=0}^{n-1} c_{i}(t) y^{i} + (1+u_n) c_{n}(t) y^n
  19. // which has degree 2*n+1 in t and degree n in y.
  20. //
  21. // Given q0 and q1 such that cos(A) = dot(q0,q1) in [0,1], in which case
  22. // A in [0,pi/2], let qh = (q0+q1)/|q0 + q1| = slerp(1/2,q0,q1). Note that
  23. // |q0 + q1| = 2*cos(A/2) because
  24. // sin(A/2)/sin(A) = sin(A/2)/(2*sin(A/2)*cos(A/2)) = 1/(2*cos(A/2))
  25. // The angle between q0 and qh is the same as the angle between qh and q1,
  26. // namely, A/2 in [0,pi/4]. It may be shown that
  27. // +--
  28. // slerp(t,q0,q1) = | slerp(2*t,q0,qh), 0 <= t <= 1/2
  29. // | slerp(2*t-1,qh,q1), 1/2 <= t <= 1
  30. // +--
  31. // The slerp functions on the right-hand side involve angles in [0,pi/4], so
  32. // the approximation is more accurate for those evaluations than using the
  33. // original.
  34. //
  35. // TODO: The constants in GetEstimate are those of the published paper.
  36. // Modify these to match the aforementioned GT document.
  37. namespace WwiseGTE
  38. {
  39. template <typename Real>
  40. class ChebyshevRatio
  41. {
  42. public:
  43. // Compute f(t,A) = sin(t*A)/sin(A), where t in [0,1], A in [0,pi/2],
  44. // cosA = cos(A), f0 = f(1-t,A), and f1 = f(t,A).
  45. static void Get(Real t, Real cosA, Real& f0, Real& f1)
  46. {
  47. if (cosA < (Real)1)
  48. {
  49. // The angle A is in (0,pi/2].
  50. Real A = std::acos(cosA);
  51. Real invSinA = (Real)1 / std::sin(A);
  52. f0 = std::sin(((Real)1 - t) * A) * invSinA;
  53. f1 = std::sin(t * A) * invSinA;
  54. }
  55. else
  56. {
  57. // The angle theta is 0.
  58. f0 = (Real)1 - t;
  59. f1 = (Real)t;
  60. }
  61. }
  62. // Compute estimates for f(t,y) = sin(t*A)/sin(A), where t in [0,1],
  63. // A in [0,pi/2], y = 1 - cos(A), f0 is the estimate for f(1-t,y), and
  64. // f1 is the estimate for f(t,y). The approximating function is a
  65. // polynomial of two variables. The template parameter N must be in
  66. // {1..16}. The degree in t is 2*N+1 and the degree in Y is N.
  67. template <int N>
  68. static void GetEstimate(Real t, Real y, Real & f0, Real & f1)
  69. {
  70. static_assert(1 <= N && N <= 16, "Invalid degree.");
  71. // The ASM output shows that the constants/ in these arrays are
  72. // loaded to XMM registers as literal values, and only those
  73. // constants required for the specified degree D are loaded.
  74. // That is, the compiler does a good job of optimizing the code.
  75. Real const onePlusMu[16] =
  76. {
  77. (Real)1.62943436108234530,
  78. (Real)1.73965850021313961,
  79. (Real)1.79701067629566813,
  80. (Real)1.83291820510335812,
  81. (Real)1.85772477879039977,
  82. (Real)1.87596835698904785,
  83. (Real)1.88998444919711206,
  84. (Real)1.90110745351730037,
  85. (Real)1.91015881189952352,
  86. (Real)1.91767344933047190,
  87. (Real)1.92401541194159076,
  88. (Real)1.92944142668012797,
  89. (Real)1.93413793373091059,
  90. (Real)1.93824371262559758,
  91. (Real)1.94186426368404708,
  92. (Real)1.94508125972497303
  93. };
  94. Real const a[16] =
  95. {
  96. (N != 1 ? (Real)1 : onePlusMu[0]) / ((Real)1 * (Real)3),
  97. (N != 2 ? (Real)1 : onePlusMu[1]) / ((Real)2 * (Real)5),
  98. (N != 3 ? (Real)1 : onePlusMu[2]) / ((Real)3 * (Real)7),
  99. (N != 4 ? (Real)1 : onePlusMu[3]) / ((Real)4 * (Real)9),
  100. (N != 5 ? (Real)1 : onePlusMu[4]) / ((Real)5 * (Real)11),
  101. (N != 6 ? (Real)1 : onePlusMu[5]) / ((Real)6 * (Real)13),
  102. (N != 7 ? (Real)1 : onePlusMu[6]) / ((Real)7 * (Real)15),
  103. (N != 8 ? (Real)1 : onePlusMu[7]) / ((Real)8 * (Real)17),
  104. (N != 9 ? (Real)1 : onePlusMu[8]) / ((Real)9 * (Real)19),
  105. (N != 10 ? (Real)1 : onePlusMu[9]) / ((Real)10 * (Real)21),
  106. (N != 11 ? (Real)1 : onePlusMu[10]) / ((Real)11 * (Real)23),
  107. (N != 12 ? (Real)1 : onePlusMu[11]) / ((Real)12 * (Real)25),
  108. (N != 13 ? (Real)1 : onePlusMu[12]) / ((Real)13 * (Real)27),
  109. (N != 14 ? (Real)1 : onePlusMu[13]) / ((Real)14 * (Real)29),
  110. (N != 15 ? (Real)1 : onePlusMu[14]) / ((Real)15 * (Real)31),
  111. (N != 16 ? (Real)1 : onePlusMu[15]) / ((Real)16 * (Real)33)
  112. };
  113. Real const b[16] =
  114. {
  115. (N != 1 ? (Real)1 : onePlusMu[0]) * (Real)1 / (Real)3,
  116. (N != 2 ? (Real)1 : onePlusMu[1]) * (Real)2 / (Real)5,
  117. (N != 3 ? (Real)1 : onePlusMu[2]) * (Real)3 / (Real)7,
  118. (N != 4 ? (Real)1 : onePlusMu[3]) * (Real)4 / (Real)9,
  119. (N != 5 ? (Real)1 : onePlusMu[4]) * (Real)5 / (Real)11,
  120. (N != 6 ? (Real)1 : onePlusMu[5]) * (Real)6 / (Real)13,
  121. (N != 7 ? (Real)1 : onePlusMu[6]) * (Real)7 / (Real)15,
  122. (N != 8 ? (Real)1 : onePlusMu[7]) * (Real)8 / (Real)17,
  123. (N != 9 ? (Real)1 : onePlusMu[8]) * (Real)9 / (Real)19,
  124. (N != 10 ? (Real)1 : onePlusMu[9]) * (Real)10 / (Real)21,
  125. (N != 11 ? (Real)1 : onePlusMu[10]) * (Real)11 / (Real)23,
  126. (N != 12 ? (Real)1 : onePlusMu[11]) * (Real)12 / (Real)25,
  127. (N != 13 ? (Real)1 : onePlusMu[12]) * (Real)13 / (Real)27,
  128. (N != 14 ? (Real)1 : onePlusMu[13]) * (Real)14 / (Real)29,
  129. (N != 15 ? (Real)1 : onePlusMu[14]) * (Real)15 / (Real)31,
  130. (N != 16 ? (Real)1 : onePlusMu[15]) * (Real)16 / (Real)33
  131. };
  132. Real term0 = (Real)1 - t, term1 = t;
  133. Real sqr0 = term0 * term0, sqr1 = term1 * term1;
  134. f0 = term0;
  135. f1 = term1;
  136. for (int i = 0; i < N; ++i)
  137. {
  138. term0 *= (b[i] - a[i] * sqr0) * y;
  139. term1 *= (b[i] - a[i] * sqr1) * y;
  140. f0 += term0;
  141. f1 += term1;
  142. }
  143. }
  144. };
  145. }