TSQuaternion.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. /* Copyright (C) <2009-2011> <Thorben Linneweber, Jitter Physics>
  2. *
  3. * This software is provided 'as-is', without any express or implied
  4. * warranty. In no event will the authors be held liable for any damages
  5. * arising from the use of this software.
  6. *
  7. * Permission is granted to anyone to use this software for any purpose,
  8. * including commercial applications, and to alter it and redistribute it
  9. * freely, subject to the following restrictions:
  10. *
  11. * 1. The origin of this software must not be misrepresented; you must not
  12. * claim that you wrote the original software. If you use this software
  13. * in a product, an acknowledgment in the product documentation would be
  14. * appreciated but is not required.
  15. * 2. Altered source versions must be plainly marked as such, and must not be
  16. * misrepresented as being the original software.
  17. * 3. This notice may not be removed or altered from any source distribution.
  18. */
  19. using System;
  20. namespace TrueSync
  21. {
  22. /// <summary>
  23. /// A Quaternion representing an orientation.
  24. /// </summary>
  25. [Serializable]
  26. public struct TSQuaternion
  27. {
  28. /// <summary>The X component of the quaternion.</summary>
  29. public FP x;
  30. /// <summary>The Y component of the quaternion.</summary>
  31. public FP y;
  32. /// <summary>The Z component of the quaternion.</summary>
  33. public FP z;
  34. /// <summary>The W component of the quaternion.</summary>
  35. public FP w;
  36. public static readonly TSQuaternion identity;
  37. static TSQuaternion() {
  38. identity = new TSQuaternion(0, 0, 0, 1);
  39. }
  40. /// <summary>
  41. /// Initializes a new instance of the JQuaternion structure.
  42. /// </summary>
  43. /// <param name="x">The X component of the quaternion.</param>
  44. /// <param name="y">The Y component of the quaternion.</param>
  45. /// <param name="z">The Z component of the quaternion.</param>
  46. /// <param name="w">The W component of the quaternion.</param>
  47. public TSQuaternion(FP x, FP y, FP z, FP w)
  48. {
  49. this.x = x;
  50. this.y = y;
  51. this.z = z;
  52. this.w = w;
  53. }
  54. public void Set(FP new_x, FP new_y, FP new_z, FP new_w) {
  55. this.x = new_x;
  56. this.y = new_y;
  57. this.z = new_z;
  58. this.w = new_w;
  59. }
  60. public void SetFromToRotation(TSVector fromDirection, TSVector toDirection) {
  61. TSQuaternion targetRotation = TSQuaternion.FromToRotation(fromDirection, toDirection);
  62. this.Set(targetRotation.x, targetRotation.y, targetRotation.z, targetRotation.w);
  63. }
  64. public TSVector eulerAngles {
  65. get {
  66. TSVector result = new TSVector();
  67. FP ysqr = y * y;
  68. FP t0 = -2.0f * (ysqr + z * z) + 1.0f;
  69. FP t1 = +2.0f * (x * y - w * z);
  70. FP t2 = -2.0f * (x * z + w * y);
  71. FP t3 = +2.0f * (y * z - w * x);
  72. FP t4 = -2.0f * (x * x + ysqr) + 1.0f;
  73. t2 = t2 > 1.0f ? 1.0f : t2;
  74. t2 = t2 < -1.0f ? -1.0f : t2;
  75. result.x = FP.Atan2(t3, t4) * FP.Rad2Deg;
  76. result.y = FP.Asin(t2) * FP.Rad2Deg;
  77. result.z = FP.Atan2(t1, t0) * FP.Rad2Deg;
  78. return result * -1;
  79. }
  80. }
  81. public static FP Angle(TSQuaternion a, TSQuaternion b) {
  82. TSQuaternion aInv = TSQuaternion.Inverse(a);
  83. TSQuaternion f = b * aInv;
  84. FP angle = FP.Acos(f.w) * 2 * FP.Rad2Deg;
  85. if (angle > 180) {
  86. angle = 360 - angle;
  87. }
  88. return angle;
  89. }
  90. /// <summary>
  91. /// Quaternions are added.
  92. /// </summary>
  93. /// <param name="quaternion1">The first quaternion.</param>
  94. /// <param name="quaternion2">The second quaternion.</param>
  95. /// <returns>The sum of both quaternions.</returns>
  96. #region public static JQuaternion Add(JQuaternion quaternion1, JQuaternion quaternion2)
  97. public static TSQuaternion Add(TSQuaternion quaternion1, TSQuaternion quaternion2)
  98. {
  99. TSQuaternion result;
  100. TSQuaternion.Add(ref quaternion1, ref quaternion2, out result);
  101. return result;
  102. }
  103. public static TSQuaternion LookRotation(TSVector forward) {
  104. return CreateFromMatrix(TSMatrix.LookAt(forward, TSVector.up));
  105. }
  106. public static TSQuaternion LookRotation(TSVector forward, TSVector upwards) {
  107. return CreateFromMatrix(TSMatrix.LookAt(forward, upwards));
  108. }
  109. public static TSQuaternion Slerp(TSQuaternion from, TSQuaternion to, FP t) {
  110. t = TSMath.Clamp(t, 0, 1);
  111. FP dot = Dot(from, to);
  112. if (dot < 0.0f) {
  113. to = Multiply(to, -1);
  114. dot = -dot;
  115. }
  116. FP halfTheta = FP.Acos(dot);
  117. return Multiply(Multiply(from, FP.Sin((1 - t) * halfTheta)) + Multiply(to, FP.Sin(t * halfTheta)), 1 / FP.Sin(halfTheta));
  118. }
  119. public static TSQuaternion RotateTowards(TSQuaternion from, TSQuaternion to, FP maxDegreesDelta) {
  120. FP dot = Dot(from, to);
  121. if (dot < 0.0f) {
  122. to = Multiply(to, -1);
  123. dot = -dot;
  124. }
  125. FP halfTheta = FP.Acos(dot);
  126. FP theta = halfTheta * 2;
  127. maxDegreesDelta *= FP.Deg2Rad;
  128. if (maxDegreesDelta >= theta) {
  129. return to;
  130. }
  131. maxDegreesDelta /= theta;
  132. return Multiply(Multiply(from, FP.Sin((1 - maxDegreesDelta) * halfTheta)) + Multiply(to, FP.Sin(maxDegreesDelta * halfTheta)), 1 / FP.Sin(halfTheta));
  133. }
  134. public static TSQuaternion Euler(FP x, FP y, FP z) {
  135. x *= FP.Deg2Rad;
  136. y *= FP.Deg2Rad;
  137. z *= FP.Deg2Rad;
  138. TSQuaternion rotation;
  139. TSQuaternion.CreateFromYawPitchRoll(y, x, z, out rotation);
  140. return rotation;
  141. }
  142. public static TSQuaternion Euler(TSVector eulerAngles) {
  143. return Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
  144. }
  145. public static TSQuaternion AngleAxis(FP angle, TSVector axis) {
  146. axis = axis * FP.Deg2Rad;
  147. axis.Normalize();
  148. FP halfAngle = angle * FP.Deg2Rad * FP.Half;
  149. TSQuaternion rotation;
  150. FP sin = FP.Sin(halfAngle);
  151. rotation.x = axis.x * sin;
  152. rotation.y = axis.y * sin;
  153. rotation.z = axis.z * sin;
  154. rotation.w = FP.Cos(halfAngle);
  155. return rotation;
  156. }
  157. public static void CreateFromYawPitchRoll(FP yaw, FP pitch, FP roll, out TSQuaternion result)
  158. {
  159. FP num9 = roll * FP.Half;
  160. FP num6 = FP.Sin(num9);
  161. FP num5 = FP.Cos(num9);
  162. FP num8 = pitch * FP.Half;
  163. FP num4 = FP.Sin(num8);
  164. FP num3 = FP.Cos(num8);
  165. FP num7 = yaw * FP.Half;
  166. FP num2 = FP.Sin(num7);
  167. FP num = FP.Cos(num7);
  168. result.x = ((num * num4) * num5) + ((num2 * num3) * num6);
  169. result.y = ((num2 * num3) * num5) - ((num * num4) * num6);
  170. result.z = ((num * num3) * num6) - ((num2 * num4) * num5);
  171. result.w = ((num * num3) * num5) + ((num2 * num4) * num6);
  172. }
  173. /// <summary>
  174. /// Quaternions are added.
  175. /// </summary>
  176. /// <param name="quaternion1">The first quaternion.</param>
  177. /// <param name="quaternion2">The second quaternion.</param>
  178. /// <param name="result">The sum of both quaternions.</param>
  179. public static void Add(ref TSQuaternion quaternion1, ref TSQuaternion quaternion2, out TSQuaternion result)
  180. {
  181. result.x = quaternion1.x + quaternion2.x;
  182. result.y = quaternion1.y + quaternion2.y;
  183. result.z = quaternion1.z + quaternion2.z;
  184. result.w = quaternion1.w + quaternion2.w;
  185. }
  186. #endregion
  187. public static TSQuaternion Conjugate(TSQuaternion value)
  188. {
  189. TSQuaternion quaternion;
  190. quaternion.x = -value.x;
  191. quaternion.y = -value.y;
  192. quaternion.z = -value.z;
  193. quaternion.w = value.w;
  194. return quaternion;
  195. }
  196. public static FP Dot(TSQuaternion a, TSQuaternion b) {
  197. return a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z;
  198. }
  199. public static TSQuaternion Inverse(TSQuaternion rotation) {
  200. FP invNorm = FP.One / ((rotation.x * rotation.x) + (rotation.y * rotation.y) + (rotation.z * rotation.z) + (rotation.w * rotation.w));
  201. return TSQuaternion.Multiply(TSQuaternion.Conjugate(rotation), invNorm);
  202. }
  203. public static TSQuaternion FromToRotation(TSVector fromVector, TSVector toVector) {
  204. TSVector w = TSVector.Cross(fromVector, toVector);
  205. TSQuaternion q = new TSQuaternion(w.x, w.y, w.z, TSVector.Dot(fromVector, toVector));
  206. q.w += FP.Sqrt(fromVector.sqrMagnitude * toVector.sqrMagnitude);
  207. q.Normalize();
  208. return q;
  209. }
  210. public static TSQuaternion Lerp(TSQuaternion a, TSQuaternion b, FP t) {
  211. t = TSMath.Clamp(t, FP.Zero, FP.One);
  212. return LerpUnclamped(a, b, t);
  213. }
  214. public static TSQuaternion LerpUnclamped(TSQuaternion a, TSQuaternion b, FP t) {
  215. TSQuaternion result = TSQuaternion.Multiply(a, (1 - t)) + TSQuaternion.Multiply(b, t);
  216. result.Normalize();
  217. return result;
  218. }
  219. /// <summary>
  220. /// Quaternions are subtracted.
  221. /// </summary>
  222. /// <param name="quaternion1">The first quaternion.</param>
  223. /// <param name="quaternion2">The second quaternion.</param>
  224. /// <returns>The difference of both quaternions.</returns>
  225. #region public static JQuaternion Subtract(JQuaternion quaternion1, JQuaternion quaternion2)
  226. public static TSQuaternion Subtract(TSQuaternion quaternion1, TSQuaternion quaternion2)
  227. {
  228. TSQuaternion result;
  229. TSQuaternion.Subtract(ref quaternion1, ref quaternion2, out result);
  230. return result;
  231. }
  232. /// <summary>
  233. /// Quaternions are subtracted.
  234. /// </summary>
  235. /// <param name="quaternion1">The first quaternion.</param>
  236. /// <param name="quaternion2">The second quaternion.</param>
  237. /// <param name="result">The difference of both quaternions.</param>
  238. public static void Subtract(ref TSQuaternion quaternion1, ref TSQuaternion quaternion2, out TSQuaternion result)
  239. {
  240. result.x = quaternion1.x - quaternion2.x;
  241. result.y = quaternion1.y - quaternion2.y;
  242. result.z = quaternion1.z - quaternion2.z;
  243. result.w = quaternion1.w - quaternion2.w;
  244. }
  245. #endregion
  246. /// <summary>
  247. /// Multiply two quaternions.
  248. /// </summary>
  249. /// <param name="quaternion1">The first quaternion.</param>
  250. /// <param name="quaternion2">The second quaternion.</param>
  251. /// <returns>The product of both quaternions.</returns>
  252. #region public static JQuaternion Multiply(JQuaternion quaternion1, JQuaternion quaternion2)
  253. public static TSQuaternion Multiply(TSQuaternion quaternion1, TSQuaternion quaternion2)
  254. {
  255. TSQuaternion result;
  256. TSQuaternion.Multiply(ref quaternion1, ref quaternion2, out result);
  257. return result;
  258. }
  259. /// <summary>
  260. /// Multiply two quaternions.
  261. /// </summary>
  262. /// <param name="quaternion1">The first quaternion.</param>
  263. /// <param name="quaternion2">The second quaternion.</param>
  264. /// <param name="result">The product of both quaternions.</param>
  265. public static void Multiply(ref TSQuaternion quaternion1, ref TSQuaternion quaternion2, out TSQuaternion result)
  266. {
  267. FP x = quaternion1.x;
  268. FP y = quaternion1.y;
  269. FP z = quaternion1.z;
  270. FP w = quaternion1.w;
  271. FP num4 = quaternion2.x;
  272. FP num3 = quaternion2.y;
  273. FP num2 = quaternion2.z;
  274. FP num = quaternion2.w;
  275. FP num12 = (y * num2) - (z * num3);
  276. FP num11 = (z * num4) - (x * num2);
  277. FP num10 = (x * num3) - (y * num4);
  278. FP num9 = ((x * num4) + (y * num3)) + (z * num2);
  279. result.x = ((x * num) + (num4 * w)) + num12;
  280. result.y = ((y * num) + (num3 * w)) + num11;
  281. result.z = ((z * num) + (num2 * w)) + num10;
  282. result.w = (w * num) - num9;
  283. }
  284. #endregion
  285. /// <summary>
  286. /// Scale a quaternion
  287. /// </summary>
  288. /// <param name="quaternion1">The quaternion to scale.</param>
  289. /// <param name="scaleFactor">Scale factor.</param>
  290. /// <returns>The scaled quaternion.</returns>
  291. #region public static JQuaternion Multiply(JQuaternion quaternion1, FP scaleFactor)
  292. public static TSQuaternion Multiply(TSQuaternion quaternion1, FP scaleFactor)
  293. {
  294. TSQuaternion result;
  295. TSQuaternion.Multiply(ref quaternion1, scaleFactor, out result);
  296. return result;
  297. }
  298. /// <summary>
  299. /// Scale a quaternion
  300. /// </summary>
  301. /// <param name="quaternion1">The quaternion to scale.</param>
  302. /// <param name="scaleFactor">Scale factor.</param>
  303. /// <param name="result">The scaled quaternion.</param>
  304. public static void Multiply(ref TSQuaternion quaternion1, FP scaleFactor, out TSQuaternion result)
  305. {
  306. result.x = quaternion1.x * scaleFactor;
  307. result.y = quaternion1.y * scaleFactor;
  308. result.z = quaternion1.z * scaleFactor;
  309. result.w = quaternion1.w * scaleFactor;
  310. }
  311. #endregion
  312. /// <summary>
  313. /// Sets the length of the quaternion to one.
  314. /// </summary>
  315. #region public void Normalize()
  316. public void Normalize()
  317. {
  318. FP num2 = (((this.x * this.x) + (this.y * this.y)) + (this.z * this.z)) + (this.w * this.w);
  319. FP num = 1 / (FP.Sqrt(num2));
  320. this.x *= num;
  321. this.y *= num;
  322. this.z *= num;
  323. this.w *= num;
  324. }
  325. #endregion
  326. /// <summary>
  327. /// Creates a quaternion from a matrix.
  328. /// </summary>
  329. /// <param name="matrix">A matrix representing an orientation.</param>
  330. /// <returns>JQuaternion representing an orientation.</returns>
  331. #region public static JQuaternion CreateFromMatrix(JMatrix matrix)
  332. public static TSQuaternion CreateFromMatrix(TSMatrix matrix)
  333. {
  334. TSQuaternion result;
  335. TSQuaternion.CreateFromMatrix(ref matrix, out result);
  336. return result;
  337. }
  338. /// <summary>
  339. /// Creates a quaternion from a matrix.
  340. /// </summary>
  341. /// <param name="matrix">A matrix representing an orientation.</param>
  342. /// <param name="result">JQuaternion representing an orientation.</param>
  343. public static void CreateFromMatrix(ref TSMatrix matrix, out TSQuaternion result)
  344. {
  345. FP num8 = (matrix.M11 + matrix.M22) + matrix.M33;
  346. if (num8 > FP.Zero)
  347. {
  348. FP num = FP.Sqrt((num8 + FP.One));
  349. result.w = num * FP.Half;
  350. num = FP.Half / num;
  351. result.x = (matrix.M23 - matrix.M32) * num;
  352. result.y = (matrix.M31 - matrix.M13) * num;
  353. result.z = (matrix.M12 - matrix.M21) * num;
  354. }
  355. else if ((matrix.M11 >= matrix.M22) && (matrix.M11 >= matrix.M33))
  356. {
  357. FP num7 = FP.Sqrt((((FP.One + matrix.M11) - matrix.M22) - matrix.M33));
  358. FP num4 = FP.Half / num7;
  359. result.x = FP.Half * num7;
  360. result.y = (matrix.M12 + matrix.M21) * num4;
  361. result.z = (matrix.M13 + matrix.M31) * num4;
  362. result.w = (matrix.M23 - matrix.M32) * num4;
  363. }
  364. else if (matrix.M22 > matrix.M33)
  365. {
  366. FP num6 = FP.Sqrt((((FP.One + matrix.M22) - matrix.M11) - matrix.M33));
  367. FP num3 = FP.Half / num6;
  368. result.x = (matrix.M21 + matrix.M12) * num3;
  369. result.y = FP.Half * num6;
  370. result.z = (matrix.M32 + matrix.M23) * num3;
  371. result.w = (matrix.M31 - matrix.M13) * num3;
  372. }
  373. else
  374. {
  375. FP num5 = FP.Sqrt((((FP.One + matrix.M33) - matrix.M11) - matrix.M22));
  376. FP num2 = FP.Half / num5;
  377. result.x = (matrix.M31 + matrix.M13) * num2;
  378. result.y = (matrix.M32 + matrix.M23) * num2;
  379. result.z = FP.Half * num5;
  380. result.w = (matrix.M12 - matrix.M21) * num2;
  381. }
  382. }
  383. #endregion
  384. /// <summary>
  385. /// Multiply two quaternions.
  386. /// </summary>
  387. /// <param name="value1">The first quaternion.</param>
  388. /// <param name="value2">The second quaternion.</param>
  389. /// <returns>The product of both quaternions.</returns>
  390. #region public static FP operator *(JQuaternion value1, JQuaternion value2)
  391. public static TSQuaternion operator *(TSQuaternion value1, TSQuaternion value2)
  392. {
  393. TSQuaternion result;
  394. TSQuaternion.Multiply(ref value1, ref value2,out result);
  395. return result;
  396. }
  397. #endregion
  398. /// <summary>
  399. /// Add two quaternions.
  400. /// </summary>
  401. /// <param name="value1">The first quaternion.</param>
  402. /// <param name="value2">The second quaternion.</param>
  403. /// <returns>The sum of both quaternions.</returns>
  404. #region public static FP operator +(JQuaternion value1, JQuaternion value2)
  405. public static TSQuaternion operator +(TSQuaternion value1, TSQuaternion value2)
  406. {
  407. TSQuaternion result;
  408. TSQuaternion.Add(ref value1, ref value2, out result);
  409. return result;
  410. }
  411. #endregion
  412. /// <summary>
  413. /// Subtract two quaternions.
  414. /// </summary>
  415. /// <param name="value1">The first quaternion.</param>
  416. /// <param name="value2">The second quaternion.</param>
  417. /// <returns>The difference of both quaternions.</returns>
  418. #region public static FP operator -(JQuaternion value1, JQuaternion value2)
  419. public static TSQuaternion operator -(TSQuaternion value1, TSQuaternion value2)
  420. {
  421. TSQuaternion result;
  422. TSQuaternion.Subtract(ref value1, ref value2, out result);
  423. return result;
  424. }
  425. #endregion
  426. /**
  427. * @brief Rotates a {@link TSVector} by the {@link TSQuanternion}.
  428. **/
  429. public static TSVector operator *(TSQuaternion quat, TSVector vec) {
  430. FP num = quat.x * 2f;
  431. FP num2 = quat.y * 2f;
  432. FP num3 = quat.z * 2f;
  433. FP num4 = quat.x * num;
  434. FP num5 = quat.y * num2;
  435. FP num6 = quat.z * num3;
  436. FP num7 = quat.x * num2;
  437. FP num8 = quat.x * num3;
  438. FP num9 = quat.y * num3;
  439. FP num10 = quat.w * num;
  440. FP num11 = quat.w * num2;
  441. FP num12 = quat.w * num3;
  442. TSVector result;
  443. result.x = (1f - (num5 + num6)) * vec.x + (num7 - num12) * vec.y + (num8 + num11) * vec.z;
  444. result.y = (num7 + num12) * vec.x + (1f - (num4 + num6)) * vec.y + (num9 - num10) * vec.z;
  445. result.z = (num8 - num11) * vec.x + (num9 + num10) * vec.y + (1f - (num4 + num5)) * vec.z;
  446. return result;
  447. }
  448. public override string ToString() {
  449. return string.Format("({0:f1}, {1:f1}, {2:f1}, {3:f1})", x.AsFloat(), y.AsFloat(), z.AsFloat(), w.AsFloat());
  450. }
  451. }
  452. }