Quat.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. using System;
  2. using System.Runtime.InteropServices;
  3. #if REAL_T_IS_DOUBLE
  4. using real_t = System.Double;
  5. #else
  6. using real_t = System.Single;
  7. #endif
  8. namespace Godot
  9. {
  10. [StructLayout(LayoutKind.Sequential)]
  11. public struct Quat : IEquatable<Quat>
  12. {
  13. public real_t x;
  14. public real_t y;
  15. public real_t z;
  16. public real_t w;
  17. public real_t this[int index]
  18. {
  19. get
  20. {
  21. switch (index)
  22. {
  23. case 0:
  24. return x;
  25. case 1:
  26. return y;
  27. case 2:
  28. return z;
  29. case 3:
  30. return w;
  31. default:
  32. throw new IndexOutOfRangeException();
  33. }
  34. }
  35. set
  36. {
  37. switch (index)
  38. {
  39. case 0:
  40. x = value;
  41. break;
  42. case 1:
  43. y = value;
  44. break;
  45. case 2:
  46. z = value;
  47. break;
  48. case 3:
  49. w = value;
  50. break;
  51. default:
  52. throw new IndexOutOfRangeException();
  53. }
  54. }
  55. }
  56. public real_t Length
  57. {
  58. get { return Mathf.Sqrt(LengthSquared); }
  59. }
  60. public real_t LengthSquared
  61. {
  62. get { return Dot(this); }
  63. }
  64. public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t)
  65. {
  66. real_t t2 = (1.0f - t) * t * 2f;
  67. Quat sp = Slerp(b, t);
  68. Quat sq = preA.Slerpni(postB, t);
  69. return sp.Slerpni(sq, t2);
  70. }
  71. public real_t Dot(Quat b)
  72. {
  73. return x * b.x + y * b.y + z * b.z + w * b.w;
  74. }
  75. public Vector3 GetEuler()
  76. {
  77. var basis = new Basis(this);
  78. return basis.GetEuler();
  79. }
  80. public Quat Inverse()
  81. {
  82. return new Quat(-x, -y, -z, w);
  83. }
  84. public Quat Normalized()
  85. {
  86. return this / Length;
  87. }
  88. public void Set(real_t x, real_t y, real_t z, real_t w)
  89. {
  90. this.x = x;
  91. this.y = y;
  92. this.z = z;
  93. this.w = w;
  94. }
  95. public void Set(Quat q)
  96. {
  97. this = q;
  98. }
  99. public void SetAxisAngle(Vector3 axis, real_t angle)
  100. {
  101. this = new Quat(axis, angle);
  102. }
  103. public void SetEuler(Vector3 eulerYXZ)
  104. {
  105. this = new Quat(eulerYXZ);
  106. }
  107. public Quat Slerp(Quat b, real_t t)
  108. {
  109. // Calculate cosine
  110. real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w;
  111. var to1 = new Quat();
  112. // Adjust signs if necessary
  113. if (cosom < 0.0)
  114. {
  115. cosom = -cosom;
  116. to1.x = -b.x;
  117. to1.y = -b.y;
  118. to1.z = -b.z;
  119. to1.w = -b.w;
  120. }
  121. else
  122. {
  123. to1.x = b.x;
  124. to1.y = b.y;
  125. to1.z = b.z;
  126. to1.w = b.w;
  127. }
  128. real_t sinom, scale0, scale1;
  129. // Calculate coefficients
  130. if (1.0 - cosom > Mathf.Epsilon)
  131. {
  132. // Standard case (Slerp)
  133. real_t omega = Mathf.Acos(cosom);
  134. sinom = Mathf.Sin(omega);
  135. scale0 = Mathf.Sin((1.0f - t) * omega) / sinom;
  136. scale1 = Mathf.Sin(t * omega) / sinom;
  137. }
  138. else
  139. {
  140. // Quaternions are very close so we can do a linear interpolation
  141. scale0 = 1.0f - t;
  142. scale1 = t;
  143. }
  144. // Calculate final values
  145. return new Quat
  146. (
  147. scale0 * x + scale1 * to1.x,
  148. scale0 * y + scale1 * to1.y,
  149. scale0 * z + scale1 * to1.z,
  150. scale0 * w + scale1 * to1.w
  151. );
  152. }
  153. public Quat Slerpni(Quat b, real_t t)
  154. {
  155. real_t dot = Dot(b);
  156. if (Mathf.Abs(dot) > 0.9999f)
  157. {
  158. return this;
  159. }
  160. real_t theta = Mathf.Acos(dot);
  161. real_t sinT = 1.0f / Mathf.Sin(theta);
  162. real_t newFactor = Mathf.Sin(t * theta) * sinT;
  163. real_t invFactor = Mathf.Sin((1.0f - t) * theta) * sinT;
  164. return new Quat
  165. (
  166. invFactor * x + newFactor * b.x,
  167. invFactor * y + newFactor * b.y,
  168. invFactor * z + newFactor * b.z,
  169. invFactor * w + newFactor * b.w
  170. );
  171. }
  172. public Vector3 Xform(Vector3 v)
  173. {
  174. Quat q = this * v;
  175. q *= Inverse();
  176. return new Vector3(q.x, q.y, q.z);
  177. }
  178. // Static Readonly Properties
  179. public static Quat Identity { get; } = new Quat(0f, 0f, 0f, 1f);
  180. // Constructors
  181. public Quat(real_t x, real_t y, real_t z, real_t w)
  182. {
  183. this.x = x;
  184. this.y = y;
  185. this.z = z;
  186. this.w = w;
  187. }
  188. public bool IsNormalized()
  189. {
  190. return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
  191. }
  192. public Quat(Quat q)
  193. {
  194. this = q;
  195. }
  196. public Quat(Basis basis)
  197. {
  198. this = basis.Quat();
  199. }
  200. public Quat(Vector3 eulerYXZ)
  201. {
  202. real_t half_a1 = eulerYXZ.y * (real_t)0.5;
  203. real_t half_a2 = eulerYXZ.x * (real_t)0.5;
  204. real_t half_a3 = eulerYXZ.z * (real_t)0.5;
  205. // R = Y(a1).X(a2).Z(a3) convention for Euler angles.
  206. // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
  207. // a3 is the angle of the first rotation, following the notation in this reference.
  208. real_t cos_a1 = Mathf.Cos(half_a1);
  209. real_t sin_a1 = Mathf.Sin(half_a1);
  210. real_t cos_a2 = Mathf.Cos(half_a2);
  211. real_t sin_a2 = Mathf.Sin(half_a2);
  212. real_t cos_a3 = Mathf.Cos(half_a3);
  213. real_t sin_a3 = Mathf.Sin(half_a3);
  214. x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3;
  215. y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3;
  216. z = -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3;
  217. w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3;
  218. }
  219. public Quat(Vector3 axis, real_t angle)
  220. {
  221. real_t d = axis.Length();
  222. real_t angle_t = angle;
  223. if (d == 0f)
  224. {
  225. x = 0f;
  226. y = 0f;
  227. z = 0f;
  228. w = 0f;
  229. }
  230. else
  231. {
  232. real_t s = Mathf.Sin(angle_t * 0.5f) / d;
  233. x = axis.x * s;
  234. y = axis.y * s;
  235. z = axis.z * s;
  236. w = Mathf.Cos(angle_t * 0.5f);
  237. }
  238. }
  239. public static Quat operator *(Quat left, Quat right)
  240. {
  241. return new Quat
  242. (
  243. left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y,
  244. left.w * right.y + left.y * right.w + left.z * right.x - left.x * right.z,
  245. left.w * right.z + left.z * right.w + left.x * right.y - left.y * right.x,
  246. left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z
  247. );
  248. }
  249. public static Quat operator +(Quat left, Quat right)
  250. {
  251. return new Quat(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w);
  252. }
  253. public static Quat operator -(Quat left, Quat right)
  254. {
  255. return new Quat(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w);
  256. }
  257. public static Quat operator -(Quat left)
  258. {
  259. return new Quat(-left.x, -left.y, -left.z, -left.w);
  260. }
  261. public static Quat operator *(Quat left, Vector3 right)
  262. {
  263. return new Quat
  264. (
  265. left.w * right.x + left.y * right.z - left.z * right.y,
  266. left.w * right.y + left.z * right.x - left.x * right.z,
  267. left.w * right.z + left.x * right.y - left.y * right.x,
  268. -left.x * right.x - left.y * right.y - left.z * right.z
  269. );
  270. }
  271. public static Quat operator *(Vector3 left, Quat right)
  272. {
  273. return new Quat
  274. (
  275. right.w * left.x + right.y * left.z - right.z * left.y,
  276. right.w * left.y + right.z * left.x - right.x * left.z,
  277. right.w * left.z + right.x * left.y - right.y * left.x,
  278. -right.x * left.x - right.y * left.y - right.z * left.z
  279. );
  280. }
  281. public static Quat operator *(Quat left, real_t right)
  282. {
  283. return new Quat(left.x * right, left.y * right, left.z * right, left.w * right);
  284. }
  285. public static Quat operator *(real_t left, Quat right)
  286. {
  287. return new Quat(right.x * left, right.y * left, right.z * left, right.w * left);
  288. }
  289. public static Quat operator /(Quat left, real_t right)
  290. {
  291. return left * (1.0f / right);
  292. }
  293. public static bool operator ==(Quat left, Quat right)
  294. {
  295. return left.Equals(right);
  296. }
  297. public static bool operator !=(Quat left, Quat right)
  298. {
  299. return !left.Equals(right);
  300. }
  301. public override bool Equals(object obj)
  302. {
  303. if (obj is Quat)
  304. {
  305. return Equals((Quat)obj);
  306. }
  307. return false;
  308. }
  309. public bool Equals(Quat other)
  310. {
  311. return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
  312. }
  313. public override int GetHashCode()
  314. {
  315. return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
  316. }
  317. public override string ToString()
  318. {
  319. return String.Format("({0}, {1}, {2}, {3})", x.ToString(), y.ToString(), z.ToString(), w.ToString());
  320. }
  321. public string ToString(string format)
  322. {
  323. return String.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format));
  324. }
  325. }
  326. }