quaternion.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using static Unity.Mathematics.math;
  4. namespace Unity.Mathematics
  5. {
  6. [Serializable]
  7. public partial struct quaternion : System.IEquatable<quaternion>, IFormattable
  8. {
  9. public float4 value;
  10. /// <summary>A quaternion representing the identity transform.</summary>
  11. public static readonly quaternion identity = new quaternion(0.0f, 0.0f, 0.0f, 1.0f);
  12. /// <summary>Constructs a quaternion from four float values.</summary>
  13. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  14. public quaternion(float x, float y, float z, float w) { value.x = x; value.y = y; value.z = z; value.w = w; }
  15. /// <summary>Constructs a quaternion from float4 vector.</summary>
  16. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  17. public quaternion(float4 value) { this.value = value; }
  18. /// <summary>Implicitly converts a float4 vector to a quaternion.</summary>
  19. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  20. public static implicit operator quaternion(float4 v) { return new quaternion(v); }
  21. /// <summary>Constructs a unit quaternion from a float3x3 rotation matrix. The matrix must be orthonormal.</summary>
  22. public quaternion(float3x3 m)
  23. {
  24. float3 u = m.c0;
  25. float3 v = m.c1;
  26. float3 w = m.c2;
  27. uint u_sign = (asuint(u.x) & 0x80000000);
  28. float t = v.y + asfloat(asuint(w.z) ^ u_sign);
  29. uint4 u_mask = uint4((int)u_sign >> 31);
  30. uint4 t_mask = uint4(asint(t) >> 31);
  31. float tr = 1.0f + abs(u.x);
  32. uint4 sign_flips = uint4(0x00000000, 0x80000000, 0x80000000, 0x80000000) ^ (u_mask & uint4(0x00000000, 0x80000000, 0x00000000, 0x80000000)) ^ (t_mask & uint4(0x80000000, 0x80000000, 0x80000000, 0x00000000));
  33. value = float4(tr, u.y, w.x, v.z) + asfloat(asuint(float4(t, v.x, u.z, w.y)) ^ sign_flips); // +---, +++-, ++-+, +-++
  34. value = asfloat((asuint(value) & ~u_mask) | (asuint(value.zwxy) & u_mask));
  35. value = asfloat((asuint(value.wzyx) & ~t_mask) | (asuint(value) & t_mask));
  36. value = normalize(value);
  37. }
  38. /// <summary>Constructs a unit quaternion from an orthonormal float4x4 matrix.</summary>
  39. public quaternion(float4x4 m)
  40. {
  41. float4 u = m.c0;
  42. float4 v = m.c1;
  43. float4 w = m.c2;
  44. uint u_sign = (asuint(u.x) & 0x80000000);
  45. float t = v.y + asfloat(asuint(w.z) ^ u_sign);
  46. uint4 u_mask = uint4((int)u_sign >> 31);
  47. uint4 t_mask = uint4(asint(t) >> 31);
  48. float tr = 1.0f + abs(u.x);
  49. uint4 sign_flips = uint4(0x00000000, 0x80000000, 0x80000000, 0x80000000) ^ (u_mask & uint4(0x00000000, 0x80000000, 0x00000000, 0x80000000)) ^ (t_mask & uint4(0x80000000, 0x80000000, 0x80000000, 0x00000000));
  50. value = float4(tr, u.y, w.x, v.z) + asfloat(asuint(float4(t, v.x, u.z, w.y)) ^ sign_flips); // +---, +++-, ++-+, +-++
  51. value = asfloat((asuint(value) & ~u_mask) | (asuint(value.zwxy) & u_mask));
  52. value = asfloat((asuint(value.wzyx) & ~t_mask) | (asuint(value) & t_mask));
  53. value = normalize(value);
  54. }
  55. /// <summary>
  56. /// Returns a quaternion representing a rotation around a unit axis by an angle in radians.
  57. /// The rotation direction is clockwise when looking along the rotation axis towards the origin.
  58. /// </summary>
  59. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  60. public static quaternion AxisAngle(float3 axis, float angle)
  61. {
  62. float sina, cosa;
  63. math.sincos(0.5f * angle, out sina, out cosa);
  64. return quaternion(float4(axis * sina, cosa));
  65. }
  66. /// <summary>
  67. /// Returns a quaternion constructed by first performing a rotation around the x-axis, then the y-axis and finally the z-axis.
  68. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  69. /// </summary>
  70. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  71. public static quaternion EulerXYZ(float3 xyz)
  72. {
  73. // return mul(rotateZ(xyz.z), mul(rotateY(xyz.y), rotateX(xyz.x)));
  74. float3 s, c;
  75. sincos(0.5f * xyz, out s, out c);
  76. return quaternion(
  77. // s.x * c.y * c.z - s.y * s.z * c.x,
  78. // s.y * c.x * c.z + s.x * s.z * c.y,
  79. // s.z * c.x * c.y - s.x * s.y * c.z,
  80. // c.x * c.y * c.z + s.y * s.z * s.x
  81. float4(s.xyz, c.x) * c.yxxy * c.zzyz + s.yxxy * s.zzyz * float4(c.xyz, s.x) * float4(-1.0f, 1.0f, -1.0f, 1.0f)
  82. );
  83. }
  84. /// <summary>
  85. /// Returns a quaternion constructed by first performing a rotation around the x-axis, then the z-axis and finally the y-axis.
  86. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  87. /// </summary>
  88. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  89. public static quaternion EulerXZY(float3 xyz)
  90. {
  91. // return mul(rotateY(xyz.y), mul(rotateZ(xyz.z), rotateX(xyz.x)));
  92. float3 s, c;
  93. sincos(0.5f * xyz, out s, out c);
  94. return quaternion(
  95. // s.x * c.y * c.z + s.y * s.z * c.x,
  96. // s.y * c.x * c.z + s.x * s.z * c.y,
  97. // s.z * c.x * c.y - s.x * s.y * c.z,
  98. // c.x * c.y * c.z - s.y * s.z * s.x
  99. float4(s.xyz, c.x) * c.yxxy * c.zzyz + s.yxxy * s.zzyz * float4(c.xyz, s.x) * float4(1.0f, 1.0f, -1.0f, -1.0f)
  100. );
  101. }
  102. /// <summary>
  103. /// Returns a quaternion constructed by first performing a rotation around the y-axis, then the x-axis and finally the z-axis.
  104. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  105. /// </summary>
  106. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  107. public static quaternion EulerYXZ(float3 xyz)
  108. {
  109. // return mul(rotateZ(xyz.z), mul(rotateX(xyz.x), rotateY(xyz.y)));
  110. float3 s, c;
  111. sincos(0.5f * xyz, out s, out c);
  112. return quaternion(
  113. // s.x * c.y * c.z - s.y * s.z * c.x,
  114. // s.y * c.x * c.z + s.x * s.z * c.y,
  115. // s.z * c.x * c.y + s.x * s.y * c.z,
  116. // c.x * c.y * c.z - s.y * s.z * s.x
  117. float4(s.xyz, c.x) * c.yxxy * c.zzyz + s.yxxy * s.zzyz * float4(c.xyz, s.x) * float4(-1.0f, 1.0f, 1.0f, -1.0f)
  118. );
  119. }
  120. /// <summary>
  121. /// Returns a quaternion constructed by first performing a rotation around the y-axis, then the z-axis and finally the x-axis.
  122. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  123. /// </summary>
  124. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  125. public static quaternion EulerYZX(float3 xyz)
  126. {
  127. // return mul(rotateX(xyz.x), mul(rotateZ(xyz.z), rotateY(xyz.y)));
  128. float3 s, c;
  129. sincos(0.5f * xyz, out s, out c);
  130. return quaternion(
  131. // s.x * c.y * c.z - s.y * s.z * c.x,
  132. // s.y * c.x * c.z - s.x * s.z * c.y,
  133. // s.z * c.x * c.y + s.x * s.y * c.z,
  134. // c.x * c.y * c.z + s.y * s.z * s.x
  135. float4(s.xyz, c.x) * c.yxxy * c.zzyz + s.yxxy * s.zzyz * float4(c.xyz, s.x) * float4(-1.0f, -1.0f, 1.0f, 1.0f)
  136. );
  137. }
  138. /// <summary>
  139. /// Returns a quaternion constructed by first performing a rotation around the z-axis, then the x-axis and finally the y-axis.
  140. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  141. /// This is the default order rotation order in Unity.
  142. /// </summary>
  143. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  144. public static quaternion EulerZXY(float3 xyz)
  145. {
  146. // return mul(rotateY(xyz.y), mul(rotateX(xyz.x), rotateZ(xyz.z)));
  147. float3 s, c;
  148. sincos(0.5f * xyz, out s, out c);
  149. return quaternion(
  150. // s.x * c.y * c.z + s.y * s.z * c.x,
  151. // s.y * c.x * c.z - s.x * s.z * c.y,
  152. // s.z * c.x * c.y - s.x * s.y * c.z,
  153. // c.x * c.y * c.z + s.y * s.z * s.x
  154. float4(s.xyz, c.x) * c.yxxy * c.zzyz + s.yxxy * s.zzyz * float4(c.xyz, s.x) * float4(1.0f, -1.0f, -1.0f, 1.0f)
  155. );
  156. }
  157. /// <summary>
  158. /// Returns a quaternion constructed by first performing a rotation around the z-axis, then the y-axis and finally the x-axis.
  159. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  160. /// </summary>
  161. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  162. public static quaternion EulerZYX(float3 xyz)
  163. {
  164. // return mul(rotateX(xyz.x), mul(rotateY(xyz.y), rotateZ(xyz.z)));
  165. float3 s, c;
  166. sincos(0.5f * xyz, out s, out c);
  167. return quaternion(
  168. // s.x * c.y * c.z + s.y * s.z * c.x,
  169. // s.y * c.x * c.z - s.x * s.z * c.y,
  170. // s.z * c.x * c.y + s.x * s.y * c.z,
  171. // c.x * c.y * c.z - s.y * s.x * s.z
  172. float4(s.xyz, c.x) * c.yxxy * c.zzyz + s.yxxy * s.zzyz * float4(c.xyz, s.x) * float4(1.0f, -1.0f, 1.0f, -1.0f)
  173. );
  174. }
  175. /// <summary>
  176. /// Returns a quaternion constructed by first performing a rotation around the x-axis, then the y-axis and finally the z-axis.
  177. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  178. /// </summary>
  179. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  180. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  181. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  182. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  183. public static quaternion EulerXYZ(float x, float y, float z) { return EulerXYZ(float3(x, y, z)); }
  184. /// <summary>
  185. /// Returns a quaternion constructed by first performing a rotation around the x-axis, then the z-axis and finally the y-axis.
  186. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  187. /// </summary>
  188. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  189. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  190. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  191. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  192. public static quaternion EulerXZY(float x, float y, float z) { return EulerXZY(float3(x, y, z)); }
  193. /// <summary>
  194. /// Returns a quaternion constructed by first performing a rotation around the y-axis, then the x-axis and finally the z-axis.
  195. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  196. /// </summary>
  197. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  198. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  199. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  200. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  201. public static quaternion EulerYXZ(float x, float y, float z) { return EulerYXZ(float3(x, y, z)); }
  202. /// <summary>
  203. /// Returns a quaternion constructed by first performing a rotation around the y-axis, then the z-axis and finally the x-axis.
  204. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  205. /// </summary>
  206. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  207. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  208. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  209. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  210. public static quaternion EulerYZX(float x, float y, float z) { return EulerYZX(float3(x, y, z)); }
  211. /// <summary>
  212. /// Returns a quaternion constructed by first performing a rotation around the z-axis, then the x-axis and finally the y-axis.
  213. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  214. /// This is the default order rotation order in Unity.
  215. /// </summary>
  216. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  217. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  218. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  219. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  220. public static quaternion EulerZXY(float x, float y, float z) { return EulerZXY(float3(x, y, z)); }
  221. /// <summary>
  222. /// Returns a quaternion constructed by first performing a rotation around the z-axis, then the y-axis and finally the x-axis.
  223. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  224. /// </summary>
  225. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  226. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  227. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  228. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  229. public static quaternion EulerZYX(float x, float y, float z) { return EulerZYX(float3(x, y, z)); }
  230. /// <summary>
  231. /// Returns a quaternion constructed by first performing 3 rotations around the principal axes in a given order.
  232. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  233. /// When the rotation order is known at compile time, it is recommended for performance reasons to use specific
  234. /// Euler rotation constructors such as EulerZXY(...).
  235. /// </summary>
  236. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  237. /// <param name="order">The order in which the rotations are applied.</param>
  238. public static quaternion Euler(float3 xyz, RotationOrder order = RotationOrder.ZXY)
  239. {
  240. switch (order)
  241. {
  242. case RotationOrder.XYZ:
  243. return EulerXYZ(xyz);
  244. case RotationOrder.XZY:
  245. return EulerXZY(xyz);
  246. case RotationOrder.YXZ:
  247. return EulerYXZ(xyz);
  248. case RotationOrder.YZX:
  249. return EulerYZX(xyz);
  250. case RotationOrder.ZXY:
  251. return EulerZXY(xyz);
  252. case RotationOrder.ZYX:
  253. return EulerZYX(xyz);
  254. default:
  255. return quaternion.identity;
  256. }
  257. }
  258. /// <summary>
  259. /// Returns a quaternion constructed by first performing 3 rotations around the principal axes in a given order.
  260. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  261. /// When the rotation order is known at compile time, it is recommended for performance reasons to use specific
  262. /// Euler rotation constructors such as EulerZXY(...).
  263. /// </summary>
  264. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  265. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  266. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  267. /// <param name="order">The order in which the rotations are applied.</param>
  268. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  269. public static quaternion Euler(float x, float y, float z, RotationOrder order = RotationOrder.Default)
  270. {
  271. return Euler(float3(x, y, z), order);
  272. }
  273. /// <summary>Returns a float4x4 matrix that rotates around the x-axis by a given number of radians.</summary>
  274. /// <param name="angle">The clockwise rotation angle when looking along the x-axis towards the origin in radians.</param>
  275. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  276. public static quaternion RotateX(float angle)
  277. {
  278. float sina, cosa;
  279. math.sincos(0.5f * angle, out sina, out cosa);
  280. return quaternion(sina, 0.0f, 0.0f, cosa);
  281. }
  282. /// <summary>Returns a float4x4 matrix that rotates around the y-axis by a given number of radians.</summary>
  283. /// <param name="angle">The clockwise rotation angle when looking along the y-axis towards the origin in radians.</param>
  284. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  285. public static quaternion RotateY(float angle)
  286. {
  287. float sina, cosa;
  288. math.sincos(0.5f * angle, out sina, out cosa);
  289. return quaternion(0.0f, sina, 0.0f, cosa);
  290. }
  291. /// <summary>Returns a float4x4 matrix that rotates around the z-axis by a given number of radians.</summary>
  292. /// <param name="angle">The clockwise rotation angle when looking along the z-axis towards the origin in radians.</param>
  293. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  294. public static quaternion RotateZ(float angle)
  295. {
  296. float sina, cosa;
  297. math.sincos(0.5f * angle, out sina, out cosa);
  298. return quaternion(0.0f, 0.0f, sina, cosa);
  299. }
  300. /// <summary>
  301. /// Returns a quaternion view rotation given a unit length forward vector and a unit length up vector.
  302. /// The two input vectors are assumed to be unit length and not collinear.
  303. /// If these assumptions are not met use float3x3.LookRotationSafe instead.
  304. /// </summary>
  305. public static quaternion LookRotation(float3 forward, float3 up)
  306. {
  307. float3 t = normalize(cross(up, forward));
  308. return quaternion(float3x3(t, cross(forward, t), forward));
  309. }
  310. /// <summary>
  311. /// Returns a quaternion view rotation given a forward vector and an up vector.
  312. /// The two input vectors are not assumed to be unit length.
  313. /// If the magnitude of either of the vectors is so extreme that the calculation cannot be carried out reliably or the vectors are collinear,
  314. /// the identity will be returned instead.
  315. /// </summary>
  316. public static quaternion LookRotationSafe(float3 forward, float3 up)
  317. {
  318. float forwardLengthSq = dot(forward, forward);
  319. float upLengthSq = dot(up, up);
  320. forward *= rsqrt(forwardLengthSq);
  321. up *= rsqrt(upLengthSq);
  322. float3 t = cross(up, forward);
  323. float tLengthSq = dot(t, t);
  324. t *= rsqrt(tLengthSq);
  325. float mn = min(min(forwardLengthSq, upLengthSq), tLengthSq);
  326. float mx = max(max(forwardLengthSq, upLengthSq), tLengthSq);
  327. bool accept = mn > 1e-35f && mx < 1e35f && isfinite(forwardLengthSq) && isfinite(upLengthSq) && isfinite(tLengthSq);
  328. return quaternion(select(float4(0.0f, 0.0f, 0.0f, 1.0f), quaternion(float3x3(t, cross(forward, t),forward)).value, accept));
  329. }
  330. /// <summary>Returns true if the quaternion is equal to a given quaternion, false otherwise.</summary>
  331. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  332. public bool Equals(quaternion x) { return value.x == x.value.x && value.y == x.value.y && value.z == x.value.z && value.w == x.value.w; }
  333. /// <summary>Returns whether true if the quaternion is equal to a given quaternion, false otherwise.</summary>
  334. public override bool Equals(object x) { return Equals((quaternion)x); }
  335. /// <summary>Returns a hash code for the quaternion.</summary>
  336. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  337. public override int GetHashCode() { return (int)math.hash(this); }
  338. /// <summary>Returns a string representation of the quaternion.</summary>
  339. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  340. public override string ToString()
  341. {
  342. return string.Format("quaternion({0}f, {1}f, {2}f, {3}f)", value.x, value.y, value.z, value.w);
  343. }
  344. /// <summary>Returns a string representation of the quaternion using a specified format and culture-specific format information.</summary>
  345. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  346. public string ToString(string format, IFormatProvider formatProvider)
  347. {
  348. return string.Format("quaternion({0}f, {1}f, {2}f, {3}f)", value.x.ToString(format, formatProvider), value.y.ToString(format, formatProvider), value.z.ToString(format, formatProvider), value.w.ToString(format, formatProvider));
  349. }
  350. }
  351. public static partial class math
  352. {
  353. /// <summary>Returns a quaternion constructed from four float values.</summary>
  354. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  355. public static quaternion quaternion(float x, float y, float z, float w) { return new quaternion(x, y, z, w); }
  356. /// <summary>Returns a quaternion constructed from a float4 vector.</summary>
  357. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  358. public static quaternion quaternion(float4 value) { return new quaternion(value); }
  359. /// <summary>Returns a unit quaternion constructed from a float3x3 rotation matrix. The matrix must be orthonormal.</summary>
  360. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  361. public static quaternion quaternion(float3x3 m) { return new quaternion(m); }
  362. /// <summary>Returns a unit quaternion constructed from a float4x4 matrix. The matrix must be orthonormal.</summary>
  363. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  364. public static quaternion quaternion(float4x4 m) { return new quaternion(m); }
  365. /// <summary>Returns the conjugate of a quaternion value.</summary>
  366. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  367. public static quaternion conjugate(quaternion q)
  368. {
  369. return quaternion(q.value * float4(-1.0f, -1.0f, -1.0f, 1.0f));
  370. }
  371. /// <summary>Returns the inverse of a quaternion value.</summary>
  372. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  373. public static quaternion inverse(quaternion q)
  374. {
  375. float4 x = q.value;
  376. return quaternion(rcp(dot(x, x)) * x * float4(-1.0f, -1.0f, -1.0f, 1.0f));
  377. }
  378. /// <summary>Returns the dot product of two quaternions.</summary>
  379. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  380. public static float dot(quaternion a, quaternion b)
  381. {
  382. return dot(a.value, b.value);
  383. }
  384. /// <summary>Returns the length of a quaternion.</summary>
  385. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  386. public static float length(quaternion q)
  387. {
  388. return sqrt(dot(q.value, q.value));
  389. }
  390. /// <summary>Returns the squared length of a quaternion.</summary>
  391. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  392. public static float lengthsq(quaternion q)
  393. {
  394. return dot(q.value, q.value);
  395. }
  396. /// <summary>Returns a normalized version of a quaternion q by scaling it by 1 / length(q).</summary>
  397. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  398. public static quaternion normalize(quaternion q)
  399. {
  400. float4 x = q.value;
  401. return quaternion(rsqrt(dot(x, x)) * x);
  402. }
  403. /// <summary>
  404. /// Returns a safe normalized version of the q by scaling it by 1 / length(q).
  405. /// Returns the identity when 1 / length(q) does not produce a finite number.
  406. /// </summary>
  407. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  408. public static quaternion normalizesafe(quaternion q)
  409. {
  410. float4 x = q.value;
  411. float len = math.dot(x, x);
  412. return quaternion(math.select(Mathematics.quaternion.identity.value, x * math.rsqrt(len), len > FLT_MIN_NORMAL));
  413. }
  414. /// <summary>
  415. /// Returns a safe normalized version of the q by scaling it by 1 / length(q).
  416. /// Returns the given default value when 1 / length(q) does not produce a finite number.
  417. /// </summary>
  418. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  419. public static quaternion normalizesafe(quaternion q, quaternion defaultvalue)
  420. {
  421. float4 x = q.value;
  422. float len = math.dot(x, x);
  423. return quaternion(math.select(defaultvalue.value, x * math.rsqrt(len), len > FLT_MIN_NORMAL));
  424. }
  425. /// <summary>Returns the natural exponent of a quaternion. Assumes w is zero.</summary>
  426. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  427. public static quaternion unitexp(quaternion q)
  428. {
  429. float v_rcp_len = rsqrt(dot(q.value.xyz, q.value.xyz));
  430. float v_len = rcp(v_rcp_len);
  431. float sin_v_len, cos_v_len;
  432. sincos(v_len, out sin_v_len, out cos_v_len);
  433. return quaternion(float4(q.value.xyz * v_rcp_len * sin_v_len, cos_v_len));
  434. }
  435. /// <summary>Returns the natural exponent of a quaternion.</summary>
  436. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  437. public static quaternion exp(quaternion q)
  438. {
  439. float v_rcp_len = rsqrt(dot(q.value.xyz, q.value.xyz));
  440. float v_len = rcp(v_rcp_len);
  441. float sin_v_len, cos_v_len;
  442. sincos(v_len, out sin_v_len, out cos_v_len);
  443. return quaternion(float4(q.value.xyz * v_rcp_len * sin_v_len, cos_v_len) * exp(q.value.w));
  444. }
  445. /// <summary>Returns the natural logarithm of a unit length quaternion.</summary>
  446. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  447. public static quaternion unitlog(quaternion q)
  448. {
  449. float w = clamp(q.value.w, -1.0f, 1.0f);
  450. float s = acos(w) * rsqrt(1.0f - w*w);
  451. return quaternion(float4(q.value.xyz * s, 0.0f));
  452. }
  453. /// <summary>Returns the natural logarithm of a quaternion.</summary>
  454. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  455. public static quaternion log(quaternion q)
  456. {
  457. float v_len_sq = dot(q.value.xyz, q.value.xyz);
  458. float q_len_sq = v_len_sq + q.value.w*q.value.w;
  459. float s = acos(clamp(q.value.w * rsqrt(q_len_sq), -1.0f, 1.0f)) * rsqrt(v_len_sq);
  460. return quaternion(float4(q.value.xyz * s, 0.5f * log(q_len_sq)));
  461. }
  462. /// <summary>Returns the result of transforming the quaternion b by the quaternion a.</summary>
  463. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  464. public static quaternion mul(quaternion a, quaternion b)
  465. {
  466. return quaternion(a.value.wwww * b.value + (a.value.xyzx * b.value.wwwx + a.value.yzxy * b.value.zxyy) * float4(1.0f, 1.0f, 1.0f, -1.0f) - a.value.zxyz * b.value.yzxz);
  467. }
  468. /// <summary>Returns the result of transforming a vector by a quaternion.</summary>
  469. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  470. public static float3 mul(quaternion q, float3 v)
  471. {
  472. float3 t = 2 * cross(q.value.xyz, v);
  473. return v + q.value.w * t + cross(q.value.xyz, t);
  474. }
  475. /// <summary>Returns the result of rotating a vector by a unit quaternion.</summary>
  476. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  477. public static float3 rotate(quaternion q, float3 v)
  478. {
  479. float3 t = 2 * cross(q.value.xyz, v);
  480. return v + q.value.w * t + cross(q.value.xyz, t);
  481. }
  482. /// <summary>Returns the result of a normalized linear interpolation between two quaternions q1 and a2 using an interpolation parameter t.</summary>
  483. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  484. public static quaternion nlerp(quaternion q1, quaternion q2, float t)
  485. {
  486. float dt = dot(q1, q2);
  487. if(dt < 0.0f)
  488. {
  489. q2.value = -q2.value;
  490. }
  491. return normalize(quaternion(lerp(q1.value, q2.value, t)));
  492. }
  493. /// <summary>Returns the result of a spherical interpolation between two quaternions q1 and a2 using an interpolation parameter t.</summary>
  494. public static quaternion slerp(quaternion q1, quaternion q2, float t)
  495. {
  496. float dt = dot(q1, q2);
  497. if (dt < 0.0f)
  498. {
  499. dt = -dt;
  500. q2.value = -q2.value;
  501. }
  502. if (dt < 0.9995f)
  503. {
  504. float angle = acos(dt);
  505. float s = rsqrt(1.0f - dt * dt); // 1.0f / sin(angle)
  506. float w1 = sin(angle * (1.0f - t)) * s;
  507. float w2 = sin(angle * t) * s;
  508. return quaternion(q1.value * w1 + q2.value * w2);
  509. }
  510. else
  511. {
  512. // if the angle is small, use linear interpolation
  513. return nlerp(q1, q2, t);
  514. }
  515. }
  516. /// <summary>Returns a uint hash code of a quaternion.</summary>
  517. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  518. public static uint hash(quaternion q)
  519. {
  520. return hash(q.value);
  521. }
  522. /// <summary>
  523. /// Returns a uint4 vector hash code of a quaternion.
  524. /// When multiple elements are to be hashes together, it can more efficient to calculate and combine wide hash
  525. /// that are only reduced to a narrow uint hash at the very end instead of at every step.
  526. /// </summary>
  527. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  528. public static uint4 hashwide(quaternion q)
  529. {
  530. return hashwide(q.value);
  531. }
  532. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  533. public static float3 forward(quaternion q) { return mul(q, float3(0, 0, 1)); } // for compatibility
  534. }
  535. }