310 lines
9.6 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright: (c) 2010-2014 Sam Hocevar <sam@hocevar.net>
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the Do What The Fuck You Want To
  7. // Public License, Version 2, as published by Sam Hocevar. See
  8. // http://www.wtfpl.net/ for more details.
  9. //
  10. #include <lol/engine-internal.h>
  11. namespace lol
  12. {
  13. static inline void MatrixToQuat(quat &that, mat3 const &m)
  14. {
  15. /* See http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/christian.htm for a version with no branches */
  16. float t = m[0][0] + m[1][1] + m[2][2];
  17. if (t > 0)
  18. {
  19. that.w = 0.5f * std::sqrt(1.0f + t);
  20. float s = 0.25f / that.w;
  21. that.x = s * (m[1][2] - m[2][1]);
  22. that.y = s * (m[2][0] - m[0][2]);
  23. that.z = s * (m[0][1] - m[1][0]);
  24. }
  25. else if (m[0][0] > m[1][1] && m[0][0] > m[2][2])
  26. {
  27. that.x = 0.5f * std::sqrt(1.0f + m[0][0] - m[1][1] - m[2][2]);
  28. float s = 0.25f / that.x;
  29. that.y = s * (m[0][1] + m[1][0]);
  30. that.z = s * (m[2][0] + m[0][2]);
  31. that.w = s * (m[1][2] - m[2][1]);
  32. }
  33. else if (m[1][1] > m[2][2])
  34. {
  35. that.y = 0.5f * std::sqrt(1.0f - m[0][0] + m[1][1] - m[2][2]);
  36. float s = 0.25f / that.y;
  37. that.x = s * (m[0][1] + m[1][0]);
  38. that.z = s * (m[1][2] + m[2][1]);
  39. that.w = s * (m[2][0] - m[0][2]);
  40. }
  41. else
  42. {
  43. that.z = 0.5f * std::sqrt(1.0f - m[0][0] - m[1][1] + m[2][2]);
  44. float s = 0.25f / that.z;
  45. that.x = s * (m[2][0] + m[0][2]);
  46. that.y = s * (m[1][2] + m[2][1]);
  47. that.w = s * (m[0][1] - m[1][0]);
  48. }
  49. }
  50. template<> quat::quat_t(mat3 const &m)
  51. {
  52. MatrixToQuat(*this, m);
  53. }
  54. template<> quat::quat_t(mat4 const &m)
  55. {
  56. MatrixToQuat(*this, mat3(m));
  57. }
  58. template<> quat quat::rotate(float degrees, vec3 const &v)
  59. {
  60. float half_angle = radians(degrees) * 0.5f;
  61. vec3 tmp = normalize(v) * sin(half_angle);
  62. return quat(cos(half_angle), tmp.x, tmp.y, tmp.z);
  63. }
  64. template<> quat quat::rotate(float degrees, float x, float y, float z)
  65. {
  66. return quat::rotate(degrees, vec3(x, y, z));
  67. }
  68. template<> quat quat::rotate(vec3 const &src, vec3 const &dst)
  69. {
  70. /* Algorithm directly taken from Sam Hocevar's article "Quaternion from
  71. * two vectors: the final version".
  72. * http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final */
  73. float magnitude = lol::sqrt(sqlength(src) * sqlength(dst));
  74. float real_part = magnitude + dot(src, dst);
  75. vec3 w;
  76. if (real_part < 1.e-6f * magnitude)
  77. {
  78. /* If src and dst are exactly opposite, rotate 180 degrees
  79. * around an arbitrary orthogonal axis. Axis normalisation
  80. * can happen later, when we normalise the quaternion. */
  81. real_part = 0.0f;
  82. w = abs(src.x) > abs(src.z) ? vec3(-src.y, src.x, 0.f)
  83. : vec3(0.f, -src.z, src.y);
  84. }
  85. else
  86. {
  87. /* Otherwise, build quaternion the standard way. */
  88. w = cross(src, dst);
  89. }
  90. return normalize(quat(real_part, w.x, w.y, w.z));
  91. }
  92. template<> quat slerp(quat const &qa, quat const &qb, float f)
  93. {
  94. float const magnitude = lol::sqrt(sqlength(qa) * sqlength(qb));
  95. float const product = lol::dot(qa, qb) / magnitude;
  96. /* If quaternions are equal or opposite, there is no need
  97. * to slerp anything, just return qa. */
  98. if (std::abs(product) >= 1.0f)
  99. return qa;
  100. float const sign = (product < 0.0f) ? -1.0f : 1.0f;
  101. float const theta = lol::acos(sign * product);
  102. float const s1 = lol::sin(sign * f * theta);
  103. float const s0 = lol::sin((1.0f - f) * theta);
  104. /* This is the same as 1/sin(theta) */
  105. float const d = 1.0f / lol::sqrt(1.f - product * product);
  106. return qa * (s0 * d) + qb * (s1 * d);
  107. }
  108. static inline vec3 quat_toeuler_generic(quat const &q, int i, int j, int k)
  109. {
  110. float n = norm(q);
  111. if (!n)
  112. return vec3::zero;
  113. /* (2 + i - j) % 3 means x-y-z direct order; otherwise indirect */
  114. float const sign = ((2 + i - j) % 3) ? 1.f : -1.f;
  115. vec3 ret;
  116. /* k == i means X-Y-X style Euler angles; otherwise we’re
  117. * actually handling X-Y-Z style Tait-Bryan angles. */
  118. if (k == i)
  119. {
  120. k = 3 - i - j;
  121. ret[0] = atan2(q[1 + i] * q[1 + j] + sign * (q.w * q[1 + k]),
  122. q.w * q[1 + j] - sign * (q[1 + i] * q[1 + k]));
  123. ret[1] = acos(2.f * (sq(q.w) + sq(q[1 + i])) - 1.f);
  124. ret[2] = atan2(q[1 + i] * q[1 + j] - sign * (q.w * q[1 + k]),
  125. q.w * q[1 + j] + sign * (q[1 + i] * q[1 + k]));
  126. }
  127. else
  128. {
  129. ret[0] = atan2(2.f * (q.w * q[1 + i] - sign * (q[1 + j] * q[1 + k])),
  130. 1.f - 2.f * (sq(q[1 + i]) + sq(q[1 + j])));
  131. ret[1] = asin(2.f * (q.w * q[1 + j] + sign * (q[1 + i] * q[1 + k])));
  132. ret[2] = atan2(2.f * (q.w * q[1 + k] - sign * (q[1 + j] * q[1 + i])),
  133. 1.f - 2.f * (sq(q[1 + k]) + sq(q[1 + j])));
  134. }
  135. return degrees(ret / n);
  136. }
  137. static inline mat3 mat3_fromeuler_generic(vec3 const &v, int i, int j, int k)
  138. {
  139. mat3 ret;
  140. vec3 const w = radians(v);
  141. float const s0 = sin(w[0]), c0 = cos(w[0]);
  142. float const s1 = sin(w[1]), c1 = cos(w[1]);
  143. float const s2 = sin(w[2]), c2 = cos(w[2]);
  144. /* (2 + i - j) % 3 means x-y-z direct order; otherwise indirect */
  145. float const sign = ((2 + i - j) % 3) ? 1.f : -1.f;
  146. /* k == i means X-Y-X style Euler angles; otherwise we’re
  147. * actually handling X-Y-Z style Tait-Bryan angles. */
  148. if (k == i)
  149. {
  150. k = 3 - i - j;
  151. ret[i][i] = c1;
  152. ret[i][j] = s0 * s1;
  153. ret[i][k] = - sign * (c0 * s1);
  154. ret[j][i] = s1 * s2;
  155. ret[j][j] = c0 * c2 - s0 * c1 * s2;
  156. ret[j][k] = sign * (s0 * c2 + c0 * c1 * s2);
  157. ret[k][i] = sign * (s1 * c2);
  158. ret[k][j] = - sign * (c0 * s2 + s0 * c1 * c2);
  159. ret[k][k] = - s0 * s2 + c0 * c1 * c2;
  160. }
  161. else
  162. {
  163. ret[i][i] = c1 * c2;
  164. ret[i][j] = sign * (c0 * s2) + s0 * s1 * c2;
  165. ret[i][k] = s0 * s2 - sign * (c0 * s1 * c2);
  166. ret[j][i] = - sign * (c1 * s2);
  167. ret[j][j] = c0 * c2 - sign * (s0 * s1 * s2);
  168. ret[j][k] = sign * (s0 * c2) + c0 * s1 * s2;
  169. ret[k][i] = sign * s1;
  170. ret[k][j] = - sign * (s0 * c1);
  171. ret[k][k] = c0 * c1;
  172. }
  173. return ret;
  174. }
  175. static inline quat quat_fromeuler_generic(vec3 const &v, int i, int j, int k)
  176. {
  177. vec3 const half_angles = radians(v * 0.5f);
  178. float const s0 = sin(half_angles[0]), c0 = cos(half_angles[0]);
  179. float const s1 = sin(half_angles[1]), c1 = cos(half_angles[1]);
  180. float const s2 = sin(half_angles[2]), c2 = cos(half_angles[2]);
  181. quat ret;
  182. /* (2 + i - j) % 3 means x-y-z direct order; otherwise indirect */
  183. float const sign = ((2 + i - j) % 3) ? 1.f : -1.f;
  184. /* k == i means X-Y-X style Euler angles; otherwise we’re
  185. * actually handling X-Y-Z style Tait-Bryan angles. */
  186. if (k == i)
  187. {
  188. k = 3 - i - j;
  189. ret[0] = c1 * (c0 * c2 - s0 * s2);
  190. ret[1 + i] = c1 * (c0 * s2 + s0 * c2);
  191. ret[1 + j] = s1 * (c0 * c2 + s0 * s2);
  192. ret[1 + k] = sign * (s1 * (s0 * c2 - c0 * s2));
  193. }
  194. else
  195. {
  196. ret[0] = c0 * c1 * c2 - sign * (s0 * s1 * s2);
  197. ret[1 + i] = s0 * c1 * c2 + sign * (c0 * s1 * s2);
  198. ret[1 + j] = c0 * s1 * c2 - sign * (s0 * c1 * s2);
  199. ret[1 + k] = c0 * c1 * s2 + sign * (s0 * s1 * c2);
  200. }
  201. return ret;
  202. }
  203. #define DEFINE_GENERIC_EULER_CONVERSIONS(a1, a2, a3) \
  204. DEFINE_GENERIC_EULER_CONVERSIONS_INNER(a1, a2, a3, a1##a2##a3) \
  205. #define DEFINE_GENERIC_EULER_CONVERSIONS_INNER(a1, a2, a3, name) \
  206. /* Create quaternions from Euler angles */ \
  207. template<> quat quat::fromeuler_##name(vec3 const &v) \
  208. { \
  209. int x = 0, y = 1, z = 2; UNUSED(x, y, z); \
  210. return quat_fromeuler_generic(v, a1, a2, a3); \
  211. } \
  212. \
  213. template<> quat quat::fromeuler_##name(float phi, float theta, float psi) \
  214. { \
  215. return quat::fromeuler_##name(vec3(phi, theta, psi)); \
  216. } \
  217. \
  218. /* Create 3×3 matrices from Euler angles */ \
  219. template<> mat3 mat3::fromeuler_##name(vec3 const &v) \
  220. { \
  221. int x = 0, y = 1, z = 2; UNUSED(x, y, z); \
  222. return mat3_fromeuler_generic(v, a1, a2, a3); \
  223. } \
  224. \
  225. template<> mat3 mat3::fromeuler_##name(float phi, float theta, float psi) \
  226. { \
  227. return mat3::fromeuler_##name(vec3(phi, theta, psi)); \
  228. } \
  229. \
  230. /* Create 4×4 matrices from Euler angles */ \
  231. template<> mat4 mat4::fromeuler_##name(vec3 const &v) \
  232. { \
  233. int x = 0, y = 1, z = 2; UNUSED(x, y, z); \
  234. return mat4(mat3_fromeuler_generic(v, a1, a2, a3), 1.f); \
  235. } \
  236. \
  237. template<> mat4 mat4::fromeuler_##name(float phi, float theta, float psi) \
  238. { \
  239. return mat4::fromeuler_##name(vec3(phi, theta, psi)); \
  240. } \
  241. \
  242. /* Retrieve Euler angles from a quaternion */ \
  243. template<> vec3 vec3::toeuler_##name(quat const &q) \
  244. { \
  245. int x = 0, y = 1, z = 2; UNUSED(x, y, z); \
  246. return quat_toeuler_generic(q, a1, a2, a3); \
  247. }
  248. DEFINE_GENERIC_EULER_CONVERSIONS(x, y, x)
  249. DEFINE_GENERIC_EULER_CONVERSIONS(x, z, x)
  250. DEFINE_GENERIC_EULER_CONVERSIONS(y, x, y)
  251. DEFINE_GENERIC_EULER_CONVERSIONS(y, z, y)
  252. DEFINE_GENERIC_EULER_CONVERSIONS(z, x, z)
  253. DEFINE_GENERIC_EULER_CONVERSIONS(z, y, z)
  254. DEFINE_GENERIC_EULER_CONVERSIONS(x, y, z)
  255. DEFINE_GENERIC_EULER_CONVERSIONS(x, z, y)
  256. DEFINE_GENERIC_EULER_CONVERSIONS(y, x, z)
  257. DEFINE_GENERIC_EULER_CONVERSIONS(y, z, x)
  258. DEFINE_GENERIC_EULER_CONVERSIONS(z, x, y)
  259. DEFINE_GENERIC_EULER_CONVERSIONS(z, y, x)
  260. #undef DEFINE_GENERIC_EULER_CONVERSIONS
  261. #undef DEFINE_GENERIC_EULER_CONVERSIONS_INNER
  262. } /* namespace lol */