Quat.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. using System;
  2. using System.Runtime.InteropServices;
  3. #if REAL_T_IS_DOUBLE
  4. using real_t = System.Double;
  5. #else
  6. using real_t = System.Single;
  7. #endif
  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 Quat 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 Quat : IEquatable<Quat>
  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. /// Performs a cubic spherical interpolation between quaternions `preA`,
  110. /// this vector, `b`, and `postB`, by the given amount `t`.
  111. /// </summary>
  112. /// <param name="b">The destination quaternion.</param>
  113. /// <param name="preA">A quaternion before this quaternion.</param>
  114. /// <param name="postB">A quaternion after `b`.</param>
  115. /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
  116. /// <returns>The interpolated quaternion.</returns>
  117. public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t weight)
  118. {
  119. real_t t = weight;
  120. real_t t2 = (1.0f - t) * t * 2f;
  121. Quat sp = Slerp(b, t);
  122. Quat sq = preA.Slerpni(postB, t);
  123. return sp.Slerpni(sq, t2);
  124. }
  125. /// <summary>
  126. /// Returns the dot product of two quaternions.
  127. /// </summary>
  128. /// <param name="b">The other quaternion.</param>
  129. /// <returns>The dot product.</returns>
  130. public real_t Dot(Quat b)
  131. {
  132. return x * b.x + y * b.y + z * b.z + w * b.w;
  133. }
  134. /// <summary>
  135. /// Returns Euler angles (in the YXZ convention: when decomposing,
  136. /// first Z, then X, and Y last) corresponding to the rotation
  137. /// represented by the unit quaternion. Returned vector contains
  138. /// the rotation angles in the format (X angle, Y angle, Z angle).
  139. /// </summary>
  140. /// <returns>The Euler angle representation of this quaternion.</returns>
  141. public Vector3 GetEuler()
  142. {
  143. #if DEBUG
  144. if (!IsNormalized())
  145. {
  146. throw new InvalidOperationException("Quat is not normalized");
  147. }
  148. #endif
  149. var basis = new Basis(this);
  150. return basis.GetEuler();
  151. }
  152. /// <summary>
  153. /// Returns the inverse of the quaternion.
  154. /// </summary>
  155. /// <returns>The inverse quaternion.</returns>
  156. public Quat Inverse()
  157. {
  158. #if DEBUG
  159. if (!IsNormalized())
  160. {
  161. throw new InvalidOperationException("Quat is not normalized");
  162. }
  163. #endif
  164. return new Quat(-x, -y, -z, w);
  165. }
  166. /// <summary>
  167. /// Returns whether the quaternion is normalized or not.
  168. /// </summary>
  169. /// <returns>A bool for whether the quaternion is normalized or not.</returns>
  170. public bool IsNormalized()
  171. {
  172. return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
  173. }
  174. /// <summary>
  175. /// Returns a copy of the quaternion, normalized to unit length.
  176. /// </summary>
  177. /// <returns>The normalized quaternion.</returns>
  178. public Quat Normalized()
  179. {
  180. return this / Length;
  181. }
  182. [Obsolete("Set is deprecated. Use the Quat(" + nameof(real_t) + ", " + nameof(real_t) + ", " + nameof(real_t) + ", " + nameof(real_t) + ") constructor instead.", error: true)]
  183. public void Set(real_t x, real_t y, real_t z, real_t w)
  184. {
  185. this.x = x;
  186. this.y = y;
  187. this.z = z;
  188. this.w = w;
  189. }
  190. [Obsolete("Set is deprecated. Use the Quat(" + nameof(Quat) + ") constructor instead.", error: true)]
  191. public void Set(Quat q)
  192. {
  193. this = q;
  194. }
  195. [Obsolete("SetAxisAngle is deprecated. Use the Quat(" + nameof(Vector3) + ", " + nameof(real_t) + ") constructor instead.", error: true)]
  196. public void SetAxisAngle(Vector3 axis, real_t angle)
  197. {
  198. this = new Quat(axis, angle);
  199. }
  200. [Obsolete("SetEuler is deprecated. Use the Quat(" + nameof(Vector3) + ") constructor instead.", error: true)]
  201. public void SetEuler(Vector3 eulerYXZ)
  202. {
  203. this = new Quat(eulerYXZ);
  204. }
  205. /// <summary>
  206. /// Returns the result of the spherical linear interpolation between
  207. /// this quaternion and `to` by amount `weight`.
  208. ///
  209. /// Note: Both quaternions must be normalized.
  210. /// </summary>
  211. /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
  212. /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
  213. /// <returns>The resulting quaternion of the interpolation.</returns>
  214. public Quat Slerp(Quat to, real_t weight)
  215. {
  216. #if DEBUG
  217. if (!IsNormalized())
  218. {
  219. throw new InvalidOperationException("Quat is not normalized");
  220. }
  221. if (!to.IsNormalized())
  222. {
  223. throw new ArgumentException("Argument is not normalized", nameof(to));
  224. }
  225. #endif
  226. // Calculate cosine.
  227. real_t cosom = x * to.x + y * to.y + z * to.z + w * to.w;
  228. var to1 = new Quat();
  229. // Adjust signs if necessary.
  230. if (cosom < 0.0)
  231. {
  232. cosom = -cosom;
  233. to1.x = -to.x;
  234. to1.y = -to.y;
  235. to1.z = -to.z;
  236. to1.w = -to.w;
  237. }
  238. else
  239. {
  240. to1.x = to.x;
  241. to1.y = to.y;
  242. to1.z = to.z;
  243. to1.w = to.w;
  244. }
  245. real_t sinom, scale0, scale1;
  246. // Calculate coefficients.
  247. if (1.0 - cosom > Mathf.Epsilon)
  248. {
  249. // Standard case (Slerp).
  250. real_t omega = Mathf.Acos(cosom);
  251. sinom = Mathf.Sin(omega);
  252. scale0 = Mathf.Sin((1.0f - weight) * omega) / sinom;
  253. scale1 = Mathf.Sin(weight * omega) / sinom;
  254. }
  255. else
  256. {
  257. // Quaternions are very close so we can do a linear interpolation.
  258. scale0 = 1.0f - weight;
  259. scale1 = weight;
  260. }
  261. // Calculate final values.
  262. return new Quat
  263. (
  264. scale0 * x + scale1 * to1.x,
  265. scale0 * y + scale1 * to1.y,
  266. scale0 * z + scale1 * to1.z,
  267. scale0 * w + scale1 * to1.w
  268. );
  269. }
  270. /// <summary>
  271. /// Returns the result of the spherical linear interpolation between
  272. /// this quaternion and `to` by amount `weight`, but without
  273. /// checking if the rotation path is not bigger than 90 degrees.
  274. /// </summary>
  275. /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
  276. /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
  277. /// <returns>The resulting quaternion of the interpolation.</returns>
  278. public Quat Slerpni(Quat to, real_t weight)
  279. {
  280. real_t dot = Dot(to);
  281. if (Mathf.Abs(dot) > 0.9999f)
  282. {
  283. return this;
  284. }
  285. real_t theta = Mathf.Acos(dot);
  286. real_t sinT = 1.0f / Mathf.Sin(theta);
  287. real_t newFactor = Mathf.Sin(weight * theta) * sinT;
  288. real_t invFactor = Mathf.Sin((1.0f - weight) * theta) * sinT;
  289. return new Quat
  290. (
  291. invFactor * x + newFactor * to.x,
  292. invFactor * y + newFactor * to.y,
  293. invFactor * z + newFactor * to.z,
  294. invFactor * w + newFactor * to.w
  295. );
  296. }
  297. /// <summary>
  298. /// Returns a vector transformed (multiplied) by this quaternion.
  299. /// </summary>
  300. /// <param name="v">A vector to transform.</param>
  301. /// <returns>The transformed vector.</returns>
  302. public Vector3 Xform(Vector3 v)
  303. {
  304. #if DEBUG
  305. if (!IsNormalized())
  306. {
  307. throw new InvalidOperationException("Quat is not normalized");
  308. }
  309. #endif
  310. var u = new Vector3(x, y, z);
  311. Vector3 uv = u.Cross(v);
  312. return v + ((uv * w) + u.Cross(uv)) * 2;
  313. }
  314. // Constants
  315. private static readonly Quat _identity = new Quat(0, 0, 0, 1);
  316. /// <summary>
  317. /// The identity quaternion, representing no rotation.
  318. /// Equivalent to an identity <see cref="Basis"/> matrix. If a vector is transformed by
  319. /// an identity quaternion, it will not change.
  320. /// </summary>
  321. /// <value>Equivalent to `new Quat(0, 0, 0, 1)`.</value>
  322. public static Quat Identity { get { return _identity; } }
  323. /// <summary>
  324. /// Constructs a quaternion defined by the given values.
  325. /// </summary>
  326. /// <param name="x">X component of the quaternion (imaginary `i` axis part).</param>
  327. /// <param name="y">Y component of the quaternion (imaginary `j` axis part).</param>
  328. /// <param name="z">Z component of the quaternion (imaginary `k` axis part).</param>
  329. /// <param name="w">W component of the quaternion (real part).</param>
  330. public Quat(real_t x, real_t y, real_t z, real_t w)
  331. {
  332. this.x = x;
  333. this.y = y;
  334. this.z = z;
  335. this.w = w;
  336. }
  337. /// <summary>
  338. /// Constructs a quaternion from the given quaternion.
  339. /// </summary>
  340. /// <param name="q">The existing quaternion.</param>
  341. public Quat(Quat q)
  342. {
  343. this = q;
  344. }
  345. /// <summary>
  346. /// Constructs a quaternion from the given <see cref="Basis"/>.
  347. /// </summary>
  348. /// <param name="basis">The basis to construct from.</param>
  349. public Quat(Basis basis)
  350. {
  351. this = basis.Quat();
  352. }
  353. /// <summary>
  354. /// Constructs a quaternion that will perform a rotation specified by
  355. /// Euler angles (in the YXZ convention: when decomposing,
  356. /// first Z, then X, and Y last),
  357. /// given in the vector format as (X angle, Y angle, Z angle).
  358. /// </summary>
  359. /// <param name="eulerYXZ"></param>
  360. public Quat(Vector3 eulerYXZ)
  361. {
  362. real_t half_a1 = eulerYXZ.y * 0.5f;
  363. real_t half_a2 = eulerYXZ.x * 0.5f;
  364. real_t half_a3 = eulerYXZ.z * 0.5f;
  365. // R = Y(a1).X(a2).Z(a3) convention for Euler angles.
  366. // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
  367. // a3 is the angle of the first rotation, following the notation in this reference.
  368. real_t cos_a1 = Mathf.Cos(half_a1);
  369. real_t sin_a1 = Mathf.Sin(half_a1);
  370. real_t cos_a2 = Mathf.Cos(half_a2);
  371. real_t sin_a2 = Mathf.Sin(half_a2);
  372. real_t cos_a3 = Mathf.Cos(half_a3);
  373. real_t sin_a3 = Mathf.Sin(half_a3);
  374. x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3;
  375. y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3;
  376. z = cos_a1 * cos_a2 * sin_a3 - sin_a1 * sin_a2 * cos_a3;
  377. w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3;
  378. }
  379. /// <summary>
  380. /// Constructs a quaternion that will rotate around the given axis
  381. /// by the specified angle. The axis must be a normalized vector.
  382. /// </summary>
  383. /// <param name="axis">The axis to rotate around. Must be normalized.</param>
  384. /// <param name="angle">The angle to rotate, in radians.</param>
  385. public Quat(Vector3 axis, real_t angle)
  386. {
  387. #if DEBUG
  388. if (!axis.IsNormalized())
  389. {
  390. throw new ArgumentException("Argument is not normalized", nameof(axis));
  391. }
  392. #endif
  393. real_t d = axis.Length();
  394. if (d == 0f)
  395. {
  396. x = 0f;
  397. y = 0f;
  398. z = 0f;
  399. w = 0f;
  400. }
  401. else
  402. {
  403. real_t sinAngle = Mathf.Sin(angle * 0.5f);
  404. real_t cosAngle = Mathf.Cos(angle * 0.5f);
  405. real_t s = sinAngle / d;
  406. x = axis.x * s;
  407. y = axis.y * s;
  408. z = axis.z * s;
  409. w = cosAngle;
  410. }
  411. }
  412. public static Quat operator *(Quat left, Quat right)
  413. {
  414. return new Quat
  415. (
  416. left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y,
  417. left.w * right.y + left.y * right.w + left.z * right.x - left.x * right.z,
  418. left.w * right.z + left.z * right.w + left.x * right.y - left.y * right.x,
  419. left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z
  420. );
  421. }
  422. public static Quat operator +(Quat left, Quat right)
  423. {
  424. return new Quat(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w);
  425. }
  426. public static Quat operator -(Quat left, Quat right)
  427. {
  428. return new Quat(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w);
  429. }
  430. public static Quat operator -(Quat left)
  431. {
  432. return new Quat(-left.x, -left.y, -left.z, -left.w);
  433. }
  434. public static Quat operator *(Quat left, Vector3 right)
  435. {
  436. return new Quat
  437. (
  438. left.w * right.x + left.y * right.z - left.z * right.y,
  439. left.w * right.y + left.z * right.x - left.x * right.z,
  440. left.w * right.z + left.x * right.y - left.y * right.x,
  441. -left.x * right.x - left.y * right.y - left.z * right.z
  442. );
  443. }
  444. public static Quat operator *(Vector3 left, Quat right)
  445. {
  446. return new Quat
  447. (
  448. right.w * left.x + right.y * left.z - right.z * left.y,
  449. right.w * left.y + right.z * left.x - right.x * left.z,
  450. right.w * left.z + right.x * left.y - right.y * left.x,
  451. -right.x * left.x - right.y * left.y - right.z * left.z
  452. );
  453. }
  454. public static Quat operator *(Quat left, real_t right)
  455. {
  456. return new Quat(left.x * right, left.y * right, left.z * right, left.w * right);
  457. }
  458. public static Quat operator *(real_t left, Quat right)
  459. {
  460. return new Quat(right.x * left, right.y * left, right.z * left, right.w * left);
  461. }
  462. public static Quat operator /(Quat left, real_t right)
  463. {
  464. return left * (1.0f / right);
  465. }
  466. public static bool operator ==(Quat left, Quat right)
  467. {
  468. return left.Equals(right);
  469. }
  470. public static bool operator !=(Quat left, Quat right)
  471. {
  472. return !left.Equals(right);
  473. }
  474. public override bool Equals(object obj)
  475. {
  476. if (obj is Quat)
  477. {
  478. return Equals((Quat)obj);
  479. }
  480. return false;
  481. }
  482. public bool Equals(Quat other)
  483. {
  484. return x == other.x && y == other.y && z == other.z && w == other.w;
  485. }
  486. /// <summary>
  487. /// Returns true if this quaternion and `other` are approximately equal, by running
  488. /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
  489. /// </summary>
  490. /// <param name="other">The other quaternion to compare.</param>
  491. /// <returns>Whether or not the quaternions are approximately equal.</returns>
  492. public bool IsEqualApprox(Quat other)
  493. {
  494. return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
  495. }
  496. public override int GetHashCode()
  497. {
  498. return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
  499. }
  500. public override string ToString()
  501. {
  502. return String.Format("({0}, {1}, {2}, {3})", x.ToString(), y.ToString(), z.ToString(), w.ToString());
  503. }
  504. public string ToString(string format)
  505. {
  506. return String.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format));
  507. }
  508. }
  509. }