Quaternion.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. #if REAL_T_IS_DOUBLE
  2. using real_t = System.Double;
  3. #else
  4. using real_t = System.Single;
  5. #endif
  6. using System;
  7. using System.Runtime.InteropServices;
  8. namespace Godot
  9. {
  10. /// <summary>
  11. /// A unit quaternion used for representing 3D rotations.
  12. /// Quaternions need to be normalized to be used for rotation.
  13. ///
  14. /// It is similar to Basis, which implements matrix representation of
  15. /// rotations, and can be parametrized using both an axis-angle pair
  16. /// or Euler angles. Basis stores rotation, scale, and shearing,
  17. /// while Quaternion only stores rotation.
  18. ///
  19. /// Due to its compactness and the way it is stored in memory, certain
  20. /// operations (obtaining axis-angle and performing SLERP, in particular)
  21. /// are more efficient and robust against floating-point errors.
  22. /// </summary>
  23. [Serializable]
  24. [StructLayout(LayoutKind.Sequential)]
  25. public struct Quaternion : IEquatable<Quaternion>
  26. {
  27. /// <summary>
  28. /// X component of the quaternion (imaginary `i` axis part).
  29. /// Quaternion components should usually not be manipulated directly.
  30. /// </summary>
  31. public real_t x;
  32. /// <summary>
  33. /// Y component of the quaternion (imaginary `j` axis part).
  34. /// Quaternion components should usually not be manipulated directly.
  35. /// </summary>
  36. public real_t y;
  37. /// <summary>
  38. /// Z component of the quaternion (imaginary `k` axis part).
  39. /// Quaternion components should usually not be manipulated directly.
  40. /// </summary>
  41. public real_t z;
  42. /// <summary>
  43. /// W component of the quaternion (real part).
  44. /// Quaternion components should usually not be manipulated directly.
  45. /// </summary>
  46. public real_t w;
  47. /// <summary>
  48. /// Access quaternion components using their index.
  49. /// </summary>
  50. /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`, `[3]` is equivalent to `.w`.</value>
  51. public real_t this[int index]
  52. {
  53. get
  54. {
  55. switch (index)
  56. {
  57. case 0:
  58. return x;
  59. case 1:
  60. return y;
  61. case 2:
  62. return z;
  63. case 3:
  64. return w;
  65. default:
  66. throw new IndexOutOfRangeException();
  67. }
  68. }
  69. set
  70. {
  71. switch (index)
  72. {
  73. case 0:
  74. x = value;
  75. break;
  76. case 1:
  77. y = value;
  78. break;
  79. case 2:
  80. z = value;
  81. break;
  82. case 3:
  83. w = value;
  84. break;
  85. default:
  86. throw new IndexOutOfRangeException();
  87. }
  88. }
  89. }
  90. /// <summary>
  91. /// Returns the length (magnitude) of the quaternion.
  92. /// </summary>
  93. /// <value>Equivalent to `Mathf.Sqrt(LengthSquared)`.</value>
  94. public real_t Length
  95. {
  96. get { return Mathf.Sqrt(LengthSquared); }
  97. }
  98. /// <summary>
  99. /// Returns the squared length (squared magnitude) of the quaternion.
  100. /// This method runs faster than <see cref="Length"/>, so prefer it if
  101. /// you need to compare quaternions or need the squared length for some formula.
  102. /// </summary>
  103. /// <value>Equivalent to `Dot(this)`.</value>
  104. public real_t LengthSquared
  105. {
  106. get { return Dot(this); }
  107. }
  108. /// <summary>
  109. /// Returns the angle between this quaternion and `to`.
  110. /// This is the magnitude of the angle you would need to rotate
  111. /// by to get from one to the other.
  112. ///
  113. /// Note: This method has an abnormally high amount
  114. /// of floating-point error, so methods such as
  115. /// <see cref="Mathf.IsZeroApprox"/> will not work reliably.
  116. /// </summary>
  117. /// <param name="to">The other quaternion.</param>
  118. /// <returns>The angle between the quaternions.</returns>
  119. public real_t AngleTo(Quaternion to)
  120. {
  121. real_t dot = Dot(to);
  122. return Mathf.Acos(Mathf.Clamp(dot * dot * 2 - 1, -1, 1));
  123. }
  124. /// <summary>
  125. /// Performs a cubic spherical interpolation between quaternions `preA`,
  126. /// this vector, `b`, and `postB`, by the given amount `t`.
  127. /// </summary>
  128. /// <param name="b">The destination quaternion.</param>
  129. /// <param name="preA">A quaternion before this quaternion.</param>
  130. /// <param name="postB">A quaternion after `b`.</param>
  131. /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
  132. /// <returns>The interpolated quaternion.</returns>
  133. public Quaternion CubicSlerp(Quaternion b, Quaternion preA, Quaternion postB, real_t weight)
  134. {
  135. real_t t2 = (1.0f - weight) * weight * 2f;
  136. Quaternion sp = Slerp(b, weight);
  137. Quaternion sq = preA.Slerpni(postB, weight);
  138. return sp.Slerpni(sq, t2);
  139. }
  140. /// <summary>
  141. /// Returns the dot product of two quaternions.
  142. /// </summary>
  143. /// <param name="b">The other quaternion.</param>
  144. /// <returns>The dot product.</returns>
  145. public real_t Dot(Quaternion b)
  146. {
  147. return (x * b.x) + (y * b.y) + (z * b.z) + (w * b.w);
  148. }
  149. /// <summary>
  150. /// Returns Euler angles (in the YXZ convention: when decomposing,
  151. /// first Z, then X, and Y last) corresponding to the rotation
  152. /// represented by the unit quaternion. Returned vector contains
  153. /// the rotation angles in the format (X angle, Y angle, Z angle).
  154. /// </summary>
  155. /// <returns>The Euler angle representation of this quaternion.</returns>
  156. public Vector3 GetEuler()
  157. {
  158. #if DEBUG
  159. if (!IsNormalized())
  160. {
  161. throw new InvalidOperationException("Quaternion is not normalized");
  162. }
  163. #endif
  164. var basis = new Basis(this);
  165. return basis.GetEuler();
  166. }
  167. /// <summary>
  168. /// Returns the inverse of the quaternion.
  169. /// </summary>
  170. /// <returns>The inverse quaternion.</returns>
  171. public Quaternion Inverse()
  172. {
  173. #if DEBUG
  174. if (!IsNormalized())
  175. {
  176. throw new InvalidOperationException("Quaternion is not normalized");
  177. }
  178. #endif
  179. return new Quaternion(-x, -y, -z, w);
  180. }
  181. /// <summary>
  182. /// Returns whether the quaternion is normalized or not.
  183. /// </summary>
  184. /// <returns>A bool for whether the quaternion is normalized or not.</returns>
  185. public bool IsNormalized()
  186. {
  187. return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
  188. }
  189. /// <summary>
  190. /// Returns a copy of the quaternion, normalized to unit length.
  191. /// </summary>
  192. /// <returns>The normalized quaternion.</returns>
  193. public Quaternion Normalized()
  194. {
  195. return this / Length;
  196. }
  197. /// <summary>
  198. /// Returns the result of the spherical linear interpolation between
  199. /// this quaternion and `to` by amount `weight`.
  200. ///
  201. /// Note: Both quaternions must be normalized.
  202. /// </summary>
  203. /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
  204. /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
  205. /// <returns>The resulting quaternion of the interpolation.</returns>
  206. public Quaternion Slerp(Quaternion to, real_t weight)
  207. {
  208. #if DEBUG
  209. if (!IsNormalized())
  210. {
  211. throw new InvalidOperationException("Quaternion is not normalized");
  212. }
  213. if (!to.IsNormalized())
  214. {
  215. throw new ArgumentException("Argument is not normalized", nameof(to));
  216. }
  217. #endif
  218. // Calculate cosine.
  219. real_t cosom = x * to.x + y * to.y + z * to.z + w * to.w;
  220. var to1 = new Quaternion();
  221. // Adjust signs if necessary.
  222. if (cosom < 0.0)
  223. {
  224. cosom = -cosom;
  225. to1.x = -to.x;
  226. to1.y = -to.y;
  227. to1.z = -to.z;
  228. to1.w = -to.w;
  229. }
  230. else
  231. {
  232. to1.x = to.x;
  233. to1.y = to.y;
  234. to1.z = to.z;
  235. to1.w = to.w;
  236. }
  237. real_t sinom, scale0, scale1;
  238. // Calculate coefficients.
  239. if (1.0 - cosom > Mathf.Epsilon)
  240. {
  241. // Standard case (Slerp).
  242. real_t omega = Mathf.Acos(cosom);
  243. sinom = Mathf.Sin(omega);
  244. scale0 = Mathf.Sin((1.0f - weight) * omega) / sinom;
  245. scale1 = Mathf.Sin(weight * omega) / sinom;
  246. }
  247. else
  248. {
  249. // Quaternions are very close so we can do a linear interpolation.
  250. scale0 = 1.0f - weight;
  251. scale1 = weight;
  252. }
  253. // Calculate final values.
  254. return new Quaternion
  255. (
  256. (scale0 * x) + (scale1 * to1.x),
  257. (scale0 * y) + (scale1 * to1.y),
  258. (scale0 * z) + (scale1 * to1.z),
  259. (scale0 * w) + (scale1 * to1.w)
  260. );
  261. }
  262. /// <summary>
  263. /// Returns the result of the spherical linear interpolation between
  264. /// this quaternion and `to` by amount `weight`, but without
  265. /// checking if the rotation path is not bigger than 90 degrees.
  266. /// </summary>
  267. /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
  268. /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
  269. /// <returns>The resulting quaternion of the interpolation.</returns>
  270. public Quaternion Slerpni(Quaternion to, real_t weight)
  271. {
  272. real_t dot = Dot(to);
  273. if (Mathf.Abs(dot) > 0.9999f)
  274. {
  275. return this;
  276. }
  277. real_t theta = Mathf.Acos(dot);
  278. real_t sinT = 1.0f / Mathf.Sin(theta);
  279. real_t newFactor = Mathf.Sin(weight * theta) * sinT;
  280. real_t invFactor = Mathf.Sin((1.0f - weight) * theta) * sinT;
  281. return new Quaternion
  282. (
  283. (invFactor * x) + (newFactor * to.x),
  284. (invFactor * y) + (newFactor * to.y),
  285. (invFactor * z) + (newFactor * to.z),
  286. (invFactor * w) + (newFactor * to.w)
  287. );
  288. }
  289. /// <summary>
  290. /// Returns a vector transformed (multiplied) by this quaternion.
  291. /// </summary>
  292. /// <param name="v">A vector to transform.</param>
  293. /// <returns>The transformed vector.</returns>
  294. public Vector3 Xform(Vector3 v)
  295. {
  296. #if DEBUG
  297. if (!IsNormalized())
  298. {
  299. throw new InvalidOperationException("Quaternion is not normalized");
  300. }
  301. #endif
  302. var u = new Vector3(x, y, z);
  303. Vector3 uv = u.Cross(v);
  304. return v + (((uv * w) + u.Cross(uv)) * 2);
  305. }
  306. // Constants
  307. private static readonly Quaternion _identity = new Quaternion(0, 0, 0, 1);
  308. /// <summary>
  309. /// The identity quaternion, representing no rotation.
  310. /// Equivalent to an identity <see cref="Basis"/> matrix. If a vector is transformed by
  311. /// an identity quaternion, it will not change.
  312. /// </summary>
  313. /// <value>Equivalent to `new Quaternion(0, 0, 0, 1)`.</value>
  314. public static Quaternion Identity { get { return _identity; } }
  315. /// <summary>
  316. /// Constructs a quaternion defined by the given values.
  317. /// </summary>
  318. /// <param name="x">X component of the quaternion (imaginary `i` axis part).</param>
  319. /// <param name="y">Y component of the quaternion (imaginary `j` axis part).</param>
  320. /// <param name="z">Z component of the quaternion (imaginary `k` axis part).</param>
  321. /// <param name="w">W component of the quaternion (real part).</param>
  322. public Quaternion(real_t x, real_t y, real_t z, real_t w)
  323. {
  324. this.x = x;
  325. this.y = y;
  326. this.z = z;
  327. this.w = w;
  328. }
  329. /// <summary>
  330. /// Constructs a quaternion from the given quaternion.
  331. /// </summary>
  332. /// <param name="q">The existing quaternion.</param>
  333. public Quaternion(Quaternion q)
  334. {
  335. this = q;
  336. }
  337. /// <summary>
  338. /// Constructs a quaternion from the given <see cref="Basis"/>.
  339. /// </summary>
  340. /// <param name="basis">The basis to construct from.</param>
  341. public Quaternion(Basis basis)
  342. {
  343. this = basis.Quaternion();
  344. }
  345. /// <summary>
  346. /// Constructs a quaternion that will perform a rotation specified by
  347. /// Euler angles (in the YXZ convention: when decomposing,
  348. /// first Z, then X, and Y last),
  349. /// given in the vector format as (X angle, Y angle, Z angle).
  350. /// </summary>
  351. /// <param name="eulerYXZ"></param>
  352. public Quaternion(Vector3 eulerYXZ)
  353. {
  354. real_t halfA1 = eulerYXZ.y * 0.5f;
  355. real_t halfA2 = eulerYXZ.x * 0.5f;
  356. real_t halfA3 = eulerYXZ.z * 0.5f;
  357. // R = Y(a1).X(a2).Z(a3) convention for Euler angles.
  358. // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
  359. // a3 is the angle of the first rotation, following the notation in this reference.
  360. real_t cosA1 = Mathf.Cos(halfA1);
  361. real_t sinA1 = Mathf.Sin(halfA1);
  362. real_t cosA2 = Mathf.Cos(halfA2);
  363. real_t sinA2 = Mathf.Sin(halfA2);
  364. real_t cosA3 = Mathf.Cos(halfA3);
  365. real_t sinA3 = Mathf.Sin(halfA3);
  366. x = (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3);
  367. y = (sinA1 * cosA2 * cosA3) - (cosA1 * sinA2 * sinA3);
  368. z = (cosA1 * cosA2 * sinA3) - (sinA1 * sinA2 * cosA3);
  369. w = (sinA1 * sinA2 * sinA3) + (cosA1 * cosA2 * cosA3);
  370. }
  371. /// <summary>
  372. /// Constructs a quaternion that will rotate around the given axis
  373. /// by the specified angle. The axis must be a normalized vector.
  374. /// </summary>
  375. /// <param name="axis">The axis to rotate around. Must be normalized.</param>
  376. /// <param name="angle">The angle to rotate, in radians.</param>
  377. public Quaternion(Vector3 axis, real_t angle)
  378. {
  379. #if DEBUG
  380. if (!axis.IsNormalized())
  381. {
  382. throw new ArgumentException("Argument is not normalized", nameof(axis));
  383. }
  384. #endif
  385. real_t d = axis.Length();
  386. if (d == 0f)
  387. {
  388. x = 0f;
  389. y = 0f;
  390. z = 0f;
  391. w = 0f;
  392. }
  393. else
  394. {
  395. real_t sinAngle = Mathf.Sin(angle * 0.5f);
  396. real_t cosAngle = Mathf.Cos(angle * 0.5f);
  397. real_t s = sinAngle / d;
  398. x = axis.x * s;
  399. y = axis.y * s;
  400. z = axis.z * s;
  401. w = cosAngle;
  402. }
  403. }
  404. public static Quaternion operator *(Quaternion left, Quaternion right)
  405. {
  406. return new Quaternion
  407. (
  408. (left.w * right.x) + (left.x * right.w) + (left.y * right.z) - (left.z * right.y),
  409. (left.w * right.y) + (left.y * right.w) + (left.z * right.x) - (left.x * right.z),
  410. (left.w * right.z) + (left.z * right.w) + (left.x * right.y) - (left.y * right.x),
  411. (left.w * right.w) - (left.x * right.x) - (left.y * right.y) - (left.z * right.z)
  412. );
  413. }
  414. public static Quaternion operator +(Quaternion left, Quaternion right)
  415. {
  416. return new Quaternion(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w);
  417. }
  418. public static Quaternion operator -(Quaternion left, Quaternion right)
  419. {
  420. return new Quaternion(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w);
  421. }
  422. public static Quaternion operator -(Quaternion left)
  423. {
  424. return new Quaternion(-left.x, -left.y, -left.z, -left.w);
  425. }
  426. public static Quaternion operator *(Quaternion left, Vector3 right)
  427. {
  428. return new Quaternion
  429. (
  430. (left.w * right.x) + (left.y * right.z) - (left.z * right.y),
  431. (left.w * right.y) + (left.z * right.x) - (left.x * right.z),
  432. (left.w * right.z) + (left.x * right.y) - (left.y * right.x),
  433. -(left.x * right.x) - (left.y * right.y) - (left.z * right.z)
  434. );
  435. }
  436. public static Quaternion operator *(Vector3 left, Quaternion right)
  437. {
  438. return new Quaternion
  439. (
  440. (right.w * left.x) + (right.y * left.z) - (right.z * left.y),
  441. (right.w * left.y) + (right.z * left.x) - (right.x * left.z),
  442. (right.w * left.z) + (right.x * left.y) - (right.y * left.x),
  443. -(right.x * left.x) - (right.y * left.y) - (right.z * left.z)
  444. );
  445. }
  446. public static Quaternion operator *(Quaternion left, real_t right)
  447. {
  448. return new Quaternion(left.x * right, left.y * right, left.z * right, left.w * right);
  449. }
  450. public static Quaternion operator *(real_t left, Quaternion right)
  451. {
  452. return new Quaternion(right.x * left, right.y * left, right.z * left, right.w * left);
  453. }
  454. public static Quaternion operator /(Quaternion left, real_t right)
  455. {
  456. return left * (1.0f / right);
  457. }
  458. public static bool operator ==(Quaternion left, Quaternion right)
  459. {
  460. return left.Equals(right);
  461. }
  462. public static bool operator !=(Quaternion left, Quaternion right)
  463. {
  464. return !left.Equals(right);
  465. }
  466. public override bool Equals(object obj)
  467. {
  468. if (obj is Quaternion)
  469. {
  470. return Equals((Quaternion)obj);
  471. }
  472. return false;
  473. }
  474. public bool Equals(Quaternion other)
  475. {
  476. return x == other.x && y == other.y && z == other.z && w == other.w;
  477. }
  478. /// <summary>
  479. /// Returns true if this quaternion and `other` are approximately equal, by running
  480. /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
  481. /// </summary>
  482. /// <param name="other">The other quaternion to compare.</param>
  483. /// <returns>Whether or not the quaternions are approximately equal.</returns>
  484. public bool IsEqualApprox(Quaternion other)
  485. {
  486. return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
  487. }
  488. public override int GetHashCode()
  489. {
  490. return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
  491. }
  492. public override string ToString()
  493. {
  494. return $"({x}, {y}, {z}, {w})";
  495. }
  496. public string ToString(string format)
  497. {
  498. return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}, {w.ToString(format)})";
  499. }
  500. }
  501. }