BsQuaternion.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Math/BsQuaternion.h"
  4. #include "Math/BsMath.h"
  5. #include "Math/BsMatrix3.h"
  6. #include "Math/BsVector3.h"
  7. namespace bs
  8. {
  9. const float Quaternion::EPSILON = 1e-03f;
  10. const Quaternion Quaternion::ZERO(0.0f, 0.0f, 0.0f, 0.0f);
  11. const Quaternion Quaternion::IDENTITY(1.0f, 0.0f, 0.0f, 0.0f);
  12. const Quaternion::EulerAngleOrderData Quaternion::EA_LOOKUP[6] =
  13. { { 0, 1, 2}, { 0, 2, 1}, { 1, 0, 2},
  14. { 1, 2, 0}, { 2, 0, 1}, { 2, 1, 0} };;
  15. void Quaternion::fromRotationMatrix(const Matrix3& mat)
  16. {
  17. // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
  18. // article "Quaternion Calculus and Fast Animation".
  19. float trace = mat[0][0]+mat[1][1]+mat[2][2];
  20. float root;
  21. if (trace > 0.0f)
  22. {
  23. // |w| > 1/2, may as well choose w > 1/2
  24. root = Math::sqrt(trace + 1.0f); // 2w
  25. w = 0.5f*root;
  26. root = 0.5f/root; // 1/(4w)
  27. x = (mat[2][1]-mat[1][2])*root;
  28. y = (mat[0][2]-mat[2][0])*root;
  29. z = (mat[1][0]-mat[0][1])*root;
  30. }
  31. else
  32. {
  33. // |w| <= 1/2
  34. static UINT32 nextLookup[3] = { 1, 2, 0 };
  35. UINT32 i = 0;
  36. if (mat[1][1] > mat[0][0])
  37. i = 1;
  38. if (mat[2][2] > mat[i][i])
  39. i = 2;
  40. UINT32 j = nextLookup[i];
  41. UINT32 k = nextLookup[j];
  42. root = Math::sqrt(mat[i][i]-mat[j][j]-mat[k][k] + 1.0f);
  43. float* cmpntLookup[3] = { &x, &y, &z };
  44. *cmpntLookup[i] = 0.5f*root;
  45. root = 0.5f/root;
  46. w = (mat[k][j]-mat[j][k])*root;
  47. *cmpntLookup[j] = (mat[j][i]+mat[i][j])*root;
  48. *cmpntLookup[k] = (mat[k][i]+mat[i][k])*root;
  49. }
  50. normalize();
  51. }
  52. void Quaternion::fromAxisAngle(const Vector3& axis, const Radian& angle)
  53. {
  54. Radian halfAngle (0.5f*angle);
  55. float sin = Math::sin(halfAngle);
  56. w = Math::cos(halfAngle);
  57. x = sin*axis.x;
  58. y = sin*axis.y;
  59. z = sin*axis.z;
  60. }
  61. void Quaternion::fromAxes(const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis)
  62. {
  63. Matrix3 kRot;
  64. kRot[0][0] = xaxis.x;
  65. kRot[1][0] = xaxis.y;
  66. kRot[2][0] = xaxis.z;
  67. kRot[0][1] = yaxis.x;
  68. kRot[1][1] = yaxis.y;
  69. kRot[2][1] = yaxis.z;
  70. kRot[0][2] = zaxis.x;
  71. kRot[1][2] = zaxis.y;
  72. kRot[2][2] = zaxis.z;
  73. fromRotationMatrix(kRot);
  74. }
  75. void Quaternion::fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle)
  76. {
  77. Radian halfXAngle = xAngle * 0.5f;
  78. Radian halfYAngle = yAngle * 0.5f;
  79. Radian halfZAngle = zAngle * 0.5f;
  80. float cx = Math::cos(halfXAngle);
  81. float sx = Math::sin(halfXAngle);
  82. float cy = Math::cos(halfYAngle);
  83. float sy = Math::sin(halfYAngle);
  84. float cz = Math::cos(halfZAngle);
  85. float sz = Math::sin(halfZAngle);
  86. Quaternion quatX(cx, sx, 0.0f, 0.0f);
  87. Quaternion quatY(cy, 0.0f, sy, 0.0f);
  88. Quaternion quatZ(cz, 0.0f, 0.0f, sz);
  89. *this = (quatZ * quatX) * quatY;
  90. }
  91. void Quaternion::fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order)
  92. {
  93. const EulerAngleOrderData& l = EA_LOOKUP[(int)order];
  94. Radian halfXAngle = xAngle * 0.5f;
  95. Radian halfYAngle = yAngle * 0.5f;
  96. Radian halfZAngle = zAngle * 0.5f;
  97. float cx = Math::cos(halfXAngle);
  98. float sx = Math::sin(halfXAngle);
  99. float cy = Math::cos(halfYAngle);
  100. float sy = Math::sin(halfYAngle);
  101. float cz = Math::cos(halfZAngle);
  102. float sz = Math::sin(halfZAngle);
  103. Quaternion quats[3];
  104. quats[0] = Quaternion(cx, sx, 0.0f, 0.0f);
  105. quats[1] = Quaternion(cy, 0.0f, sy, 0.0f);
  106. quats[2] = Quaternion(cz, 0.0f, 0.0f, sz);
  107. *this = (quats[l.c] * quats[l.b]) * quats[l.a];
  108. }
  109. void Quaternion::toRotationMatrix(Matrix3& mat) const
  110. {
  111. float tx = x+x;
  112. float ty = y+y;
  113. float fTz = z+z;
  114. float twx = tx*w;
  115. float twy = ty*w;
  116. float twz = fTz*w;
  117. float txx = tx*x;
  118. float txy = ty*x;
  119. float txz = fTz*x;
  120. float tyy = ty*y;
  121. float tyz = fTz*y;
  122. float tzz = fTz*z;
  123. mat[0][0] = 1.0f-(tyy+tzz);
  124. mat[0][1] = txy-twz;
  125. mat[0][2] = txz+twy;
  126. mat[1][0] = txy+twz;
  127. mat[1][1] = 1.0f-(txx+tzz);
  128. mat[1][2] = tyz-twx;
  129. mat[2][0] = txz-twy;
  130. mat[2][1] = tyz+twx;
  131. mat[2][2] = 1.0f-(txx+tyy);
  132. }
  133. void Quaternion::toAxisAngle(Vector3& axis, Radian& angle) const
  134. {
  135. float sqrLength = x*x+y*y+z*z;
  136. if ( sqrLength > 0.0 )
  137. {
  138. angle = 2.0*Math::acos(w);
  139. float invLength = Math::invSqrt(sqrLength);
  140. axis.x = x*invLength;
  141. axis.y = y*invLength;
  142. axis.z = z*invLength;
  143. }
  144. else
  145. {
  146. // Angle is 0 (mod 2*pi), so any axis will do
  147. angle = Radian(0.0);
  148. axis.x = 1.0;
  149. axis.y = 0.0;
  150. axis.z = 0.0;
  151. }
  152. }
  153. void Quaternion::toAxes(Vector3& xaxis, Vector3& yaxis, Vector3& zaxis) const
  154. {
  155. Matrix3 matRot;
  156. toRotationMatrix(matRot);
  157. xaxis.x = matRot[0][0];
  158. xaxis.y = matRot[1][0];
  159. xaxis.z = matRot[2][0];
  160. yaxis.x = matRot[0][1];
  161. yaxis.y = matRot[1][1];
  162. yaxis.z = matRot[2][1];
  163. zaxis.x = matRot[0][2];
  164. zaxis.y = matRot[1][2];
  165. zaxis.z = matRot[2][2];
  166. }
  167. bool Quaternion::toEulerAngles(Radian& xAngle, Radian& yAngle, Radian& zAngle) const
  168. {
  169. Matrix3 matRot;
  170. toRotationMatrix(matRot);
  171. return matRot.toEulerAngles(xAngle, yAngle, zAngle);
  172. }
  173. Vector3 Quaternion::xAxis() const
  174. {
  175. float fTy = 2.0f*y;
  176. float fTz = 2.0f*z;
  177. float fTwy = fTy*w;
  178. float fTwz = fTz*w;
  179. float fTxy = fTy*x;
  180. float fTxz = fTz*x;
  181. float fTyy = fTy*y;
  182. float fTzz = fTz*z;
  183. return Vector3(1.0f-(fTyy+fTzz), fTxy+fTwz, fTxz-fTwy);
  184. }
  185. Vector3 Quaternion::yAxis() const
  186. {
  187. float fTx = 2.0f*x;
  188. float fTy = 2.0f*y;
  189. float fTz = 2.0f*z;
  190. float fTwx = fTx*w;
  191. float fTwz = fTz*w;
  192. float fTxx = fTx*x;
  193. float fTxy = fTy*x;
  194. float fTyz = fTz*y;
  195. float fTzz = fTz*z;
  196. return Vector3(fTxy-fTwz, 1.0f-(fTxx+fTzz), fTyz+fTwx);
  197. }
  198. Vector3 Quaternion::zAxis() const
  199. {
  200. float fTx = 2.0f*x;
  201. float fTy = 2.0f*y;
  202. float fTz = 2.0f*z;
  203. float fTwx = fTx*w;
  204. float fTwy = fTy*w;
  205. float fTxx = fTx*x;
  206. float fTxz = fTz*x;
  207. float fTyy = fTy*y;
  208. float fTyz = fTz*y;
  209. return Vector3(fTxz+fTwy, fTyz-fTwx, 1.0f-(fTxx+fTyy));
  210. }
  211. Quaternion Quaternion::inverse() const
  212. {
  213. float fNorm = w*w+x*x+y*y+z*z;
  214. if (fNorm > 0.0f)
  215. {
  216. float fInvNorm = 1.0f/fNorm;
  217. return Quaternion(w*fInvNorm,-x*fInvNorm,-y*fInvNorm,-z*fInvNorm);
  218. }
  219. else
  220. {
  221. // Return an invalid result to flag the error
  222. return ZERO;
  223. }
  224. }
  225. Vector3 Quaternion::rotate(const Vector3& v) const
  226. {
  227. // Note: Does compiler generate fast code here? Perhaps its better to pull all code locally without constructing
  228. // an intermediate matrix.
  229. Matrix3 rot;
  230. toRotationMatrix(rot);
  231. return rot.transform(v);
  232. }
  233. void Quaternion::lookRotation(const Vector3& forwardDir)
  234. {
  235. if (forwardDir == Vector3::ZERO)
  236. return;
  237. Vector3 nrmForwardDir = Vector3::normalize(forwardDir);
  238. Vector3 currentForwardDir = -zAxis();
  239. if ((nrmForwardDir + currentForwardDir).squaredLength() < 0.00005f)
  240. {
  241. // Oops, a 180 degree turn (infinite possible rotation axes)
  242. // Default to yaw i.e. use current UP
  243. *this = Quaternion(-y, -z, w, x);
  244. }
  245. else
  246. {
  247. // Derive shortest arc to new direction
  248. Quaternion rotQuat = getRotationFromTo(currentForwardDir, nrmForwardDir);
  249. *this = rotQuat * *this;
  250. }
  251. }
  252. void Quaternion::lookRotation(const Vector3& forwardDir, const Vector3& upDir)
  253. {
  254. Vector3 forward = Vector3::normalize(forwardDir);
  255. Vector3 up = Vector3::normalize(upDir);
  256. if (Math::approxEquals(Vector3::dot(forward, up), 1.0f))
  257. {
  258. lookRotation(forward);
  259. return;
  260. }
  261. Vector3 x = Vector3::cross(forward, up);
  262. Vector3 y = Vector3::cross(x, forward);
  263. x.normalize();
  264. y.normalize();
  265. *this = Quaternion(x, y, -forward);
  266. }
  267. Quaternion Quaternion::slerp(float t, const Quaternion& p, const Quaternion& q, bool shortestPath)
  268. {
  269. float cos = p.dot(q);
  270. Quaternion quat;
  271. if (cos < 0.0f && shortestPath)
  272. {
  273. cos = -cos;
  274. quat = -q;
  275. }
  276. else
  277. {
  278. quat = q;
  279. }
  280. if (Math::abs(cos) < 1 - EPSILON)
  281. {
  282. // Standard case (slerp)
  283. float sin = Math::sqrt(1 - Math::sqr(cos));
  284. Radian angle = Math::atan2(sin, cos);
  285. float invSin = 1.0f / sin;
  286. float coeff0 = Math::sin((1.0f - t) * angle) * invSin;
  287. float coeff1 = Math::sin(t * angle) * invSin;
  288. return coeff0 * p + coeff1 * quat;
  289. }
  290. else
  291. {
  292. // There are two situations:
  293. // 1. "p" and "q" are very close (fCos ~= +1), so we can do a linear
  294. // interpolation safely.
  295. // 2. "p" and "q" are almost inverse of each other (fCos ~= -1), there
  296. // are an infinite number of possibilities interpolation. but we haven't
  297. // have method to fix this case, so just use linear interpolation here.
  298. Quaternion ret = (1.0f - t) * p + t * quat;
  299. // Taking the complement requires renormalization
  300. ret.normalize();
  301. return ret;
  302. }
  303. }
  304. Quaternion Quaternion::getRotationFromTo(const Vector3& from, const Vector3& dest, const Vector3& fallbackAxis)
  305. {
  306. // Based on Stan Melax's article in Game Programming Gems
  307. Quaternion q;
  308. Vector3 v0 = from;
  309. Vector3 v1 = dest;
  310. v0.normalize();
  311. v1.normalize();
  312. float d = v0.dot(v1);
  313. // If dot == 1, vectors are the same
  314. if (d >= 1.0f)
  315. return Quaternion::IDENTITY;
  316. if (d < (1e-6f - 1.0f))
  317. {
  318. if (fallbackAxis != Vector3::ZERO)
  319. {
  320. // Rotate 180 degrees about the fallback axis
  321. q.fromAxisAngle(fallbackAxis, Radian(Math::PI));
  322. }
  323. else
  324. {
  325. // Generate an axis
  326. Vector3 axis = Vector3::UNIT_X.cross(from);
  327. if (axis.isZeroLength()) // Pick another if colinear
  328. axis = Vector3::UNIT_Y.cross(from);
  329. axis.normalize();
  330. q.fromAxisAngle(axis, Radian(Math::PI));
  331. }
  332. }
  333. else
  334. {
  335. float s = Math::sqrt( (1+d)*2 );
  336. float invs = 1 / s;
  337. Vector3 c = v0.cross(v1);
  338. q.x = c.x * invs;
  339. q.y = c.y * invs;
  340. q.z = c.z * invs;
  341. q.w = s * 0.5f;
  342. q.normalize();
  343. }
  344. return q;
  345. }
  346. }