Quaternion.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. /**
  2. Copyright 2013 BlackBerry Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. Original file from GamePlay3D: http://gameplay3d.org
  13. This file was modified to fit the cocos2d-x project
  14. */
  15. #include "math/Quaternion.h"
  16. #include <cmath>
  17. #include "base/ccMacros.h"
  18. NS_CC_MATH_BEGIN
  19. /* --- --- --- --- Add by XuJJ start --- --- --- --- */
  20. void QuaternionToMatrix (const Quaternion& q, Mat4& m)
  21. {
  22. // If q is guaranteed to be a unit quaternion, s will always
  23. // be 1. In that case, this calculation can be optimized out.
  24. #if DEBUGMODE
  25. if (!CompareApproximately (SqrMagnitude (q), 1.0F, Vector3f::epsilon))
  26. {
  27. AssertString(Format("Quaternion To Matrix conversion failed because input Quaternion is invalid {%f, %f, %f, %f} l=%f", q.x, q.y, q.z, q.w, SqrMagnitude(q)));
  28. }
  29. #endif
  30. //float norm = GetNorm (q);
  31. //float s = (norm > 0.0) ? 2.0/norm : 0;
  32. // Precalculate coordinate products
  33. float x = q.x * 2.0F;
  34. float y = q.y * 2.0F;
  35. float z = q.z * 2.0F;
  36. float xx = q.x * x;
  37. float yy = q.y * y;
  38. float zz = q.z * z;
  39. float xy = q.x * y;
  40. float xz = q.x * z;
  41. float yz = q.y * z;
  42. float wx = q.w * x;
  43. float wy = q.w * y;
  44. float wz = q.w * z;
  45. // Calculate 3x3 matrix from orthonormal basis
  46. m.m[0] = 1.0f - (yy + zz);
  47. m.m[1] = xy + wz;
  48. m.m[2] = xz - wy;
  49. m.m[3] = 0.0F;
  50. m.m[4] = xy - wz;
  51. m.m[5] = 1.0f - (xx + zz);
  52. m.m[6] = yz + wx;
  53. m.m[7] = 0.0F;
  54. m.m[8] = xz + wy;
  55. m.m[9] = yz - wx;
  56. m.m[10] = 1.0f - (xx + yy);
  57. m.m[11] = 0.0F;
  58. m.m[12] = 0.0F;
  59. m.m[13] = 0.0F;
  60. m.m[14] = 0.0F;
  61. m.m[15] = 1.0F;
  62. }
  63. /* --- --- --- --- Add by XuJJ end --- --- --- --- */
  64. const Quaternion Quaternion::ZERO(0.0f, 0.0f, 0.0f, 0.0f);
  65. Quaternion::Quaternion()
  66. : x(0.0f), y(0.0f), z(0.0f), w(1.0f)
  67. {
  68. }
  69. Quaternion::Quaternion(float xx, float yy, float zz, float ww)
  70. : x(xx), y(yy), z(zz), w(ww)
  71. {
  72. }
  73. Quaternion::Quaternion(float* array)
  74. {
  75. set(array);
  76. }
  77. Quaternion::Quaternion(const Mat4& m)
  78. {
  79. set(m);
  80. }
  81. Quaternion::Quaternion(const Vec3& axis, float angle)
  82. {
  83. set(axis, angle);
  84. }
  85. Quaternion::Quaternion(const Quaternion& copy)
  86. {
  87. set(copy);
  88. }
  89. Quaternion::~Quaternion()
  90. {
  91. }
  92. const Quaternion& Quaternion::identity()
  93. {
  94. static Quaternion value(0.0f, 0.0f, 0.0f, 1.0f);
  95. return value;
  96. }
  97. const Quaternion& Quaternion::zero()
  98. {
  99. static Quaternion value(0.0f, 0.0f, 0.0f, 0.0f);
  100. return value;
  101. }
  102. bool Quaternion::isIdentity() const
  103. {
  104. return x == 0.0f && y == 0.0f && z == 0.0f && w == 1.0f;
  105. }
  106. bool Quaternion::isZero() const
  107. {
  108. return x == 0.0f && y == 0.0f && z == 0.0f && w == 0.0f;
  109. }
  110. void Quaternion::createFromRotationMatrix(const Mat4& m, Quaternion* dst)
  111. {
  112. m.getRotation(dst);
  113. }
  114. void Quaternion::createFromAxisAngle(const Vec3& axis, float angle, Quaternion* dst)
  115. {
  116. GP_ASSERT(dst);
  117. float halfAngle = angle * 0.5f;
  118. float sinHalfAngle = sinf(halfAngle);
  119. Vec3 normal(axis);
  120. normal.normalize();
  121. dst->x = normal.x * sinHalfAngle;
  122. dst->y = normal.y * sinHalfAngle;
  123. dst->z = normal.z * sinHalfAngle;
  124. dst->w = cosf(halfAngle);
  125. }
  126. void Quaternion::conjugate()
  127. {
  128. x = -x;
  129. y = -y;
  130. z = -z;
  131. //w = w;
  132. }
  133. Quaternion Quaternion::getConjugated() const
  134. {
  135. Quaternion q(*this);
  136. q.conjugate();
  137. return q;
  138. }
  139. bool Quaternion::inverse()
  140. {
  141. float n = x * x + y * y + z * z + w * w;
  142. if (n == 1.0f)
  143. {
  144. x = -x;
  145. y = -y;
  146. z = -z;
  147. //w = w;
  148. return true;
  149. }
  150. // Too close to zero.
  151. if (n < 0.000001f)
  152. return false;
  153. n = 1.0f / n;
  154. x = -x * n;
  155. y = -y * n;
  156. z = -z * n;
  157. w = w * n;
  158. return true;
  159. }
  160. Quaternion Quaternion::getInversed() const
  161. {
  162. Quaternion q(*this);
  163. q.inverse();
  164. return q;
  165. }
  166. void Quaternion::multiply(const Quaternion& q)
  167. {
  168. multiply(*this, q, this);
  169. }
  170. void Quaternion::multiply(const Quaternion& q1, const Quaternion& q2, Quaternion* dst)
  171. {
  172. GP_ASSERT(dst);
  173. float x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
  174. float y = q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x;
  175. float z = q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w;
  176. float w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
  177. dst->x = x;
  178. dst->y = y;
  179. dst->z = z;
  180. dst->w = w;
  181. }
  182. void Quaternion::normalize()
  183. {
  184. float n = x * x + y * y + z * z + w * w;
  185. // Already normalized.
  186. if (n == 1.0f)
  187. return;
  188. n = std::sqrt(n);
  189. // Too close to zero.
  190. if (n < 0.000001f)
  191. return;
  192. n = 1.0f / n;
  193. x *= n;
  194. y *= n;
  195. z *= n;
  196. w *= n;
  197. }
  198. Quaternion Quaternion::getNormalized() const
  199. {
  200. Quaternion q(*this);
  201. q.normalize();
  202. return q;
  203. }
  204. void Quaternion::set(float xx, float yy, float zz, float ww)
  205. {
  206. this->x = xx;
  207. this->y = yy;
  208. this->z = zz;
  209. this->w = ww;
  210. }
  211. void Quaternion::set(float* array)
  212. {
  213. GP_ASSERT(array);
  214. x = array[0];
  215. y = array[1];
  216. z = array[2];
  217. w = array[3];
  218. }
  219. void Quaternion::set(const Mat4& m)
  220. {
  221. Quaternion::createFromRotationMatrix(m, this);
  222. }
  223. void Quaternion::set(const Vec3& axis, float angle)
  224. {
  225. Quaternion::createFromAxisAngle(axis, angle, this);
  226. }
  227. void Quaternion::set(const Quaternion& q)
  228. {
  229. this->x = q.x;
  230. this->y = q.y;
  231. this->z = q.z;
  232. this->w = q.w;
  233. }
  234. void Quaternion::setIdentity()
  235. {
  236. x = 0.0f;
  237. y = 0.0f;
  238. z = 0.0f;
  239. w = 1.0f;
  240. }
  241. float Quaternion::toAxisAngle(Vec3* axis) const
  242. {
  243. GP_ASSERT(axis);
  244. Quaternion q(x, y, z, w);
  245. q.normalize();
  246. axis->x = q.x;
  247. axis->y = q.y;
  248. axis->z = q.z;
  249. axis->normalize();
  250. return (2.0f * std::acos(q.w));
  251. }
  252. void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
  253. {
  254. GP_ASSERT(dst);
  255. GP_ASSERT(!(t < 0.0f || t > 1.0f));
  256. if (t == 0.0f)
  257. {
  258. memcpy(dst, &q1, sizeof(float) * 4);
  259. return;
  260. }
  261. else if (t == 1.0f)
  262. {
  263. memcpy(dst, &q2, sizeof(float) * 4);
  264. return;
  265. }
  266. float t1 = 1.0f - t;
  267. dst->x = t1 * q1.x + t * q2.x;
  268. dst->y = t1 * q1.y + t * q2.y;
  269. dst->z = t1 * q1.z + t * q2.z;
  270. dst->w = t1 * q1.w + t * q2.w;
  271. }
  272. void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
  273. {
  274. GP_ASSERT(dst);
  275. slerp(q1.x, q1.y, q1.z, q1.w, q2.x, q2.y, q2.z, q2.w, t, &dst->x, &dst->y, &dst->z, &dst->w);
  276. }
  277. void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
  278. {
  279. GP_ASSERT(!(t < 0.0f || t > 1.0f));
  280. Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
  281. Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
  282. slerpForSquad(q1, q2, t, &dstQ);
  283. slerpForSquad(s1, s2, t, &dstS);
  284. slerpForSquad(dstQ, dstS, 2.0f * t * (1.0f - t), dst);
  285. }
  286. void Quaternion::slerp(float q1x, float q1y, float q1z, float q1w, float q2x, float q2y, float q2z, float q2w, float t, float* dstx, float* dsty, float* dstz, float* dstw)
  287. {
  288. // Fast slerp implementation by kwhatmough:
  289. // It contains no division operations, no trig, no inverse trig
  290. // and no sqrt. Not only does this code tolerate small constraint
  291. // errors in the input quaternions, it actually corrects for them.
  292. GP_ASSERT(dstx && dsty && dstz && dstw);
  293. GP_ASSERT(!(t < 0.0f || t > 1.0f));
  294. if (t == 0.0f)
  295. {
  296. *dstx = q1x;
  297. *dsty = q1y;
  298. *dstz = q1z;
  299. *dstw = q1w;
  300. return;
  301. }
  302. else if (t == 1.0f)
  303. {
  304. *dstx = q2x;
  305. *dsty = q2y;
  306. *dstz = q2z;
  307. *dstw = q2w;
  308. return;
  309. }
  310. if (q1x == q2x && q1y == q2y && q1z == q2z && q1w == q2w)
  311. {
  312. *dstx = q1x;
  313. *dsty = q1y;
  314. *dstz = q1z;
  315. *dstw = q1w;
  316. return;
  317. }
  318. float halfY, alpha, beta;
  319. float u, f1, f2a, f2b;
  320. float ratio1, ratio2;
  321. float halfSecHalfTheta, versHalfTheta;
  322. float sqNotU, sqU;
  323. float cosTheta = q1w * q2w + q1x * q2x + q1y * q2y + q1z * q2z;
  324. // As usual in all slerp implementations, we fold theta.
  325. alpha = cosTheta >= 0 ? 1.0f : -1.0f;
  326. halfY = 1.0f + alpha * cosTheta;
  327. // Here we bisect the interval, so we need to fold t as well.
  328. f2b = t - 0.5f;
  329. u = f2b >= 0 ? f2b : -f2b;
  330. f2a = u - f2b;
  331. f2b += u;
  332. u += u;
  333. f1 = 1.0f - u;
  334. // One iteration of Newton to get 1-cos(theta / 2) to good accuracy.
  335. halfSecHalfTheta = 1.09f - (0.476537f - 0.0903321f * halfY) * halfY;
  336. halfSecHalfTheta *= 1.5f - halfY * halfSecHalfTheta * halfSecHalfTheta;
  337. versHalfTheta = 1.0f - halfY * halfSecHalfTheta;
  338. // Evaluate series expansions of the coefficients.
  339. sqNotU = f1 * f1;
  340. ratio2 = 0.0000440917108f * versHalfTheta;
  341. ratio1 = -0.00158730159f + (sqNotU - 16.0f) * ratio2;
  342. ratio1 = 0.0333333333f + ratio1 * (sqNotU - 9.0f) * versHalfTheta;
  343. ratio1 = -0.333333333f + ratio1 * (sqNotU - 4.0f) * versHalfTheta;
  344. ratio1 = 1.0f + ratio1 * (sqNotU - 1.0f) * versHalfTheta;
  345. sqU = u * u;
  346. ratio2 = -0.00158730159f + (sqU - 16.0f) * ratio2;
  347. ratio2 = 0.0333333333f + ratio2 * (sqU - 9.0f) * versHalfTheta;
  348. ratio2 = -0.333333333f + ratio2 * (sqU - 4.0f) * versHalfTheta;
  349. ratio2 = 1.0f + ratio2 * (sqU - 1.0f) * versHalfTheta;
  350. // Perform the bisection and resolve the folding done earlier.
  351. f1 *= ratio1 * halfSecHalfTheta;
  352. f2a *= ratio2;
  353. f2b *= ratio2;
  354. alpha *= f1 + f2a;
  355. beta = f1 + f2b;
  356. // Apply final coefficients to a and b as usual.
  357. float w = alpha * q1w + beta * q2w;
  358. float x = alpha * q1x + beta * q2x;
  359. float y = alpha * q1y + beta * q2y;
  360. float z = alpha * q1z + beta * q2z;
  361. // This final adjustment to the quaternion's length corrects for
  362. // any small constraint error in the inputs q1 and q2 But as you
  363. // can see, it comes at the cost of 9 additional multiplication
  364. // operations. If this error-correcting feature is not required,
  365. // the following code may be removed.
  366. f1 = 1.5f - 0.5f * (w * w + x * x + y * y + z * z);
  367. *dstw = w * f1;
  368. *dstx = x * f1;
  369. *dsty = y * f1;
  370. *dstz = z * f1;
  371. }
  372. void Quaternion::slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
  373. {
  374. GP_ASSERT(dst);
  375. // cos(omega) = q1 * q2;
  376. // slerp(q1, q2, t) = (q1*sin((1-t)*omega) + q2*sin(t*omega))/sin(omega);
  377. // q1 = +- q2, slerp(q1,q2,t) = q1.
  378. // This is a straight-forward implementation of the formula of slerp. It does not do any sign switching.
  379. float c = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
  380. if (std::abs(c) >= 1.0f)
  381. {
  382. dst->x = q1.x;
  383. dst->y = q1.y;
  384. dst->z = q1.z;
  385. dst->w = q1.w;
  386. return;
  387. }
  388. float omega = std::acos(c);
  389. float s = std::sqrt(1.0f - c * c);
  390. if (std::abs(s) <= 0.00001f)
  391. {
  392. dst->x = q1.x;
  393. dst->y = q1.y;
  394. dst->z = q1.z;
  395. dst->w = q1.w;
  396. return;
  397. }
  398. float r1 = std::sin((1 - t) * omega) / s;
  399. float r2 = std::sin(t * omega) / s;
  400. dst->x = (q1.x * r1 + q2.x * r2);
  401. dst->y = (q1.y * r1 + q2.y * r2);
  402. dst->z = (q1.z * r1 + q2.z * r2);
  403. dst->w = (q1.w * r1 + q2.w * r2);
  404. }
  405. NS_CC_MATH_END