Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

596 строки
16 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2010—2020 Sam Hocevar <sam@hocevar.net>
  5. //
  6. // Lol Engine is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #pragma once
  13. //
  14. // The complex, quaternion and dual quaternion classes
  15. // ———————————————————————————————————————————————————
  16. //
  17. #include <lol/vector>
  18. #include <ostream> // std::ostream
  19. #include <cmath> // std::atan2, std::sqrt
  20. #include "ops.h"
  21. #include "matrix.h"
  22. namespace lol
  23. {
  24. /*
  25. * 2-element transforms: complex numbers
  26. */
  27. template<typename T>
  28. struct [[nodiscard]] cmplx_t : public linear_ops::base<T>
  29. {
  30. static int const count = 2;
  31. typedef T scalar_element;
  32. typedef T element;
  33. typedef cmplx_t<T> type;
  34. inline constexpr cmplx_t() = default;
  35. inline constexpr cmplx_t(cmplx_t<T> const &) = default;
  36. inline constexpr cmplx_t(T X) : x(X), y(T(0)) {}
  37. inline constexpr cmplx_t(T X, T Y) : x(X), y(Y) {}
  38. template<typename U>
  39. explicit inline constexpr cmplx_t(cmplx_t<U> const &z)
  40. : x(z.x), y(z.y) {}
  41. LOL_COMMON_MEMBER_OPS(x)
  42. inline cmplx_t<T> operator *(cmplx_t<T> const &val) const
  43. {
  44. return cmplx_t<T>(x * val.x - y * val.y, x * val.y + y * val.x);
  45. }
  46. inline cmplx_t<T> operator *=(cmplx_t<T> const &val)
  47. {
  48. return *this = (*this) * val;
  49. }
  50. inline cmplx_t<T> operator ~() const
  51. {
  52. return cmplx_t<T>(x, -y);
  53. }
  54. template<typename U>
  55. friend std::ostream &operator<<(std::ostream &stream, cmplx_t<U> const &v);
  56. T x, y;
  57. };
  58. /*
  59. * 4-element transforms: quaternions
  60. */
  61. template<typename T>
  62. struct [[nodiscard]] quat_t : public linear_ops::base<T>
  63. {
  64. static int const count = 4;
  65. typedef T scalar_element;
  66. typedef T element;
  67. typedef quat_t<T> type;
  68. /* Default constructor and copy constructor */
  69. inline constexpr quat_t() = default;
  70. inline constexpr quat_t(quat_t<T> const &) = default;
  71. /* Explicit constructor for type conversion */
  72. template<typename U>
  73. explicit inline constexpr quat_t(quat_t<U> const &q)
  74. : w(q.w), x(q.x), y(q.y), z(q.z) {}
  75. /* Various explicit constructors */
  76. explicit inline constexpr quat_t(T W, T X, T Y, T Z)
  77. : w(W), x(X), y(Y), z(Z) {}
  78. explicit inline constexpr quat_t(T W)
  79. : w(W), x(0), y(0), z(0) {}
  80. /* Construct a unit quaternion from a pure rotation matrix */
  81. explicit quat_t(mat_t<T,3,3> const &m)
  82. {
  83. using std::sqrt;
  84. T tr = m[0][0] + m[1][1] + m[2][2];
  85. if (tr > T(0))
  86. {
  87. T const p = T(0.5) * sqrt(T(1) + tr);
  88. T const q = T(0.25) / p;
  89. w = p;
  90. x = q * (m[1][2] - m[2][1]);
  91. y = q * (m[2][0] - m[0][2]);
  92. z = q * (m[0][1] - m[1][0]);
  93. }
  94. else
  95. {
  96. int i = (m[0][0] > m[1][1] && m[0][0] > m[2][2]) ? 0
  97. : (m[1][1] > m[2][2]) ? 1
  98. : 2;
  99. int j = (i + 1) % 3, k = (i + 2) % 3;
  100. T const p = T(0.5) * sqrt(T(1) - tr + m[i][i] + m[i][i]);
  101. T const q = T(0.25) / p;
  102. w = q * (m[j][k] - m[k][j]);
  103. (*this)[1 + i] = p;
  104. (*this)[1 + j] = q * (m[i][j] + m[j][i]);
  105. (*this)[1 + k] = q * (m[k][i] + m[i][k]);
  106. }
  107. }
  108. LOL_COMMON_MEMBER_OPS(w)
  109. inline quat_t operator *(quat_t const &val) const
  110. {
  111. vec_t<T,3> v1(x, y, z);
  112. vec_t<T,3> v2(val.x, val.y, val.z);
  113. vec_t<T,3> v3 = cross(v1, v2) + w * v2 + val.w * v1;
  114. return quat_t(w * val.w - dot(v1, v2), v3.x, v3.y, v3.z);
  115. }
  116. inline quat_t operator *=(quat_t const &val)
  117. {
  118. return *this = (*this * val);
  119. }
  120. /* Create a unit quaternion representing a rotation around an axis. */
  121. static quat_t rotate(T radians, T x, T y, T z);
  122. static quat_t rotate(T radians, vec_t<T,3> const &v);
  123. /* Create a unit quaternion representing a rotation between two vectors.
  124. * Input vectors need not be normalised. */
  125. static quat_t rotate(vec_t<T,3> const &src, vec_t<T,3> const &dst);
  126. /* Convert from Euler angles. The axes in fromeuler_xyx are
  127. * x, then y', then x", ie. the axes are attached to the model.
  128. * If you want to rotate around static axes, just reverse the order
  129. * of the arguments. Angle values are in radians. */
  130. static quat_t fromeuler_xyx(vec_t<T,3> const &v);
  131. static quat_t fromeuler_xzx(vec_t<T,3> const &v);
  132. static quat_t fromeuler_yxy(vec_t<T,3> const &v);
  133. static quat_t fromeuler_yzy(vec_t<T,3> const &v);
  134. static quat_t fromeuler_zxz(vec_t<T,3> const &v);
  135. static quat_t fromeuler_zyz(vec_t<T,3> const &v);
  136. static quat_t fromeuler_xyx(T phi, T theta, T psi);
  137. static quat_t fromeuler_xzx(T phi, T theta, T psi);
  138. static quat_t fromeuler_yxy(T phi, T theta, T psi);
  139. static quat_t fromeuler_yzy(T phi, T theta, T psi);
  140. static quat_t fromeuler_zxz(T phi, T theta, T psi);
  141. static quat_t fromeuler_zyz(T phi, T theta, T psi);
  142. /* Convert from Tait-Bryan angles (incorrectly called Euler angles,
  143. * but since everyone does it…). The axes in fromeuler_xyz are
  144. * x, then y', then z", ie. the axes are attached to the model.
  145. * If you want to apply yaw around x, pitch around y, and roll
  146. * around z, use fromeuler_xyz. Angle values are in radians.
  147. * If you want to rotate around static axes, reverse the order in
  148. * the function name (_zyx instead of _xyz) AND reverse the order
  149. * of the arguments. */
  150. static quat_t fromeuler_xyz(vec_t<T,3> const &v);
  151. static quat_t fromeuler_xzy(vec_t<T,3> const &v);
  152. static quat_t fromeuler_yxz(vec_t<T,3> const &v);
  153. static quat_t fromeuler_yzx(vec_t<T,3> const &v);
  154. static quat_t fromeuler_zxy(vec_t<T,3> const &v);
  155. static quat_t fromeuler_zyx(vec_t<T,3> const &v);
  156. static quat_t fromeuler_xyz(T phi, T theta, T psi);
  157. static quat_t fromeuler_xzy(T phi, T theta, T psi);
  158. static quat_t fromeuler_yxz(T phi, T theta, T psi);
  159. static quat_t fromeuler_yzx(T phi, T theta, T psi);
  160. static quat_t fromeuler_zxy(T phi, T theta, T psi);
  161. static quat_t fromeuler_zyx(T phi, T theta, T psi);
  162. inline quat_t operator ~() const
  163. {
  164. return quat_t(w, -x, -y, -z);
  165. }
  166. /* Transform vectors or points */
  167. inline vec_t<T,3> transform(vec_t<T,3> const &v) const
  168. {
  169. quat_t p = quat_t(0, v.x, v.y, v.z);
  170. quat_t q = *this * p / *this;
  171. return vec_t<T,3>(q.x, q.y, q.z);
  172. }
  173. inline vec_t<T,4> transform(vec_t<T,4> const &v) const
  174. {
  175. quat_t p = quat_t(0, v.x, v.y, v.z);
  176. quat_t q = *this * p / *this;
  177. return vec_t<T,4>(q.x, q.y, q.z, v.w);
  178. }
  179. inline vec_t<T,3> operator *(vec_t<T,3> const &v) const
  180. {
  181. return transform(v);
  182. }
  183. inline vec_t<T,4> operator *(vec_t<T,4> const &v) const
  184. {
  185. return transform(v);
  186. }
  187. inline vec_t<T,3> axis()
  188. {
  189. vec_t<T,3> v(x, y, z);
  190. T n2 = sqlength(v);
  191. if (n2 <= (T)1e-6)
  192. return vec_t<T,3>::axis_x;
  193. return normalize(v);
  194. }
  195. [[nodiscard]] inline T angle()
  196. {
  197. using std::atan2, std::sqrt;
  198. vec_t<T,3> v(x, y, z);
  199. T n2 = sqlength(v);
  200. if (n2 <= (T)1e-6)
  201. return (T)0;
  202. return (T)2 * atan2(sqrt(n2), w);
  203. }
  204. template<typename U>
  205. friend std::ostream &operator<<(std::ostream &stream, quat_t<U> const &v);
  206. /* XXX: storage order is wxyz, unlike vectors! */
  207. T w, x, y, z;
  208. };
  209. /*
  210. * SQT transforms: scale / rotation / translation
  211. */
  212. template<typename T>
  213. struct [[nodiscard]] sqt_t
  214. {
  215. /* Default constructor and copy constructor */
  216. inline constexpr sqt_t() = default;
  217. inline constexpr sqt_t(sqt_t<T> const &) = default;
  218. inline constexpr sqt_t<T>& operator =(const sqt_t<T>&) = default;
  219. /* Explicit constructor for type conversion */
  220. template<typename U>
  221. explicit inline constexpr sqt_t(sqt_t<U> const &other)
  222. : q(other.q), t(other.t), s(other.s) {}
  223. /* Various explicit constructors */
  224. explicit inline constexpr sqt_t(T const &s_,
  225. quat_t<T> const &q_,
  226. vec_t<T,3> const &t_)
  227. : q(q_), t(t_), s(s_) {}
  228. explicit inline constexpr sqt_t(T const &s_)
  229. : q(1.f), t(0.f), s(s_) {}
  230. explicit inline constexpr sqt_t(quat_t<T> const &q_)
  231. : q(q_), t(0.f), s(1.f) {}
  232. explicit inline constexpr sqt_t(vec_t<T,3> const &t_)
  233. : q(1.f), t(t_), s(1.f) {}
  234. /* Transform vectors or points */
  235. inline vec_t<T,3> transform(vec_t<T,3> const &v) const
  236. {
  237. return t + q.transform(s * v);
  238. }
  239. inline vec_t<T,4> transform(vec_t<T,4> const &v) const
  240. {
  241. // XXX: needs serious testing for w != 1
  242. vec_t<T,4> tmp = q.transform(vec_t<T,4>(s * v.xyz, v.w));
  243. return vec_t<T,4>(tmp.xyz, 0.f) + vec_t<T,4>(t, 1.f) * tmp.w;
  244. }
  245. inline vec_t<T,3> operator *(vec_t<T,3> const &v) const
  246. {
  247. return transform(v);
  248. }
  249. inline vec_t<T,4> operator *(vec_t<T,4> const &v) const
  250. {
  251. return transform(v);
  252. }
  253. /* Compose two SQTs together */
  254. inline sqt_t<T> operator *(sqt_t<T> const &other) const
  255. {
  256. return sqt_t<T>(s * other.s,
  257. q * other.q,
  258. transform(other.t));
  259. }
  260. quat_t<T> q;
  261. vec_t<T,3> t;
  262. T s;
  263. };
  264. /*
  265. * stdstream method implementations
  266. */
  267. template<typename U>
  268. std::ostream &operator<<(std::ostream &stream, cmplx_t<U> const &c)
  269. {
  270. return stream << "(" << c.x << ", " << c.y << ")";
  271. }
  272. template<typename U>
  273. std::ostream &operator<<(std::ostream &stream, quat_t<U> const &q)
  274. {
  275. return stream << "(" << q.w << ", " << q.x << ", "
  276. << q.y << ", " << q.z << ")";
  277. }
  278. /*
  279. * Common operations on transforms
  280. */
  281. template<typename T> [[nodiscard]]
  282. static inline T dot(cmplx_t<T> const &t1, cmplx_t<T> const &t2)
  283. {
  284. T ret(0);
  285. for (size_t i = 0; i < sizeof(t1) / sizeof(T); ++i)
  286. ret += t1[i] * t2[i];
  287. return ret;
  288. }
  289. template<typename T> [[nodiscard]]
  290. static inline T sqlength(cmplx_t<T> const &t)
  291. {
  292. return dot(t, t);
  293. }
  294. template<typename T> [[nodiscard]]
  295. static inline T length(cmplx_t<T> const &t)
  296. {
  297. using std::sqrt;
  298. /* FIXME: this is not very nice */
  299. return (T)sqrt((double)sqlength(t));
  300. }
  301. template<typename T> [[nodiscard]]
  302. static inline T norm(cmplx_t<T> const &t)
  303. {
  304. return length(t);
  305. }
  306. template<typename T>
  307. static inline cmplx_t<T> normalize(cmplx_t<T> const &z)
  308. {
  309. T norm = (T)length(z);
  310. return norm ? z / norm : cmplx_t<T>(T(0));
  311. }
  312. /* XXX: duplicate */
  313. template<typename T> [[nodiscard]]
  314. static inline T dot(quat_t<T> const &t1, quat_t<T> const &t2)
  315. {
  316. T ret(0);
  317. for (size_t i = 0; i < sizeof(t1) / sizeof(T); ++i)
  318. ret += t1[i] * t2[i];
  319. return ret;
  320. }
  321. template<typename T> [[nodiscard]]
  322. static inline T sqlength(quat_t<T> const &t)
  323. {
  324. return dot(t, t);
  325. }
  326. template<typename T> [[nodiscard]]
  327. static inline T length(quat_t<T> const &t)
  328. {
  329. using std::sqrt;
  330. /* FIXME: this is not very nice */
  331. return (T)sqrt(sqlength(t));
  332. }
  333. template<typename T> [[nodiscard]]
  334. static inline T norm(quat_t<T> const &t)
  335. {
  336. return length(t);
  337. }
  338. template<typename T>
  339. static inline quat_t<T> normalize(quat_t<T> const &z)
  340. {
  341. T norm = (T)length(z);
  342. return norm ? z / norm : quat_t<T>(T(0));
  343. }
  344. /*
  345. * Complex numbers only
  346. */
  347. template<typename T>
  348. static inline cmplx_t<T> inverse(cmplx_t<T> const &z)
  349. {
  350. return ~z / sqlength(z);
  351. }
  352. template<typename T>
  353. static inline cmplx_t<T> operator /(T a, cmplx_t<T> const &b)
  354. {
  355. return a * inverse(b);
  356. }
  357. template<typename T>
  358. static inline cmplx_t<T> operator /(cmplx_t<T> a, cmplx_t<T> const &b)
  359. {
  360. return a * inverse(b);
  361. }
  362. template<typename T> [[nodiscard]]
  363. static inline bool operator ==(cmplx_t<T> const &a, T b)
  364. {
  365. return (a.x == b) && !a.y;
  366. }
  367. template<typename T> [[nodiscard]]
  368. static inline bool operator !=(cmplx_t<T> const &a, T b)
  369. {
  370. return (a.x != b) || a.y;
  371. }
  372. template<typename T> [[nodiscard]]
  373. static inline bool operator ==(T a, cmplx_t<T> const &b) { return b == a; }
  374. template<typename T> [[nodiscard]]
  375. static inline bool operator !=(T a, cmplx_t<T> const &b) { return b != a; }
  376. /*
  377. * Quaternions only
  378. */
  379. template<typename T>
  380. static inline quat_t<T> inverse(quat_t<T> const &q)
  381. {
  382. return ~q / sqlength(q);
  383. }
  384. template<typename T>
  385. static inline quat_t<T> operator /(T x, quat_t<T> const &y)
  386. {
  387. return x * inverse(y);
  388. }
  389. template<typename T>
  390. static inline quat_t<T> operator /(quat_t<T> const &x, quat_t<T> const &y)
  391. {
  392. return x * inverse(y);
  393. }
  394. template<typename T>
  395. extern quat_t<T> slerp(quat_t<T> const &qa, quat_t<T> const &qb, T f);
  396. /*
  397. * SQTs only
  398. */
  399. template<typename T>
  400. static inline sqt_t<T> inverse(sqt_t<T> const &tr)
  401. {
  402. auto inv_s = T(1) / tr.s;
  403. auto inv_q = inverse(tr.q);
  404. return sqt_t<T>(inv_s, inv_q, inv_q * tr.t * -inv_s);
  405. }
  406. template<typename T>
  407. static inline sqt_t<T> operator /(sqt_t<T> const &x, sqt_t<T> const &y)
  408. {
  409. return x * inverse(y);
  410. }
  411. //
  412. // Generic GLSL-like type names
  413. //
  414. #define T_(tleft, tright, suffix) \
  415. typedef tleft half tright f16##suffix; \
  416. typedef tleft float tright suffix; \
  417. typedef tleft double tright d##suffix; \
  418. typedef tleft ldouble tright f128##suffix; \
  419. typedef tleft int8_t tright i8##suffix; \
  420. typedef tleft uint8_t tright u8##suffix; \
  421. typedef tleft int16_t tright i16##suffix; \
  422. typedef tleft uint16_t tright u16##suffix; \
  423. typedef tleft int32_t tright i##suffix; \
  424. typedef tleft uint32_t tright u##suffix; \
  425. typedef tleft int64_t tright i64##suffix; \
  426. typedef tleft uint64_t tright u64##suffix; \
  427. typedef tleft real tright r##suffix;
  428. /* Idiotic hack to put "," inside a macro argument */
  429. #define C_ ,
  430. T_(mat_t<, C_ 2 C_ 2>, mat2)
  431. T_(mat_t<, C_ 3 C_ 3>, mat3)
  432. T_(mat_t<, C_ 4 C_ 4>, mat4)
  433. T_(mat_t<, C_ 2 C_ 3>, mat2x3)
  434. T_(mat_t<, C_ 2 C_ 4>, mat2x4)
  435. T_(mat_t<, C_ 3 C_ 2>, mat3x2)
  436. T_(mat_t<, C_ 3 C_ 4>, mat3x4)
  437. T_(mat_t<, C_ 4 C_ 2>, mat4x2)
  438. T_(mat_t<, C_ 4 C_ 3>, mat4x3)
  439. T_(cmplx_t<, >, cmplx)
  440. T_(quat_t<, >, quat)
  441. T_(sqt_t<, >, sqt)
  442. #undef C_
  443. #undef T_
  444. static_assert(sizeof(cmplx) == 8, "sizeof(cmplx) == 8");
  445. static_assert(sizeof(dcmplx) == 16, "sizeof(dcmplx) == 16");
  446. static_assert(sizeof(quat) == 16, "sizeof(quat) == 16");
  447. static_assert(sizeof(dquat) == 32, "sizeof(dquat) == 32");
  448. static_assert(sizeof(imat2) == 16, "sizeof(imat2) == 16");
  449. static_assert(sizeof(mat2) == 16, "sizeof(mat2) == 16");
  450. static_assert(sizeof(dmat2) == 32, "sizeof(dmat2) == 32");
  451. static_assert(sizeof(imat3) == 36, "sizeof(imat3) == 36");
  452. static_assert(sizeof(mat3) == 36, "sizeof(mat3) == 36");
  453. static_assert(sizeof(dmat3) == 72, "sizeof(dmat3) == 72");
  454. static_assert(sizeof(imat4) == 64, "sizeof(imat4) == 64");
  455. static_assert(sizeof(mat4) == 64, "sizeof(mat4) == 64");
  456. static_assert(sizeof(dmat4) == 128, "sizeof(dmat4) == 128");
  457. //
  458. // HLSL/Cg-compliant type names
  459. //
  460. typedef mat2 float2x2;
  461. typedef mat3 float3x3;
  462. typedef mat4 float4x4;
  463. typedef mat2x3 float2x3;
  464. typedef mat2x4 float2x4;
  465. typedef mat3x2 float3x2;
  466. typedef mat3x4 float3x4;
  467. typedef mat4x2 float4x2;
  468. typedef mat4x3 float4x3;
  469. typedef f16mat2 half2x2;
  470. typedef f16mat3 half3x3;
  471. typedef f16mat4 half4x4;
  472. typedef f16mat2x3 half2x3;
  473. typedef f16mat2x4 half2x4;
  474. typedef f16mat3x2 half3x2;
  475. typedef f16mat3x4 half3x4;
  476. typedef f16mat4x2 half4x2;
  477. typedef f16mat4x3 half4x3;
  478. typedef imat2 int2x2;
  479. typedef imat3 int3x3;
  480. typedef imat4 int4x4;
  481. typedef imat2x3 int2x3;
  482. typedef imat2x4 int2x4;
  483. typedef imat3x2 int3x2;
  484. typedef imat3x4 int3x4;
  485. typedef imat4x2 int4x2;
  486. typedef imat4x3 int4x3;
  487. } // namespace lol
  488. #include "matrix.ipp"
  489. #include "transform.ipp"