Keine Beschreibung
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

quaternion.cs 42KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using Unity.IL2CPP.CompilerServices;
  4. using static Unity.Mathematics.math;
  5. namespace Unity.Mathematics
  6. {
  7. /// <summary>
  8. /// A quaternion type for representing rotations.
  9. /// </summary>
  10. [Il2CppEagerStaticClassConstruction]
  11. [Serializable]
  12. public partial struct quaternion : System.IEquatable<quaternion>, IFormattable
  13. {
  14. /// <summary>
  15. /// The quaternion component values.
  16. /// </summary>
  17. public float4 value;
  18. /// <summary>A quaternion representing the identity transform.</summary>
  19. public static readonly quaternion identity = new quaternion(0.0f, 0.0f, 0.0f, 1.0f);
  20. /// <summary>Constructs a quaternion from four float values.</summary>
  21. /// <param name="x">The quaternion x component.</param>
  22. /// <param name="y">The quaternion y component.</param>
  23. /// <param name="z">The quaternion z component.</param>
  24. /// <param name="w">The quaternion w component.</param>
  25. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  26. public quaternion(float x, float y, float z, float w) { value.x = x; value.y = y; value.z = z; value.w = w; }
  27. /// <summary>Constructs a quaternion from float4 vector.</summary>
  28. /// <param name="value">The quaternion xyzw component values.</param>
  29. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  30. public quaternion(float4 value) { this.value = value; }
  31. /// <summary>Implicitly converts a float4 vector to a quaternion.</summary>
  32. /// <param name="v">The quaternion xyzw component values.</param>
  33. /// <returns>The quaternion constructed from a float4 vector.</returns>
  34. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  35. public static implicit operator quaternion(float4 v) { return new quaternion(v); }
  36. /// <summary>Constructs a unit quaternion from a float3x3 rotation matrix. The matrix must be orthonormal.</summary>
  37. /// <param name="m">The float3x3 orthonormal rotation matrix.</param>
  38. public quaternion(float3x3 m)
  39. {
  40. float3 u = m.c0;
  41. float3 v = m.c1;
  42. float3 w = m.c2;
  43. uint u_sign = (asuint(u.x) & 0x80000000);
  44. float t = v.y + asfloat(asuint(w.z) ^ u_sign);
  45. uint4 u_mask = uint4((int)u_sign >> 31);
  46. uint4 t_mask = uint4(asint(t) >> 31);
  47. float tr = 1.0f + abs(u.x);
  48. uint4 sign_flips = uint4(0x00000000, 0x80000000, 0x80000000, 0x80000000) ^ (u_mask & uint4(0x00000000, 0x80000000, 0x00000000, 0x80000000)) ^ (t_mask & uint4(0x80000000, 0x80000000, 0x80000000, 0x00000000));
  49. value = float4(tr, u.y, w.x, v.z) + asfloat(asuint(float4(t, v.x, u.z, w.y)) ^ sign_flips); // +---, +++-, ++-+, +-++
  50. value = asfloat((asuint(value) & ~u_mask) | (asuint(value.zwxy) & u_mask));
  51. value = asfloat((asuint(value.wzyx) & ~t_mask) | (asuint(value) & t_mask));
  52. value = normalize(value);
  53. }
  54. /// <summary>Constructs a unit quaternion from an orthonormal float4x4 matrix.</summary>
  55. /// <param name="m">The float4x4 orthonormal rotation matrix.</param>
  56. public quaternion(float4x4 m)
  57. {
  58. float4 u = m.c0;
  59. float4 v = m.c1;
  60. float4 w = m.c2;
  61. uint u_sign = (asuint(u.x) & 0x80000000);
  62. float t = v.y + asfloat(asuint(w.z) ^ u_sign);
  63. uint4 u_mask = uint4((int)u_sign >> 31);
  64. uint4 t_mask = uint4(asint(t) >> 31);
  65. float tr = 1.0f + abs(u.x);
  66. uint4 sign_flips = uint4(0x00000000, 0x80000000, 0x80000000, 0x80000000) ^ (u_mask & uint4(0x00000000, 0x80000000, 0x00000000, 0x80000000)) ^ (t_mask & uint4(0x80000000, 0x80000000, 0x80000000, 0x00000000));
  67. value = float4(tr, u.y, w.x, v.z) + asfloat(asuint(float4(t, v.x, u.z, w.y)) ^ sign_flips); // +---, +++-, ++-+, +-++
  68. value = asfloat((asuint(value) & ~u_mask) | (asuint(value.zwxy) & u_mask));
  69. value = asfloat((asuint(value.wzyx) & ~t_mask) | (asuint(value) & t_mask));
  70. value = normalize(value);
  71. }
  72. /// <summary>
  73. /// Returns a quaternion representing a rotation around a unit axis by an angle in radians.
  74. /// The rotation direction is clockwise when looking along the rotation axis towards the origin.
  75. /// </summary>
  76. /// <param name="axis">The axis of rotation.</param>
  77. /// <param name="angle">The angle of rotation in radians.</param>
  78. /// <returns>The quaternion representing a rotation around an axis.</returns>
  79. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  80. public static quaternion AxisAngle(float3 axis, float angle)
  81. {
  82. float sina, cosa;
  83. math.sincos(0.5f * angle, out sina, out cosa);
  84. return quaternion(float4(axis * sina, cosa));
  85. }
  86. /// <summary>
  87. /// Returns a quaternion constructed by first performing a rotation around the x-axis, then the y-axis and finally the z-axis.
  88. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  89. /// </summary>
  90. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  91. /// <returns>The quaternion representing the Euler angle rotation in x-y-z order.</returns>
  92. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  93. public static quaternion EulerXYZ(float3 xyz)
  94. {
  95. // return mul(rotateZ(xyz.z), mul(rotateY(xyz.y), rotateX(xyz.x)));
  96. float3 s, c;
  97. sincos(0.5f * xyz, out s, out c);
  98. return quaternion(
  99. // s.x * c.y * c.z - s.y * s.z * c.x,
  100. // s.y * c.x * c.z + s.x * s.z * c.y,
  101. // s.z * c.x * c.y - s.x * s.y * c.z,
  102. // c.x * c.y * c.z + s.y * s.z * s.x
  103. 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)
  104. );
  105. }
  106. /// <summary>
  107. /// Returns a quaternion constructed by first performing a rotation around the x-axis, then the z-axis and finally the y-axis.
  108. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  109. /// </summary>
  110. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  111. /// <returns>The quaternion representing the Euler angle rotation in x-z-y order.</returns>
  112. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  113. public static quaternion EulerXZY(float3 xyz)
  114. {
  115. // return mul(rotateY(xyz.y), mul(rotateZ(xyz.z), rotateX(xyz.x)));
  116. float3 s, c;
  117. sincos(0.5f * xyz, out s, out c);
  118. return quaternion(
  119. // s.x * c.y * c.z + s.y * s.z * c.x,
  120. // s.y * c.x * c.z + s.x * s.z * c.y,
  121. // s.z * c.x * c.y - s.x * s.y * c.z,
  122. // c.x * c.y * c.z - s.y * s.z * s.x
  123. 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)
  124. );
  125. }
  126. /// <summary>
  127. /// Returns a quaternion constructed by first performing a rotation around the y-axis, then the x-axis and finally the z-axis.
  128. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  129. /// </summary>
  130. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  131. /// <returns>The quaternion representing the Euler angle rotation in y-x-z order.</returns>
  132. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  133. public static quaternion EulerYXZ(float3 xyz)
  134. {
  135. // return mul(rotateZ(xyz.z), mul(rotateX(xyz.x), rotateY(xyz.y)));
  136. float3 s, c;
  137. sincos(0.5f * xyz, out s, out c);
  138. return quaternion(
  139. // s.x * c.y * c.z - s.y * s.z * c.x,
  140. // s.y * c.x * c.z + s.x * s.z * c.y,
  141. // s.z * c.x * c.y + s.x * s.y * c.z,
  142. // c.x * c.y * c.z - s.y * s.z * s.x
  143. 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)
  144. );
  145. }
  146. /// <summary>
  147. /// Returns a quaternion constructed by first performing a rotation around the y-axis, then the z-axis and finally the x-axis.
  148. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  149. /// </summary>
  150. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  151. /// <returns>The quaternion representing the Euler angle rotation in y-z-x order.</returns>
  152. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  153. public static quaternion EulerYZX(float3 xyz)
  154. {
  155. // return mul(rotateX(xyz.x), mul(rotateZ(xyz.z), rotateY(xyz.y)));
  156. float3 s, c;
  157. sincos(0.5f * xyz, out s, out c);
  158. return quaternion(
  159. // s.x * c.y * c.z - s.y * s.z * c.x,
  160. // s.y * c.x * c.z - s.x * s.z * c.y,
  161. // s.z * c.x * c.y + s.x * s.y * c.z,
  162. // c.x * c.y * c.z + s.y * s.z * s.x
  163. 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)
  164. );
  165. }
  166. /// <summary>
  167. /// Returns a quaternion constructed by first performing a rotation around the z-axis, then the x-axis and finally the y-axis.
  168. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  169. /// This is the default order rotation order in Unity.
  170. /// </summary>
  171. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  172. /// <returns>The quaternion representing the Euler angle rotation in z-x-y order.</returns>
  173. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  174. public static quaternion EulerZXY(float3 xyz)
  175. {
  176. // return mul(rotateY(xyz.y), mul(rotateX(xyz.x), rotateZ(xyz.z)));
  177. float3 s, c;
  178. sincos(0.5f * xyz, out s, out c);
  179. return quaternion(
  180. // s.x * c.y * c.z + s.y * s.z * c.x,
  181. // s.y * c.x * c.z - s.x * s.z * c.y,
  182. // s.z * c.x * c.y - s.x * s.y * c.z,
  183. // c.x * c.y * c.z + s.y * s.z * s.x
  184. 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)
  185. );
  186. }
  187. /// <summary>
  188. /// Returns a quaternion constructed by first performing a rotation around the z-axis, then the y-axis and finally the x-axis.
  189. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  190. /// </summary>
  191. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  192. /// <returns>The quaternion representing the Euler angle rotation in z-y-x order.</returns>
  193. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  194. public static quaternion EulerZYX(float3 xyz)
  195. {
  196. // return mul(rotateX(xyz.x), mul(rotateY(xyz.y), rotateZ(xyz.z)));
  197. float3 s, c;
  198. sincos(0.5f * xyz, out s, out c);
  199. return quaternion(
  200. // s.x * c.y * c.z + s.y * s.z * c.x,
  201. // s.y * c.x * c.z - s.x * s.z * c.y,
  202. // s.z * c.x * c.y + s.x * s.y * c.z,
  203. // c.x * c.y * c.z - s.y * s.x * s.z
  204. 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)
  205. );
  206. }
  207. /// <summary>
  208. /// Returns a quaternion constructed by first performing a rotation around the x-axis, then the y-axis and finally the z-axis.
  209. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  210. /// </summary>
  211. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  212. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  213. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  214. /// <returns>The quaternion representing the Euler angle rotation in x-y-z order.</returns>
  215. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  216. public static quaternion EulerXYZ(float x, float y, float z) { return EulerXYZ(float3(x, y, z)); }
  217. /// <summary>
  218. /// Returns a quaternion constructed by first performing a rotation around the x-axis, then the z-axis and finally the y-axis.
  219. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  220. /// </summary>
  221. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  222. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  223. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  224. /// <returns>The quaternion representing the Euler angle rotation in x-z-y order.</returns>
  225. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  226. public static quaternion EulerXZY(float x, float y, float z) { return EulerXZY(float3(x, y, z)); }
  227. /// <summary>
  228. /// Returns a quaternion constructed by first performing a rotation around the y-axis, then the x-axis and finally the z-axis.
  229. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  230. /// </summary>
  231. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  232. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  233. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  234. /// <returns>The quaternion representing the Euler angle rotation in y-x-z order.</returns>
  235. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  236. public static quaternion EulerYXZ(float x, float y, float z) { return EulerYXZ(float3(x, y, z)); }
  237. /// <summary>
  238. /// Returns a quaternion constructed by first performing a rotation around the y-axis, then the z-axis and finally the x-axis.
  239. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  240. /// </summary>
  241. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  242. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  243. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  244. /// <returns>The quaternion representing the Euler angle rotation in y-z-x order.</returns>
  245. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  246. public static quaternion EulerYZX(float x, float y, float z) { return EulerYZX(float3(x, y, z)); }
  247. /// <summary>
  248. /// Returns a quaternion constructed by first performing a rotation around the z-axis, then the x-axis and finally the y-axis.
  249. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  250. /// This is the default order rotation order in Unity.
  251. /// </summary>
  252. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  253. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  254. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  255. /// <returns>The quaternion representing the Euler angle rotation in z-x-y order.</returns>
  256. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  257. public static quaternion EulerZXY(float x, float y, float z) { return EulerZXY(float3(x, y, z)); }
  258. /// <summary>
  259. /// Returns a quaternion constructed by first performing a rotation around the z-axis, then the y-axis and finally the x-axis.
  260. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  261. /// </summary>
  262. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  263. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  264. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  265. /// <returns>The quaternion representing the Euler angle rotation in z-y-x order.</returns>
  266. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  267. public static quaternion EulerZYX(float x, float y, float z) { return EulerZYX(float3(x, y, z)); }
  268. /// <summary>
  269. /// Returns a quaternion constructed by first performing 3 rotations around the principal axes in a given order.
  270. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  271. /// When the rotation order is known at compile time, it is recommended for performance reasons to use specific
  272. /// Euler rotation constructors such as EulerZXY(...).
  273. /// </summary>
  274. /// <param name="xyz">A float3 vector containing the rotation angles around the x-, y- and z-axis measures in radians.</param>
  275. /// <param name="order">The order in which the rotations are applied.</param>
  276. /// <returns>The quaternion representing the Euler angle rotation in the specified order.</returns>
  277. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  278. public static quaternion Euler(float3 xyz, RotationOrder order = RotationOrder.ZXY)
  279. {
  280. switch (order)
  281. {
  282. case RotationOrder.XYZ:
  283. return EulerXYZ(xyz);
  284. case RotationOrder.XZY:
  285. return EulerXZY(xyz);
  286. case RotationOrder.YXZ:
  287. return EulerYXZ(xyz);
  288. case RotationOrder.YZX:
  289. return EulerYZX(xyz);
  290. case RotationOrder.ZXY:
  291. return EulerZXY(xyz);
  292. case RotationOrder.ZYX:
  293. return EulerZYX(xyz);
  294. default:
  295. return quaternion.identity;
  296. }
  297. }
  298. /// <summary>
  299. /// Returns a quaternion constructed by first performing 3 rotations around the principal axes in a given order.
  300. /// All rotation angles are in radians and clockwise when looking along the rotation axis towards the origin.
  301. /// When the rotation order is known at compile time, it is recommended for performance reasons to use specific
  302. /// Euler rotation constructors such as EulerZXY(...).
  303. /// </summary>
  304. /// <param name="x">The rotation angle around the x-axis in radians.</param>
  305. /// <param name="y">The rotation angle around the y-axis in radians.</param>
  306. /// <param name="z">The rotation angle around the z-axis in radians.</param>
  307. /// <param name="order">The order in which the rotations are applied.</param>
  308. /// <returns>The quaternion representing the Euler angle rotation in the specified order.</returns>
  309. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  310. public static quaternion Euler(float x, float y, float z, RotationOrder order = RotationOrder.Default)
  311. {
  312. return Euler(float3(x, y, z), order);
  313. }
  314. /// <summary>Returns a quaternion that rotates around the x-axis by a given number of radians.</summary>
  315. /// <param name="angle">The clockwise rotation angle when looking along the x-axis towards the origin in radians.</param>
  316. /// <returns>The quaternion representing a rotation around the x-axis.</returns>
  317. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  318. public static quaternion RotateX(float angle)
  319. {
  320. float sina, cosa;
  321. math.sincos(0.5f * angle, out sina, out cosa);
  322. return quaternion(sina, 0.0f, 0.0f, cosa);
  323. }
  324. /// <summary>Returns a quaternion that rotates around the y-axis by a given number of radians.</summary>
  325. /// <param name="angle">The clockwise rotation angle when looking along the y-axis towards the origin in radians.</param>
  326. /// <returns>The quaternion representing a rotation around the y-axis.</returns>
  327. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  328. public static quaternion RotateY(float angle)
  329. {
  330. float sina, cosa;
  331. math.sincos(0.5f * angle, out sina, out cosa);
  332. return quaternion(0.0f, sina, 0.0f, cosa);
  333. }
  334. /// <summary>Returns a quaternion that rotates around the z-axis by a given number of radians.</summary>
  335. /// <param name="angle">The clockwise rotation angle when looking along the z-axis towards the origin in radians.</param>
  336. /// <returns>The quaternion representing a rotation around the z-axis.</returns>
  337. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  338. public static quaternion RotateZ(float angle)
  339. {
  340. float sina, cosa;
  341. math.sincos(0.5f * angle, out sina, out cosa);
  342. return quaternion(0.0f, 0.0f, sina, cosa);
  343. }
  344. /// <summary>
  345. /// Returns a quaternion view rotation given a unit length forward vector and a unit length up vector.
  346. /// The two input vectors are assumed to be unit length and not collinear.
  347. /// If these assumptions are not met use float3x3.LookRotationSafe instead.
  348. /// </summary>
  349. /// <param name="forward">The view forward direction.</param>
  350. /// <param name="up">The view up direction.</param>
  351. /// <returns>The quaternion view rotation.</returns>
  352. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  353. public static quaternion LookRotation(float3 forward, float3 up)
  354. {
  355. float3 t = normalize(cross(up, forward));
  356. return quaternion(float3x3(t, cross(forward, t), forward));
  357. }
  358. /// <summary>
  359. /// Returns a quaternion view rotation given a forward vector and an up vector.
  360. /// The two input vectors are not assumed to be unit length.
  361. /// If the magnitude of either of the vectors is so extreme that the calculation cannot be carried out reliably or the vectors are collinear,
  362. /// the identity will be returned instead.
  363. /// </summary>
  364. /// <param name="forward">The view forward direction.</param>
  365. /// <param name="up">The view up direction.</param>
  366. /// <returns>The quaternion view rotation or the identity quaternion.</returns>
  367. public static quaternion LookRotationSafe(float3 forward, float3 up)
  368. {
  369. float forwardLengthSq = dot(forward, forward);
  370. float upLengthSq = dot(up, up);
  371. forward *= rsqrt(forwardLengthSq);
  372. up *= rsqrt(upLengthSq);
  373. float3 t = cross(up, forward);
  374. float tLengthSq = dot(t, t);
  375. t *= rsqrt(tLengthSq);
  376. float mn = min(min(forwardLengthSq, upLengthSq), tLengthSq);
  377. float mx = max(max(forwardLengthSq, upLengthSq), tLengthSq);
  378. bool accept = mn > 1e-35f && mx < 1e35f && isfinite(forwardLengthSq) && isfinite(upLengthSq) && isfinite(tLengthSq);
  379. return quaternion(select(float4(0.0f, 0.0f, 0.0f, 1.0f), quaternion(float3x3(t, cross(forward, t),forward)).value, accept));
  380. }
  381. /// <summary>Returns true if the quaternion is equal to a given quaternion, false otherwise.</summary>
  382. /// <param name="x">The quaternion to compare with.</param>
  383. /// <returns>True if the quaternion is equal to the input, false otherwise.</returns>
  384. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  385. 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; }
  386. /// <summary>Returns whether true if the quaternion is equal to a given quaternion, false otherwise.</summary>
  387. /// <param name="x">The object to compare with.</param>
  388. /// <returns>True if the quaternion is equal to the input, false otherwise.</returns>
  389. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  390. public override bool Equals(object x) { return x is quaternion converted && Equals(converted); }
  391. /// <summary>Returns a hash code for the quaternion.</summary>
  392. /// <returns>The hash code of the quaternion.</returns>
  393. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  394. public override int GetHashCode() { return (int)math.hash(this); }
  395. /// <summary>Returns a string representation of the quaternion.</summary>
  396. /// <returns>The string representation of the quaternion.</returns>
  397. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  398. public override string ToString()
  399. {
  400. return string.Format("quaternion({0}f, {1}f, {2}f, {3}f)", value.x, value.y, value.z, value.w);
  401. }
  402. /// <summary>Returns a string representation of the quaternion using a specified format and culture-specific format information.</summary>
  403. /// <param name="format">The format string.</param>
  404. /// <param name="formatProvider">The format provider to use during string formatting.</param>
  405. /// <returns>The formatted string representation of the quaternion.</returns>
  406. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  407. public string ToString(string format, IFormatProvider formatProvider)
  408. {
  409. 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));
  410. }
  411. }
  412. public static partial class math
  413. {
  414. /// <summary>Returns a quaternion constructed from four float values.</summary>
  415. /// <param name="x">The x component of the quaternion.</param>
  416. /// <param name="y">The y component of the quaternion.</param>
  417. /// <param name="z">The z component of the quaternion.</param>
  418. /// <param name="w">The w component of the quaternion.</param>
  419. /// <returns>The quaternion constructed from individual components.</returns>
  420. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  421. public static quaternion quaternion(float x, float y, float z, float w) { return new quaternion(x, y, z, w); }
  422. /// <summary>Returns a quaternion constructed from a float4 vector.</summary>
  423. /// <param name="value">The float4 containing the components of the quaternion.</param>
  424. /// <returns>The quaternion constructed from a float4.</returns>
  425. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  426. public static quaternion quaternion(float4 value) { return new quaternion(value); }
  427. /// <summary>Returns a unit quaternion constructed from a float3x3 rotation matrix. The matrix must be orthonormal.</summary>
  428. /// <param name="m">The float3x3 rotation matrix.</param>
  429. /// <returns>The quaternion constructed from a float3x3 matrix.</returns>
  430. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  431. public static quaternion quaternion(float3x3 m) { return new quaternion(m); }
  432. /// <summary>Returns a unit quaternion constructed from a float4x4 matrix. The matrix must be orthonormal.</summary>
  433. /// <param name="m">The float4x4 matrix (must be orthonormal).</param>
  434. /// <returns>The quaternion constructed from a float4x4 matrix.</returns>
  435. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  436. public static quaternion quaternion(float4x4 m) { return new quaternion(m); }
  437. /// <summary>Returns the conjugate of a quaternion value.</summary>
  438. /// <param name="q">The quaternion to conjugate.</param>
  439. /// <returns>The conjugate of the input quaternion.</returns>
  440. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  441. public static quaternion conjugate(quaternion q)
  442. {
  443. return quaternion(q.value * float4(-1.0f, -1.0f, -1.0f, 1.0f));
  444. }
  445. /// <summary>Returns the inverse of a quaternion value.</summary>
  446. /// <param name="q">The quaternion to invert.</param>
  447. /// <returns>The quaternion inverse of the input quaternion.</returns>
  448. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  449. public static quaternion inverse(quaternion q)
  450. {
  451. float4 x = q.value;
  452. return quaternion(rcp(dot(x, x)) * x * float4(-1.0f, -1.0f, -1.0f, 1.0f));
  453. }
  454. /// <summary>Returns the dot product of two quaternions.</summary>
  455. /// <param name="a">The first quaternion.</param>
  456. /// <param name="b">The second quaternion.</param>
  457. /// <returns>The dot product of two quaternions.</returns>
  458. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  459. public static float dot(quaternion a, quaternion b)
  460. {
  461. return dot(a.value, b.value);
  462. }
  463. /// <summary>Returns the length of a quaternion.</summary>
  464. /// <param name="q">The input quaternion.</param>
  465. /// <returns>The length of the input quaternion.</returns>
  466. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  467. public static float length(quaternion q)
  468. {
  469. return sqrt(dot(q.value, q.value));
  470. }
  471. /// <summary>Returns the squared length of a quaternion.</summary>
  472. /// <param name="q">The input quaternion.</param>
  473. /// <returns>The length squared of the input quaternion.</returns>
  474. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  475. public static float lengthsq(quaternion q)
  476. {
  477. return dot(q.value, q.value);
  478. }
  479. /// <summary>Returns a normalized version of a quaternion q by scaling it by 1 / length(q).</summary>
  480. /// <param name="q">The quaternion to normalize.</param>
  481. /// <returns>The normalized quaternion.</returns>
  482. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  483. public static quaternion normalize(quaternion q)
  484. {
  485. float4 x = q.value;
  486. return quaternion(rsqrt(dot(x, x)) * x);
  487. }
  488. /// <summary>
  489. /// Returns a safe normalized version of the q by scaling it by 1 / length(q).
  490. /// Returns the identity when 1 / length(q) does not produce a finite number.
  491. /// </summary>
  492. /// <param name="q">The quaternion to normalize.</param>
  493. /// <returns>The normalized quaternion or the identity quaternion.</returns>
  494. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  495. public static quaternion normalizesafe(quaternion q)
  496. {
  497. float4 x = q.value;
  498. float len = math.dot(x, x);
  499. return quaternion(math.select(Mathematics.quaternion.identity.value, x * math.rsqrt(len), len > FLT_MIN_NORMAL));
  500. }
  501. /// <summary>
  502. /// Returns a safe normalized version of the q by scaling it by 1 / length(q).
  503. /// Returns the given default value when 1 / length(q) does not produce a finite number.
  504. /// </summary>
  505. /// <param name="q">The quaternion to normalize.</param>
  506. /// <param name="defaultvalue">The default value.</param>
  507. /// <returns>The normalized quaternion or the default value.</returns>
  508. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  509. public static quaternion normalizesafe(quaternion q, quaternion defaultvalue)
  510. {
  511. float4 x = q.value;
  512. float len = math.dot(x, x);
  513. return quaternion(math.select(defaultvalue.value, x * math.rsqrt(len), len > FLT_MIN_NORMAL));
  514. }
  515. /// <summary>Returns the natural exponent of a quaternion. Assumes w is zero.</summary>
  516. /// <param name="q">The quaternion with w component equal to zero.</param>
  517. /// <returns>The natural exponent of the input quaternion.</returns>
  518. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  519. public static quaternion unitexp(quaternion q)
  520. {
  521. float v_rcp_len = rsqrt(dot(q.value.xyz, q.value.xyz));
  522. float v_len = rcp(v_rcp_len);
  523. float sin_v_len, cos_v_len;
  524. sincos(v_len, out sin_v_len, out cos_v_len);
  525. return quaternion(float4(q.value.xyz * v_rcp_len * sin_v_len, cos_v_len));
  526. }
  527. /// <summary>Returns the natural exponent of a quaternion.</summary>
  528. /// <param name="q">The quaternion.</param>
  529. /// <returns>The natural exponent of the input quaternion.</returns>
  530. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  531. public static quaternion exp(quaternion q)
  532. {
  533. float v_rcp_len = rsqrt(dot(q.value.xyz, q.value.xyz));
  534. float v_len = rcp(v_rcp_len);
  535. float sin_v_len, cos_v_len;
  536. sincos(v_len, out sin_v_len, out cos_v_len);
  537. return quaternion(float4(q.value.xyz * v_rcp_len * sin_v_len, cos_v_len) * exp(q.value.w));
  538. }
  539. /// <summary>Returns the natural logarithm of a unit length quaternion.</summary>
  540. /// <param name="q">The unit length quaternion.</param>
  541. /// <returns>The natural logarithm of the unit length quaternion.</returns>
  542. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  543. public static quaternion unitlog(quaternion q)
  544. {
  545. float w = clamp(q.value.w, -1.0f, 1.0f);
  546. float s = acos(w) * rsqrt(1.0f - w*w);
  547. return quaternion(float4(q.value.xyz * s, 0.0f));
  548. }
  549. /// <summary>Returns the natural logarithm of a quaternion.</summary>
  550. /// <param name="q">The quaternion.</param>
  551. /// <returns>The natural logarithm of the input quaternion.</returns>
  552. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  553. public static quaternion log(quaternion q)
  554. {
  555. float v_len_sq = dot(q.value.xyz, q.value.xyz);
  556. float q_len_sq = v_len_sq + q.value.w*q.value.w;
  557. float s = acos(clamp(q.value.w * rsqrt(q_len_sq), -1.0f, 1.0f)) * rsqrt(v_len_sq);
  558. return quaternion(float4(q.value.xyz * s, 0.5f * log(q_len_sq)));
  559. }
  560. /// <summary>Returns the result of transforming the quaternion b by the quaternion a.</summary>
  561. /// <param name="a">The quaternion on the left.</param>
  562. /// <param name="b">The quaternion on the right.</param>
  563. /// <returns>The result of transforming quaternion b by the quaternion a.</returns>
  564. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  565. public static quaternion mul(quaternion a, quaternion b)
  566. {
  567. 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);
  568. }
  569. /// <summary>Returns the result of transforming a vector by a quaternion.</summary>
  570. /// <param name="q">The quaternion transformation.</param>
  571. /// <param name="v">The vector to transform.</param>
  572. /// <returns>The transformation of vector v by quaternion q.</returns>
  573. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  574. public static float3 mul(quaternion q, float3 v)
  575. {
  576. float3 t = 2 * cross(q.value.xyz, v);
  577. return v + q.value.w * t + cross(q.value.xyz, t);
  578. }
  579. /// <summary>Returns the result of rotating a vector by a unit quaternion.</summary>
  580. /// <param name="q">The quaternion rotation.</param>
  581. /// <param name="v">The vector to rotate.</param>
  582. /// <returns>The rotation of vector v by quaternion q.</returns>
  583. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  584. public static float3 rotate(quaternion q, float3 v)
  585. {
  586. float3 t = 2 * cross(q.value.xyz, v);
  587. return v + q.value.w * t + cross(q.value.xyz, t);
  588. }
  589. /// <summary>Returns the result of a normalized linear interpolation between two quaternions q1 and a2 using an interpolation parameter t.</summary>
  590. /// <remarks>
  591. /// Prefer to use this over slerp() when you know the distance between q1 and q2 is small. This can be much
  592. /// higher performance due to avoiding trigonometric function evaluations that occur in slerp().
  593. /// </remarks>
  594. /// <param name="q1">The first quaternion.</param>
  595. /// <param name="q2">The second quaternion.</param>
  596. /// <param name="t">The interpolation parameter.</param>
  597. /// <returns>The normalized linear interpolation of two quaternions.</returns>
  598. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  599. public static quaternion nlerp(quaternion q1, quaternion q2, float t)
  600. {
  601. return normalize(q1.value + t * (chgsign(q2.value, dot(q1, q2)) - q1.value));
  602. }
  603. /// <summary>Returns the result of a spherical interpolation between two quaternions q1 and a2 using an interpolation parameter t.</summary>
  604. /// <param name="q1">The first quaternion.</param>
  605. /// <param name="q2">The second quaternion.</param>
  606. /// <param name="t">The interpolation parameter.</param>
  607. /// <returns>The spherical linear interpolation of two quaternions.</returns>
  608. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  609. public static quaternion slerp(quaternion q1, quaternion q2, float t)
  610. {
  611. float dt = dot(q1, q2);
  612. if (dt < 0.0f)
  613. {
  614. dt = -dt;
  615. q2.value = -q2.value;
  616. }
  617. if (dt < 0.9995f)
  618. {
  619. float angle = acos(dt);
  620. float s = rsqrt(1.0f - dt * dt); // 1.0f / sin(angle)
  621. float w1 = sin(angle * (1.0f - t)) * s;
  622. float w2 = sin(angle * t) * s;
  623. return quaternion(q1.value * w1 + q2.value * w2);
  624. }
  625. else
  626. {
  627. // if the angle is small, use linear interpolation
  628. return nlerp(q1, q2, t);
  629. }
  630. }
  631. /// <summary>Returns the angle in radians between two unit quaternions.</summary>
  632. /// <param name="q1">The first quaternion.</param>
  633. /// <param name="q2">The second quaternion.</param>
  634. /// <returns>The angle between two unit quaternions.</returns>
  635. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  636. public static float angle(quaternion q1, quaternion q2)
  637. {
  638. float diff = asin(length(normalize(mul(conjugate(q1), q2)).value.xyz));
  639. return diff + diff;
  640. }
  641. /// <summary>
  642. /// Extracts the rotation from a matrix.
  643. /// </summary>
  644. /// <remarks>This method supports any type of rotation matrix: if the matrix has a non uniform scale you should use this method.</remarks>
  645. /// <param name="m">Matrix to extract rotation from</param>
  646. /// <returns>Extracted rotation</returns>
  647. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  648. public static quaternion rotation(float3x3 m)
  649. {
  650. float det = math.determinant(m);
  651. if (math.abs(1f - det) < svd.k_EpsilonDeterminant)
  652. return math.quaternion(m);
  653. if (math.abs(det) > svd.k_EpsilonDeterminant)
  654. {
  655. float3x3 tmp = mulScale(m, math.rsqrt(math.float3(math.lengthsq(m.c0), math.lengthsq(m.c1), math.lengthsq(m.c2))));
  656. if (math.abs(1f - math.determinant(tmp)) < svd.k_EpsilonDeterminant)
  657. return math.quaternion(tmp);
  658. }
  659. return svd.svdRotation(m);
  660. }
  661. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  662. static float3x3 adj(float3x3 m, out float det)
  663. {
  664. float3x3 adjT;
  665. adjT.c0 = math.cross(m.c1, m.c2);
  666. adjT.c1 = math.cross(m.c2, m.c0);
  667. adjT.c2 = math.cross(m.c0, m.c1);
  668. det = math.dot(m.c0, adjT.c0);
  669. return math.transpose(adjT);
  670. }
  671. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  672. static bool adjInverse(float3x3 m, out float3x3 i, float epsilon = svd.k_EpsilonNormal)
  673. {
  674. i = adj(m, out float det);
  675. bool c = math.abs(det) > epsilon;
  676. float3 detInv = math.select(math.float3(1f), math.rcp(det), c);
  677. i = scaleMul(detInv, i);
  678. return c;
  679. }
  680. /// <summary>Returns a uint hash code of a quaternion.</summary>
  681. /// <param name="q">The quaternion to hash.</param>
  682. /// <returns>The hash code for the input quaternion.</returns>
  683. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  684. public static uint hash(quaternion q)
  685. {
  686. return hash(q.value);
  687. }
  688. /// <summary>
  689. /// Returns a uint4 vector hash code of a quaternion.
  690. /// When multiple elements are to be hashes together, it can more efficient to calculate and combine wide hash
  691. /// that are only reduced to a narrow uint hash at the very end instead of at every step.
  692. /// </summary>
  693. /// <param name="q">The quaternion to hash.</param>
  694. /// <returns>The uint4 vector hash code of the input quaternion.</returns>
  695. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  696. public static uint4 hashwide(quaternion q)
  697. {
  698. return hashwide(q.value);
  699. }
  700. /// <summary>
  701. /// Transforms the forward vector by a quaternion.
  702. /// </summary>
  703. /// <param name="q">The quaternion transformation.</param>
  704. /// <returns>The forward vector transformed by the input quaternion.</returns>
  705. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  706. public static float3 forward(quaternion q) { return mul(q, float3(0, 0, 1)); } // for compatibility
  707. }
  708. }