Quaternion.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. #region --- License ---
  2. /*
  3. Copyright (c) 2006 - 2008 The Open Toolkit library.
  4. Permission is hereby granted, free of charge, to any person obtaining a copy of
  5. this software and associated documentation files (the "Software"), to deal in
  6. the Software without restriction, including without limitation the rights to
  7. use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  8. of the Software, and to permit persons to whom the Software is furnished to do
  9. so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in all
  11. copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  18. SOFTWARE.
  19. */
  20. #endregion
  21. using System;
  22. using System.Runtime.InteropServices;
  23. using System.ComponentModel;
  24. using System.Xml.Serialization;
  25. namespace AtomicEngine
  26. {
  27. /// <summary>
  28. /// Represents a Quaternion.
  29. /// </summary>
  30. [StructLayout(LayoutKind.Sequential)]
  31. public struct Quaternion : IEquatable<Quaternion>
  32. {
  33. #region Fields
  34. float w;
  35. Vector3 xyz;
  36. #endregion
  37. #region Constructors
  38. /// <summary>
  39. /// Construct a new Quaternion from vector and w components
  40. /// </summary>
  41. /// <param name="v">The vector part</param>
  42. /// <param name="w">The w part</param>
  43. public Quaternion(Vector3 v, float w)
  44. {
  45. this.xyz = v;
  46. this.w = w;
  47. }
  48. /// <summary>
  49. /// Construct a new Quaternion
  50. /// </summary>
  51. /// <param name="x">The x component</param>
  52. /// <param name="y">The y component</param>
  53. /// <param name="z">The z component</param>
  54. /// <param name="w">The w component</param>
  55. public Quaternion(float x, float y, float z, float w)
  56. : this(new Vector3(x, y, z), w)
  57. { }
  58. // From Euler angles
  59. public Quaternion (float x, float y, float z)
  60. {
  61. const float M_DEGTORAD_2 = (float)Math.PI / 360.0f;
  62. x *= M_DEGTORAD_2;
  63. y *= M_DEGTORAD_2;
  64. z *= M_DEGTORAD_2;
  65. float sinX = (float)Math.Sin(x);
  66. float cosX = (float)Math.Cos(x);
  67. float sinY = (float)Math.Sin(y);
  68. float cosY = (float)Math.Cos(y);
  69. float sinZ = (float)Math.Sin(z);
  70. float cosZ = (float)Math.Cos(z);
  71. xyz = new Vector3(cosY * sinX * cosZ + sinY * cosX * sinZ,
  72. sinY * cosX * cosZ - cosY * sinX * sinZ,
  73. cosY * cosX * sinZ - sinY * sinX * cosZ);
  74. w = cosY * cosX * cosZ + sinY * sinX * sinZ;
  75. }
  76. public Quaternion (ref Matrix3 matrix)
  77. {
  78. double scale = System.Math.Pow(matrix.Determinant, 1.0d / 3.0d);
  79. float x, y, z;
  80. w = (float) (System.Math.Sqrt(System.Math.Max(0, scale + matrix[0, 0] + matrix[1, 1] + matrix[2, 2])) / 2);
  81. x = (float) (System.Math.Sqrt(System.Math.Max(0, scale + matrix[0, 0] - matrix[1, 1] - matrix[2, 2])) / 2);
  82. y = (float) (System.Math.Sqrt(System.Math.Max(0, scale - matrix[0, 0] + matrix[1, 1] - matrix[2, 2])) / 2);
  83. z = (float) (System.Math.Sqrt(System.Math.Max(0, scale - matrix[0, 0] - matrix[1, 1] + matrix[2, 2])) / 2);
  84. xyz = new Vector3 (x, y, z);
  85. if (matrix[2, 1] - matrix[1, 2] < 0) X = -X;
  86. if (matrix[0, 2] - matrix[2, 0] < 0) Y = -Y;
  87. if (matrix[1, 0] - matrix[0, 1] < 0) Z = -Z;
  88. }
  89. #endregion
  90. #region Public Members
  91. #region Properties
  92. /// <summary>
  93. /// Gets or sets an OpenTK.Vector3 with the X, Y and Z components of this instance.
  94. /// </summary>
  95. [Obsolete("Use Xyz property instead.")]
  96. [CLSCompliant(false)]
  97. [EditorBrowsable(EditorBrowsableState.Never)]
  98. public Vector3 XYZ { get { return Xyz; } set { Xyz = value; } }
  99. /// <summary>
  100. /// Gets or sets an OpenTK.Vector3 with the X, Y and Z components of this instance.
  101. /// </summary>
  102. public Vector3 Xyz { get { return xyz; } set { xyz = value; } }
  103. /// <summary>
  104. /// Gets or sets the X component of this instance.
  105. /// </summary>
  106. public float X { get { return xyz.X; } set { xyz.X = value; } }
  107. /// <summary>
  108. /// Gets or sets the Y component of this instance.
  109. /// </summary>
  110. public float Y { get { return xyz.Y; } set { xyz.Y = value; } }
  111. /// <summary>
  112. /// Gets or sets the Z component of this instance.
  113. /// </summary>
  114. public float Z { get { return xyz.Z; } set { xyz.Z = value; } }
  115. /// <summary>
  116. /// Gets or sets the W component of this instance.
  117. /// </summary>
  118. public float W { get { return w; } set { w = value; } }
  119. #endregion
  120. #region Instance
  121. #region ToAxisAngle
  122. /// <summary>
  123. /// Convert the current quaternion to axis angle representation
  124. /// </summary>
  125. /// <param name="axis">The resultant axis</param>
  126. /// <param name="angle">The resultant angle</param>
  127. public void ToAxisAngle(out Vector3 axis, out float angle)
  128. {
  129. Vector4 result = ToAxisAngle();
  130. axis = result.Xyz;
  131. angle = result.W;
  132. }
  133. /// <summary>
  134. /// Convert this instance to an axis-angle representation.
  135. /// </summary>
  136. /// <returns>A Vector4 that is the axis-angle representation of this quaternion.</returns>
  137. public Vector4 ToAxisAngle()
  138. {
  139. Quaternion q = this;
  140. if (q.W > 1.0f)
  141. q.Normalize();
  142. Vector4 result = new Vector4();
  143. result.W = 2.0f * (float)System.Math.Acos(q.W); // angle
  144. float den = (float)System.Math.Sqrt(1.0 - q.W * q.W);
  145. if (den > 0.0001f)
  146. {
  147. result.Xyz = q.Xyz / den;
  148. }
  149. else
  150. {
  151. // This occurs when the angle is zero.
  152. // Not a problem: just set an arbitrary normalized axis.
  153. result.Xyz = Vector3.UnitX;
  154. }
  155. return result;
  156. }
  157. #endregion
  158. #region public float Length
  159. /// <summary>
  160. /// Gets the length (magnitude) of the quaternion.
  161. /// </summary>
  162. /// <seealso cref="LengthSquared"/>
  163. public float Length
  164. {
  165. get
  166. {
  167. return (float)System.Math.Sqrt(W * W + Xyz.LengthSquared);
  168. }
  169. }
  170. #endregion
  171. #region public float LengthSquared
  172. /// <summary>
  173. /// Gets the square of the quaternion length (magnitude).
  174. /// </summary>
  175. public float LengthSquared
  176. {
  177. get
  178. {
  179. return W * W + Xyz.LengthSquared;
  180. }
  181. }
  182. #endregion
  183. #region public void Normalize()
  184. /// <summary>
  185. /// Scales the Quaternion to unit length.
  186. /// </summary>
  187. public void Normalize()
  188. {
  189. float scale = 1.0f / this.Length;
  190. Xyz *= scale;
  191. W *= scale;
  192. }
  193. #endregion
  194. #region EulerAngles
  195. public Vector3 ToEulerAngles()
  196. {
  197. // Derivation from http://www.geometrictools.com/Documentation/EulerAngles.pdf
  198. // Order of rotations: Z first, then X, then Y
  199. float check = 2.0f*(-Y*Z + W*X);
  200. const float radToDeg = 180f/(float) Math.PI;
  201. if (check < -0.995f)
  202. {
  203. return new Vector3(-90f, 0f, -(float)Math.Atan2(2.0f * (X * Z - W * Y), 1.0f - 2.0f * (Y * Y + Z * Z)) * radToDeg);
  204. }
  205. else if (check > 0.995f)
  206. {
  207. return new Vector3(90f, 0f, (float)Math.Atan2(2.0f * (X * Z - W * Y), 1.0f - 2.0f * (Y * Y + Z * Z)) * radToDeg);
  208. }
  209. else
  210. {
  211. return new Vector3(
  212. (float)Math.Asin(check) * radToDeg,
  213. (float)Math.Atan2(2.0f * (X * Z - W * Y), 1.0f - 2.0f * (X * X + Y * Y)) * radToDeg,
  214. (float)Math.Atan2(2.0f * (X * Y - W * Z), 1.0f - 2.0f * (X * X + Z * Z)) * radToDeg);
  215. }
  216. }
  217. public float YawAngle => ToEulerAngles().Y;
  218. public float PitchAngle => ToEulerAngles().X;
  219. public float RollAngle => ToEulerAngles().Z;
  220. #endregion
  221. #region public void Conjugate()
  222. /// <summary>
  223. /// Convert this quaternion to its conjugate
  224. /// </summary>
  225. public void Conjugate()
  226. {
  227. Xyz = -Xyz;
  228. }
  229. #endregion
  230. #endregion
  231. #region Static
  232. #region Fields
  233. /// <summary>
  234. /// Defines the identity quaternion.
  235. /// </summary>
  236. public static Quaternion Identity = new Quaternion(0, 0, 0, 1);
  237. #endregion
  238. #region Add
  239. /// <summary>
  240. /// Add two quaternions
  241. /// </summary>
  242. /// <param name="left">The first operand</param>
  243. /// <param name="right">The second operand</param>
  244. /// <returns>The result of the addition</returns>
  245. public static Quaternion Add(Quaternion left, Quaternion right)
  246. {
  247. return new Quaternion(
  248. left.Xyz + right.Xyz,
  249. left.W + right.W);
  250. }
  251. /// <summary>
  252. /// Add two quaternions
  253. /// </summary>
  254. /// <param name="left">The first operand</param>
  255. /// <param name="right">The second operand</param>
  256. /// <param name="result">The result of the addition</param>
  257. public static void Add(ref Quaternion left, ref Quaternion right, out Quaternion result)
  258. {
  259. result = new Quaternion(
  260. left.Xyz + right.Xyz,
  261. left.W + right.W);
  262. }
  263. #endregion
  264. #region Sub
  265. /// <summary>
  266. /// Subtracts two instances.
  267. /// </summary>
  268. /// <param name="left">The left instance.</param>
  269. /// <param name="right">The right instance.</param>
  270. /// <returns>The result of the operation.</returns>
  271. public static Quaternion Sub(Quaternion left, Quaternion right)
  272. {
  273. return new Quaternion(
  274. left.Xyz - right.Xyz,
  275. left.W - right.W);
  276. }
  277. /// <summary>
  278. /// Subtracts two instances.
  279. /// </summary>
  280. /// <param name="left">The left instance.</param>
  281. /// <param name="right">The right instance.</param>
  282. /// <param name="result">The result of the operation.</param>
  283. public static void Sub(ref Quaternion left, ref Quaternion right, out Quaternion result)
  284. {
  285. result = new Quaternion(
  286. left.Xyz - right.Xyz,
  287. left.W - right.W);
  288. }
  289. #endregion
  290. #region Mult
  291. /// <summary>
  292. /// Multiplies two instances.
  293. /// </summary>
  294. /// <param name="left">The first instance.</param>
  295. /// <param name="right">The second instance.</param>
  296. /// <returns>A new instance containing the result of the calculation.</returns>
  297. [Obsolete("Use Multiply instead.")]
  298. public static Quaternion Mult(Quaternion left, Quaternion right)
  299. {
  300. return new Quaternion(
  301. right.W * left.Xyz + left.W * right.Xyz + Vector3.Cross(left.Xyz, right.Xyz),
  302. left.W * right.W - Vector3.Dot(left.Xyz, right.Xyz));
  303. }
  304. /// <summary>
  305. /// Multiplies two instances.
  306. /// </summary>
  307. /// <param name="left">The first instance.</param>
  308. /// <param name="right">The second instance.</param>
  309. /// <param name="result">A new instance containing the result of the calculation.</param>
  310. [Obsolete("Use Multiply instead.")]
  311. public static void Mult(ref Quaternion left, ref Quaternion right, out Quaternion result)
  312. {
  313. result = new Quaternion(
  314. right.W * left.Xyz + left.W * right.Xyz + Vector3.Cross(left.Xyz, right.Xyz),
  315. left.W * right.W - Vector3.Dot(left.Xyz, right.Xyz));
  316. }
  317. /// <summary>
  318. /// Multiplies two instances.
  319. /// </summary>
  320. /// <param name="left">The first instance.</param>
  321. /// <param name="right">The second instance.</param>
  322. /// <returns>A new instance containing the result of the calculation.</returns>
  323. public static Quaternion Multiply(Quaternion left, Quaternion right)
  324. {
  325. Quaternion result;
  326. Multiply(ref left, ref right, out result);
  327. return result;
  328. }
  329. /// <summary>
  330. /// Multiplies two instances.
  331. /// </summary>
  332. /// <param name="left">The first instance.</param>
  333. /// <param name="right">The second instance.</param>
  334. /// <param name="result">A new instance containing the result of the calculation.</param>
  335. public static void Multiply(ref Quaternion left, ref Quaternion right, out Quaternion result)
  336. {
  337. result = new Quaternion(
  338. right.W * left.Xyz + left.W * right.Xyz + Vector3.Cross(left.Xyz, right.Xyz),
  339. left.W * right.W - Vector3.Dot(left.Xyz, right.Xyz));
  340. }
  341. /// <summary>
  342. /// Multiplies an instance by a scalar.
  343. /// </summary>
  344. /// <param name="quaternion">The instance.</param>
  345. /// <param name="scale">The scalar.</param>
  346. /// <param name="result">A new instance containing the result of the calculation.</param>
  347. public static void Multiply(ref Quaternion quaternion, float scale, out Quaternion result)
  348. {
  349. result = new Quaternion(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
  350. }
  351. [Obsolete ("Use the overload without the ref float scale")]
  352. public static void Multiply(ref Quaternion quaternion, ref float scale, out Quaternion result)
  353. {
  354. result = new Quaternion(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
  355. }
  356. /// <summary>
  357. /// Multiplies an instance by a scalar.
  358. /// </summary>
  359. /// <param name="quaternion">The instance.</param>
  360. /// <param name="scale">The scalar.</param>
  361. /// <returns>A new instance containing the result of the calculation.</returns>
  362. public static Quaternion Multiply(Quaternion quaternion, float scale)
  363. {
  364. return new Quaternion(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
  365. }
  366. #endregion
  367. #region Conjugate
  368. /// <summary>
  369. /// Get the conjugate of the given quaternion
  370. /// </summary>
  371. /// <param name="q">The quaternion</param>
  372. /// <returns>The conjugate of the given quaternion</returns>
  373. public static Quaternion Conjugate(Quaternion q)
  374. {
  375. return new Quaternion(-q.Xyz, q.W);
  376. }
  377. /// <summary>
  378. /// Get the conjugate of the given quaternion
  379. /// </summary>
  380. /// <param name="q">The quaternion</param>
  381. /// <param name="result">The conjugate of the given quaternion</param>
  382. public static void Conjugate(ref Quaternion q, out Quaternion result)
  383. {
  384. result = new Quaternion(-q.Xyz, q.W);
  385. }
  386. #endregion
  387. #region Invert
  388. /// <summary>
  389. /// Get the inverse of the given quaternion
  390. /// </summary>
  391. /// <param name="q">The quaternion to invert</param>
  392. /// <returns>The inverse of the given quaternion</returns>
  393. public static Quaternion Invert(Quaternion q)
  394. {
  395. Quaternion result;
  396. Invert(ref q, out result);
  397. return result;
  398. }
  399. /// <summary>
  400. /// Get the inverse of the given quaternion
  401. /// </summary>
  402. /// <param name="q">The quaternion to invert</param>
  403. /// <param name="result">The inverse of the given quaternion</param>
  404. public static void Invert(ref Quaternion q, out Quaternion result)
  405. {
  406. float lengthSq = q.LengthSquared;
  407. if (lengthSq != 0.0)
  408. {
  409. float i = 1.0f / lengthSq;
  410. result = new Quaternion(q.Xyz * -i, q.W * i);
  411. }
  412. else
  413. {
  414. result = q;
  415. }
  416. }
  417. #endregion
  418. #region Normalize
  419. /// <summary>
  420. /// Scale the given quaternion to unit length
  421. /// </summary>
  422. /// <param name="q">The quaternion to normalize</param>
  423. /// <returns>The normalized quaternion</returns>
  424. public static Quaternion Normalize(Quaternion q)
  425. {
  426. Quaternion result;
  427. Normalize(ref q, out result);
  428. return result;
  429. }
  430. /// <summary>
  431. /// Scale the given quaternion to unit length
  432. /// </summary>
  433. /// <param name="q">The quaternion to normalize</param>
  434. /// <param name="result">The normalized quaternion</param>
  435. public static void Normalize(ref Quaternion q, out Quaternion result)
  436. {
  437. float scale = 1.0f / q.Length;
  438. result = new Quaternion(q.Xyz * scale, q.W * scale);
  439. }
  440. #endregion
  441. #region FromAxisAngle
  442. /// <summary>
  443. /// Build a quaternion from the given axis and angle
  444. /// </summary>
  445. /// <param name="axis">The axis to rotate about</param>
  446. /// <param name="angle">The rotation angle in radians</param>
  447. /// <returns></returns>
  448. public static Quaternion FromAxisAngle(Vector3 axis, float angle)
  449. {
  450. axis.Normalize();
  451. angle *= (float)Math.PI /360f;
  452. var sinAngle = (float)Math.Sin(angle);
  453. var cosAngle = (float)Math.Cos(angle);
  454. return new Quaternion(axis.X * sinAngle, axis.Y * sinAngle, axis.Z * sinAngle, cosAngle);
  455. }
  456. #endregion
  457. #region FromRotationTo
  458. public static Quaternion FromRotationTo(Vector3 start, Vector3 end)
  459. {
  460. Quaternion result = new Quaternion();
  461. start.Normalize();
  462. end.Normalize();
  463. const float epsilon = 0.000001f;
  464. float d = Vector3.Dot(start, end);
  465. if (d > -1.0f + epsilon)
  466. {
  467. Vector3 c = Vector3.Cross(start, end);
  468. float s = (float)Math.Sqrt((1.0f + d) * 2.0f);
  469. float invS = 1.0f / s;
  470. result.X = c.X * invS;
  471. result.Y = c.Y * invS;
  472. result.Z = c.Z * invS;
  473. result.W = 0.5f * s;
  474. }
  475. else
  476. {
  477. Vector3 axis = Vector3.Cross(Vector3.UnitX, start);
  478. if (axis.Length < epsilon)
  479. axis = Vector3.Cross(Vector3.UnitY, start);
  480. return FromAxisAngle(axis, 180.0f);
  481. }
  482. return result;
  483. }
  484. #endregion
  485. #region Slerp
  486. /// <summary>
  487. /// Do Spherical linear interpolation between two quaternions
  488. /// </summary>
  489. /// <param name="q1">The first quaternion</param>
  490. /// <param name="q2">The second quaternion</param>
  491. /// <param name="blend">The blend factor</param>
  492. /// <returns>A smooth blend between the given quaternions</returns>
  493. public static Quaternion Slerp(Quaternion q1, Quaternion q2, float blend)
  494. {
  495. // if either input is zero, return the other.
  496. if (q1.LengthSquared == 0.0f)
  497. {
  498. if (q2.LengthSquared == 0.0f)
  499. {
  500. return Identity;
  501. }
  502. return q2;
  503. }
  504. else if (q2.LengthSquared == 0.0f)
  505. {
  506. return q1;
  507. }
  508. float cosHalfAngle = q1.W * q2.W + Vector3.Dot(q1.Xyz, q2.Xyz);
  509. if (cosHalfAngle >= 1.0f || cosHalfAngle <= -1.0f)
  510. {
  511. // angle = 0.0f, so just return one input.
  512. return q1;
  513. }
  514. else if (cosHalfAngle < 0.0f)
  515. {
  516. q2.Xyz = -q2.Xyz;
  517. q2.W = -q2.W;
  518. cosHalfAngle = -cosHalfAngle;
  519. }
  520. float blendA;
  521. float blendB;
  522. if (cosHalfAngle < 0.99f)
  523. {
  524. // do proper slerp for big angles
  525. float halfAngle = (float)System.Math.Acos(cosHalfAngle);
  526. float sinHalfAngle = (float)System.Math.Sin(halfAngle);
  527. float oneOverSinHalfAngle = 1.0f / sinHalfAngle;
  528. blendA = (float)System.Math.Sin(halfAngle * (1.0f - blend)) * oneOverSinHalfAngle;
  529. blendB = (float)System.Math.Sin(halfAngle * blend) * oneOverSinHalfAngle;
  530. }
  531. else
  532. {
  533. // do lerp if angle is really small.
  534. blendA = 1.0f - blend;
  535. blendB = blend;
  536. }
  537. Quaternion result = new Quaternion(blendA * q1.Xyz + blendB * q2.Xyz, blendA * q1.W + blendB * q2.W);
  538. if (result.LengthSquared > 0.0f)
  539. return Normalize(result);
  540. else
  541. return Identity;
  542. }
  543. #endregion
  544. #endregion
  545. #region Operators
  546. /// <summary>
  547. /// Adds two instances.
  548. /// </summary>
  549. /// <param name="left">The first instance.</param>
  550. /// <param name="right">The second instance.</param>
  551. /// <returns>The result of the calculation.</returns>
  552. public static Quaternion operator +(Quaternion left, Quaternion right)
  553. {
  554. left.Xyz += right.Xyz;
  555. left.W += right.W;
  556. return left;
  557. }
  558. /// <summary>
  559. /// Subtracts two instances.
  560. /// </summary>
  561. /// <param name="left">The first instance.</param>
  562. /// <param name="right">The second instance.</param>
  563. /// <returns>The result of the calculation.</returns>
  564. public static Quaternion operator -(Quaternion left, Quaternion right)
  565. {
  566. left.Xyz -= right.Xyz;
  567. left.W -= right.W;
  568. return left;
  569. }
  570. /// <summary>
  571. /// Multiplies two instances.
  572. /// </summary>
  573. /// <param name="left">The first instance.</param>
  574. /// <param name="right">The second instance.</param>
  575. /// <returns>The result of the calculation.</returns>
  576. public static Quaternion operator *(Quaternion left, Quaternion right)
  577. {
  578. Multiply(ref left, ref right, out left);
  579. return left;
  580. }
  581. /// <summary>
  582. /// Multiplies an instance by a scalar.
  583. /// </summary>
  584. /// <param name="quaternion">The instance.</param>
  585. /// <param name="scale">The scalar.</param>
  586. /// <returns>A new instance containing the result of the calculation.</returns>
  587. public static Quaternion operator *(Quaternion quaternion, float scale)
  588. {
  589. Multiply(ref quaternion, scale, out quaternion);
  590. return quaternion;
  591. }
  592. /// <summary>
  593. /// Multiplies an instance by a scalar.
  594. /// </summary>
  595. /// <param name="quaternion">The instance.</param>
  596. /// <param name="scale">The scalar.</param>
  597. /// <returns>A new instance containing the result of the calculation.</returns>
  598. public static Quaternion operator *(float scale, Quaternion quaternion)
  599. {
  600. return new Quaternion(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
  601. }
  602. /// <summary>
  603. /// Multiplies an instance by a vector3.
  604. /// </summary>
  605. /// <param name="quaternion">The instance.</param>
  606. /// <param name="vector">The vector.</param>
  607. /// <returns>A new instance containing the result of the calculation.</returns>
  608. public static Vector3 operator *(Quaternion quaternion, Vector3 vector)
  609. {
  610. var qVec = new Vector3(quaternion.X, quaternion.Y, quaternion.Z);
  611. var cross1 = Vector3.Cross(qVec, vector);
  612. var cross2 = Vector3.Cross(qVec, cross1);
  613. return vector + 2.0f * (cross1 * quaternion.W + cross2);
  614. }
  615. /// <summary>
  616. /// Compares two instances for equality.
  617. /// </summary>
  618. /// <param name="left">The first instance.</param>
  619. /// <param name="right">The second instance.</param>
  620. /// <returns>True, if left equals right; false otherwise.</returns>
  621. public static bool operator ==(Quaternion left, Quaternion right)
  622. {
  623. return left.Equals(right);
  624. }
  625. /// <summary>
  626. /// Compares two instances for inequality.
  627. /// </summary>
  628. /// <param name="left">The first instance.</param>
  629. /// <param name="right">The second instance.</param>
  630. /// <returns>True, if left does not equal right; false otherwise.</returns>
  631. public static bool operator !=(Quaternion left, Quaternion right)
  632. {
  633. return !left.Equals(right);
  634. }
  635. #endregion
  636. #region Overrides
  637. #region public override string ToString()
  638. /// <summary>
  639. /// Returns a System.String that represents the current Quaternion.
  640. /// </summary>
  641. /// <returns></returns>
  642. public override string ToString()
  643. {
  644. return String.Format("V: {0}, W: {1}", Xyz, W);
  645. }
  646. #endregion
  647. #region public override bool Equals (object o)
  648. /// <summary>
  649. /// Compares this object instance to another object for equality.
  650. /// </summary>
  651. /// <param name="other">The other object to be used in the comparison.</param>
  652. /// <returns>True if both objects are Quaternions of equal value. Otherwise it returns false.</returns>
  653. public override bool Equals(object other)
  654. {
  655. if (other is Quaternion == false) return false;
  656. return this == (Quaternion)other;
  657. }
  658. #endregion
  659. #region public override int GetHashCode ()
  660. /// <summary>
  661. /// Provides the hash code for this object.
  662. /// </summary>
  663. /// <returns>A hash code formed from the bitwise XOR of this objects members.</returns>
  664. public override int GetHashCode()
  665. {
  666. return Xyz.GetHashCode() ^ W.GetHashCode();
  667. }
  668. #endregion
  669. #endregion
  670. #endregion
  671. #region IEquatable<Quaternion> Members
  672. /// <summary>
  673. /// Compares this Quaternion instance to another Quaternion for equality.
  674. /// </summary>
  675. /// <param name="other">The other Quaternion to be used in the comparison.</param>
  676. /// <returns>True if both instances are equal; false otherwise.</returns>
  677. public bool Equals(Quaternion other)
  678. {
  679. return Xyz == other.Xyz && W == other.W;
  680. }
  681. #endregion
  682. }
  683. }