Quaternion.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. //
  2. // Copyright (c) 2008-2017 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #pragma once
  23. #include "../Math/Matrix3.h"
  24. #ifdef ATOMIC_SSE
  25. #include <emmintrin.h>
  26. #endif
  27. namespace Atomic
  28. {
  29. /// Rotation represented as a four-dimensional normalized vector.
  30. class ATOMIC_API Quaternion
  31. {
  32. public:
  33. /// Construct an identity quaternion.
  34. Quaternion()
  35. #ifndef ATOMIC_SSE
  36. :w_(1.0f),
  37. x_(0.0f),
  38. y_(0.0f),
  39. z_(0.0f)
  40. #endif
  41. {
  42. #ifdef ATOMIC_SSE
  43. _mm_storeu_ps(&w_, _mm_set_ps(0.f, 0.f, 0.f, 1.f));
  44. #endif
  45. }
  46. /// Copy-construct from another quaternion.
  47. Quaternion(const Quaternion& quat)
  48. #if defined(ATOMIC_SSE) && (!defined(_MSC_VER) || _MSC_VER >= 1700) /* Visual Studio 2012 and newer. VS2010 has a bug with these, see https://github.com/urho3d/Urho3D/issues/1044 */
  49. {
  50. _mm_storeu_ps(&w_, _mm_loadu_ps(&quat.w_));
  51. }
  52. #else
  53. :w_(quat.w_),
  54. x_(quat.x_),
  55. y_(quat.y_),
  56. z_(quat.z_)
  57. {
  58. }
  59. #endif
  60. /// Construct from values.
  61. Quaternion(float w, float x, float y, float z)
  62. #ifndef ATOMIC_SSE
  63. :w_(w),
  64. x_(x),
  65. y_(y),
  66. z_(z)
  67. #endif
  68. {
  69. #ifdef ATOMIC_SSE
  70. _mm_storeu_ps(&w_, _mm_set_ps(z, y, x, w));
  71. #endif
  72. }
  73. /// Construct from a float array.
  74. explicit Quaternion(const float* data)
  75. #ifndef ATOMIC_SSE
  76. :w_(data[0]),
  77. x_(data[1]),
  78. y_(data[2]),
  79. z_(data[3])
  80. #endif
  81. {
  82. #ifdef ATOMIC_SSE
  83. _mm_storeu_ps(&w_, _mm_loadu_ps(data));
  84. #endif
  85. }
  86. /// Construct from an angle (in degrees) and axis.
  87. Quaternion(float angle, const Vector3& axis)
  88. {
  89. FromAngleAxis(angle, axis);
  90. }
  91. /// Construct from an angle (in degrees, for Atomic2D).
  92. explicit Quaternion(float angle)
  93. {
  94. FromAngleAxis(angle, Vector3::FORWARD);
  95. }
  96. /// Construct from Euler angles (in degrees.)
  97. Quaternion(float x, float y, float z)
  98. {
  99. FromEulerAngles(x, y, z);
  100. }
  101. /// Construct from the rotation difference between two direction vectors.
  102. Quaternion(const Vector3& start, const Vector3& end)
  103. {
  104. FromRotationTo(start, end);
  105. }
  106. /// Construct from orthonormal axes.
  107. Quaternion(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis)
  108. {
  109. FromAxes(xAxis, yAxis, zAxis);
  110. }
  111. /// Construct from a rotation matrix.
  112. explicit Quaternion(const Matrix3& matrix)
  113. {
  114. FromRotationMatrix(matrix);
  115. }
  116. #ifdef ATOMIC_SSE
  117. explicit Quaternion(__m128 wxyz)
  118. {
  119. _mm_storeu_ps(&w_, wxyz);
  120. }
  121. #endif
  122. /// Assign from another quaternion.
  123. Quaternion& operator =(const Quaternion& rhs)
  124. {
  125. #if defined(ATOMIC_SSE) && (!defined(_MSC_VER) || _MSC_VER >= 1700) /* Visual Studio 2012 and newer. VS2010 has a bug with these, see https://github.com/urho3d/Urho3D/issues/1044 */
  126. _mm_storeu_ps(&w_, _mm_loadu_ps(&rhs.w_));
  127. #else
  128. w_ = rhs.w_;
  129. x_ = rhs.x_;
  130. y_ = rhs.y_;
  131. z_ = rhs.z_;
  132. #endif
  133. return *this;
  134. }
  135. /// Add-assign a quaternion.
  136. Quaternion& operator +=(const Quaternion& rhs)
  137. {
  138. #ifdef ATOMIC_SSE
  139. _mm_storeu_ps(&w_, _mm_add_ps(_mm_loadu_ps(&w_), _mm_loadu_ps(&rhs.w_)));
  140. #else
  141. w_ += rhs.w_;
  142. x_ += rhs.x_;
  143. y_ += rhs.y_;
  144. z_ += rhs.z_;
  145. #endif
  146. return *this;
  147. }
  148. /// Multiply-assign a scalar.
  149. Quaternion& operator *=(float rhs)
  150. {
  151. #ifdef ATOMIC_SSE
  152. _mm_storeu_ps(&w_, _mm_mul_ps(_mm_loadu_ps(&w_), _mm_set1_ps(rhs)));
  153. #else
  154. w_ *= rhs;
  155. x_ *= rhs;
  156. y_ *= rhs;
  157. z_ *= rhs;
  158. #endif
  159. return *this;
  160. }
  161. /// Test for equality with another quaternion without epsilon.
  162. bool operator ==(const Quaternion& rhs) const
  163. {
  164. #ifdef ATOMIC_SSE
  165. __m128 c = _mm_cmpeq_ps(_mm_loadu_ps(&w_), _mm_loadu_ps(&rhs.w_));
  166. c = _mm_and_ps(c, _mm_movehl_ps(c, c));
  167. c = _mm_and_ps(c, _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)));
  168. return _mm_cvtsi128_si32(_mm_castps_si128(c)) == -1;
  169. #else
  170. return w_ == rhs.w_ && x_ == rhs.x_ && y_ == rhs.y_ && z_ == rhs.z_;
  171. #endif
  172. }
  173. /// Test for inequality with another quaternion without epsilon.
  174. bool operator !=(const Quaternion& rhs) const { return !(*this == rhs); }
  175. /// Multiply with a scalar.
  176. Quaternion operator *(float rhs) const
  177. {
  178. #ifdef ATOMIC_SSE
  179. return Quaternion(_mm_mul_ps(_mm_loadu_ps(&w_), _mm_set1_ps(rhs)));
  180. #else
  181. return Quaternion(w_ * rhs, x_ * rhs, y_ * rhs, z_ * rhs);
  182. #endif
  183. }
  184. /// Return negation.
  185. Quaternion operator -() const
  186. {
  187. #ifdef ATOMIC_SSE
  188. return Quaternion(_mm_xor_ps(_mm_loadu_ps(&w_), _mm_castsi128_ps(_mm_set1_epi32((int)0x80000000UL))));
  189. #else
  190. return Quaternion(-w_, -x_, -y_, -z_);
  191. #endif
  192. }
  193. /// Add a quaternion.
  194. Quaternion operator +(const Quaternion& rhs) const
  195. {
  196. #ifdef ATOMIC_SSE
  197. return Quaternion(_mm_add_ps(_mm_loadu_ps(&w_), _mm_loadu_ps(&rhs.w_)));
  198. #else
  199. return Quaternion(w_ + rhs.w_, x_ + rhs.x_, y_ + rhs.y_, z_ + rhs.z_);
  200. #endif
  201. }
  202. /// Subtract a quaternion.
  203. Quaternion operator -(const Quaternion& rhs) const
  204. {
  205. #ifdef ATOMIC_SSE
  206. return Quaternion(_mm_sub_ps(_mm_loadu_ps(&w_), _mm_loadu_ps(&rhs.w_)));
  207. #else
  208. return Quaternion(w_ - rhs.w_, x_ - rhs.x_, y_ - rhs.y_, z_ - rhs.z_);
  209. #endif
  210. }
  211. /// Multiply a quaternion.
  212. Quaternion operator *(const Quaternion& rhs) const
  213. {
  214. #ifdef ATOMIC_SSE
  215. __m128 q1 = _mm_loadu_ps(&w_);
  216. __m128 q2 = _mm_loadu_ps(&rhs.w_);
  217. q2 = _mm_shuffle_ps(q2, q2, _MM_SHUFFLE(0, 3, 2, 1));
  218. const __m128 signy = _mm_castsi128_ps(_mm_set_epi32((int)0x80000000UL, (int)0x80000000UL, 0, 0));
  219. const __m128 signx = _mm_shuffle_ps(signy, signy, _MM_SHUFFLE(2, 0, 2, 0));
  220. const __m128 signz = _mm_shuffle_ps(signy, signy, _MM_SHUFFLE(3, 0, 0, 3));
  221. __m128 out = _mm_mul_ps(_mm_shuffle_ps(q1, q1, _MM_SHUFFLE(1, 1, 1, 1)), _mm_shuffle_ps(q2, q2, _MM_SHUFFLE(0, 1, 2, 3)));
  222. out = _mm_add_ps(_mm_mul_ps(_mm_xor_ps(signy, _mm_shuffle_ps(q1, q1, _MM_SHUFFLE(2, 2, 2, 2))), _mm_shuffle_ps(q2, q2, _MM_SHUFFLE(1, 0, 3, 2))), _mm_xor_ps(signx, out));
  223. out = _mm_add_ps(_mm_mul_ps(_mm_xor_ps(signz, _mm_shuffle_ps(q1, q1, _MM_SHUFFLE(3, 3, 3, 3))), _mm_shuffle_ps(q2, q2, _MM_SHUFFLE(2, 3, 0, 1))), out);
  224. out = _mm_add_ps(_mm_mul_ps(_mm_shuffle_ps(q1, q1, _MM_SHUFFLE(0, 0, 0, 0)), q2), out);
  225. return Quaternion(_mm_shuffle_ps(out, out, _MM_SHUFFLE(2, 1, 0, 3)));
  226. #else
  227. return Quaternion(
  228. w_ * rhs.w_ - x_ * rhs.x_ - y_ * rhs.y_ - z_ * rhs.z_,
  229. w_ * rhs.x_ + x_ * rhs.w_ + y_ * rhs.z_ - z_ * rhs.y_,
  230. w_ * rhs.y_ + y_ * rhs.w_ + z_ * rhs.x_ - x_ * rhs.z_,
  231. w_ * rhs.z_ + z_ * rhs.w_ + x_ * rhs.y_ - y_ * rhs.x_
  232. );
  233. #endif
  234. }
  235. /// Multiply a Vector3.
  236. Vector3 operator *(const Vector3& rhs) const
  237. {
  238. #ifdef ATOMIC_SSE
  239. __m128 q = _mm_loadu_ps(&w_);
  240. q = _mm_shuffle_ps(q, q, _MM_SHUFFLE(0, 3, 2, 1));
  241. __m128 v = _mm_set_ps(0.f, rhs.z_, rhs.y_, rhs.x_);
  242. const __m128 W = _mm_shuffle_ps(q, q, _MM_SHUFFLE(3, 3, 3, 3));
  243. const __m128 a_yzx = _mm_shuffle_ps(q, q, _MM_SHUFFLE(3, 0, 2, 1));
  244. __m128 x = _mm_mul_ps(q, _mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 0, 2, 1)));
  245. __m128 qxv = _mm_sub_ps(x, _mm_mul_ps(a_yzx, v));
  246. __m128 Wv = _mm_mul_ps(W, v);
  247. __m128 s = _mm_add_ps(qxv, _mm_shuffle_ps(Wv, Wv, _MM_SHUFFLE(3, 1, 0, 2)));
  248. __m128 qs = _mm_mul_ps(q, s);
  249. __m128 y = _mm_shuffle_ps(qs, qs, _MM_SHUFFLE(3, 1, 0, 2));
  250. s = _mm_sub_ps(_mm_mul_ps(a_yzx, s), y);
  251. s = _mm_add_ps(s, s);
  252. s = _mm_add_ps(s, v);
  253. return Vector3(
  254. _mm_cvtss_f32(s),
  255. _mm_cvtss_f32(_mm_shuffle_ps(s, s, _MM_SHUFFLE(1, 1, 1, 1))),
  256. _mm_cvtss_f32(_mm_movehl_ps(s, s)));
  257. #else
  258. Vector3 qVec(x_, y_, z_);
  259. Vector3 cross1(qVec.CrossProduct(rhs));
  260. Vector3 cross2(qVec.CrossProduct(cross1));
  261. return rhs + 2.0f * (cross1 * w_ + cross2);
  262. #endif
  263. }
  264. /// Define from an angle (in degrees) and axis.
  265. void FromAngleAxis(float angle, const Vector3& axis);
  266. /// Define from Euler angles (in degrees.)
  267. void FromEulerAngles(float x, float y, float z);
  268. /// Define from the rotation difference between two direction vectors.
  269. void FromRotationTo(const Vector3& start, const Vector3& end);
  270. /// Define from orthonormal axes.
  271. void FromAxes(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis);
  272. /// Define from a rotation matrix.
  273. void FromRotationMatrix(const Matrix3& matrix);
  274. /// Define from a direction to look in and an up direction. Return true if successful, or false if would result in a NaN, in which case the current value remains.
  275. bool FromLookRotation(const Vector3& direction, const Vector3& up = Vector3::UP);
  276. /// Normalize to unit length.
  277. void Normalize()
  278. {
  279. #ifdef ATOMIC_SSE
  280. __m128 q = _mm_loadu_ps(&w_);
  281. __m128 n = _mm_mul_ps(q, q);
  282. n = _mm_add_ps(n, _mm_shuffle_ps(n, n, _MM_SHUFFLE(2, 3, 0, 1)));
  283. n = _mm_add_ps(n, _mm_shuffle_ps(n, n, _MM_SHUFFLE(0, 1, 2, 3)));
  284. __m128 e = _mm_rsqrt_ps(n);
  285. __m128 e3 = _mm_mul_ps(_mm_mul_ps(e, e), e);
  286. __m128 half = _mm_set1_ps(0.5f);
  287. n = _mm_add_ps(e, _mm_mul_ps(half, _mm_sub_ps(e, _mm_mul_ps(n, e3))));
  288. _mm_storeu_ps(&w_, _mm_mul_ps(q, n));
  289. #else
  290. float lenSquared = LengthSquared();
  291. if (!Atomic::Equals(lenSquared, 1.0f) && lenSquared > 0.0f)
  292. {
  293. float invLen = 1.0f / sqrtf(lenSquared);
  294. w_ *= invLen;
  295. x_ *= invLen;
  296. y_ *= invLen;
  297. z_ *= invLen;
  298. }
  299. #endif
  300. }
  301. /// Return normalized to unit length.
  302. Quaternion Normalized() const
  303. {
  304. #ifdef ATOMIC_SSE
  305. __m128 q = _mm_loadu_ps(&w_);
  306. __m128 n = _mm_mul_ps(q, q);
  307. n = _mm_add_ps(n, _mm_shuffle_ps(n, n, _MM_SHUFFLE(2, 3, 0, 1)));
  308. n = _mm_add_ps(n, _mm_shuffle_ps(n, n, _MM_SHUFFLE(0, 1, 2, 3)));
  309. __m128 e = _mm_rsqrt_ps(n);
  310. __m128 e3 = _mm_mul_ps(_mm_mul_ps(e, e), e);
  311. __m128 half = _mm_set1_ps(0.5f);
  312. n = _mm_add_ps(e, _mm_mul_ps(half, _mm_sub_ps(e, _mm_mul_ps(n, e3))));
  313. return Quaternion(_mm_mul_ps(q, n));
  314. #else
  315. float lenSquared = LengthSquared();
  316. if (!Atomic::Equals(lenSquared, 1.0f) && lenSquared > 0.0f)
  317. {
  318. float invLen = 1.0f / sqrtf(lenSquared);
  319. return *this * invLen;
  320. }
  321. else
  322. return *this;
  323. #endif
  324. }
  325. /// Return inverse.
  326. Quaternion Inverse() const
  327. {
  328. #ifdef ATOMIC_SSE
  329. __m128 q = _mm_loadu_ps(&w_);
  330. __m128 n = _mm_mul_ps(q, q);
  331. n = _mm_add_ps(n, _mm_shuffle_ps(n, n, _MM_SHUFFLE(2, 3, 0, 1)));
  332. n = _mm_add_ps(n, _mm_shuffle_ps(n, n, _MM_SHUFFLE(0, 1, 2, 3)));
  333. return Quaternion(_mm_div_ps(_mm_xor_ps(q, _mm_castsi128_ps(_mm_set_epi32((int)0x80000000UL, (int)0x80000000UL, (int)0x80000000UL, 0))), n));
  334. #else
  335. float lenSquared = LengthSquared();
  336. if (lenSquared == 1.0f)
  337. return Conjugate();
  338. else if (lenSquared >= M_EPSILON)
  339. return Conjugate() * (1.0f / lenSquared);
  340. else
  341. return IDENTITY;
  342. #endif
  343. }
  344. /// Return squared length.
  345. float LengthSquared() const
  346. {
  347. #ifdef ATOMIC_SSE
  348. __m128 q = _mm_loadu_ps(&w_);
  349. __m128 n = _mm_mul_ps(q, q);
  350. n = _mm_add_ps(n, _mm_shuffle_ps(n, n, _MM_SHUFFLE(2, 3, 0, 1)));
  351. n = _mm_add_ps(n, _mm_shuffle_ps(n, n, _MM_SHUFFLE(0, 1, 2, 3)));
  352. return _mm_cvtss_f32(n);
  353. #else
  354. return w_ * w_ + x_ * x_ + y_ * y_ + z_ * z_;
  355. #endif
  356. }
  357. /// Calculate dot product.
  358. float DotProduct(const Quaternion& rhs) const
  359. {
  360. #ifdef ATOMIC_SSE
  361. __m128 q1 = _mm_loadu_ps(&w_);
  362. __m128 q2 = _mm_loadu_ps(&rhs.w_);
  363. __m128 n = _mm_mul_ps(q1, q2);
  364. n = _mm_add_ps(n, _mm_shuffle_ps(n, n, _MM_SHUFFLE(2, 3, 0, 1)));
  365. n = _mm_add_ps(n, _mm_shuffle_ps(n, n, _MM_SHUFFLE(0, 1, 2, 3)));
  366. return _mm_cvtss_f32(n);
  367. #else
  368. return w_ * rhs.w_ + x_ * rhs.x_ + y_ * rhs.y_ + z_ * rhs.z_;
  369. #endif
  370. }
  371. /// Test for equality with another quaternion with epsilon.
  372. bool Equals(const Quaternion& rhs) const
  373. {
  374. return Atomic::Equals(w_, rhs.w_) && Atomic::Equals(x_, rhs.x_) && Atomic::Equals(y_, rhs.y_) && Atomic::Equals(z_, rhs.z_);
  375. }
  376. /// Return whether is NaN.
  377. bool IsNaN() const { return Atomic::IsNaN(w_) || Atomic::IsNaN(x_) || Atomic::IsNaN(y_) || Atomic::IsNaN(z_); }
  378. /// Return conjugate.
  379. Quaternion Conjugate() const
  380. {
  381. #ifdef ATOMIC_SSE
  382. __m128 q = _mm_loadu_ps(&w_);
  383. return Quaternion(_mm_xor_ps(q, _mm_castsi128_ps(_mm_set_epi32((int)0x80000000UL, (int)0x80000000UL, (int)0x80000000UL, 0))));
  384. #else
  385. return Quaternion(w_, -x_, -y_, -z_);
  386. #endif
  387. }
  388. /// Return Euler angles in degrees.
  389. Vector3 EulerAngles() const;
  390. /// Return yaw angle in degrees.
  391. float YawAngle() const;
  392. /// Return pitch angle in degrees.
  393. float PitchAngle() const;
  394. /// Return roll angle in degrees.
  395. float RollAngle() const;
  396. /// Return rotation axis.
  397. Vector3 Axis() const;
  398. /// Return rotation angle.
  399. float Angle() const;
  400. /// Return the rotation matrix that corresponds to this quaternion.
  401. Matrix3 RotationMatrix() const;
  402. /// Spherical interpolation with another quaternion.
  403. Quaternion Slerp(const Quaternion& rhs, float t) const;
  404. /// Normalized linear interpolation with another quaternion.
  405. Quaternion Nlerp(const Quaternion& rhs, float t, bool shortestPath = false) const;
  406. /// Return float data.
  407. const float* Data() const { return &w_; }
  408. /// Return as string.
  409. String ToString() const;
  410. /// W coordinate.
  411. float w_;
  412. /// X coordinate.
  413. float x_;
  414. /// Y coordinate.
  415. float y_;
  416. /// Z coordinate.
  417. float z_;
  418. /// Identity quaternion.
  419. static const Quaternion IDENTITY;
  420. };
  421. }