Quaternion.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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. #include "../Precompiled.h"
  23. #include "../Math/Quaternion.h"
  24. #include <cstdio>
  25. #include "../DebugNew.h"
  26. namespace Atomic
  27. {
  28. const Quaternion Quaternion::IDENTITY;
  29. void Quaternion::FromAngleAxis(float angle, const Vector3& axis)
  30. {
  31. Vector3 normAxis = axis.Normalized();
  32. angle *= M_DEGTORAD_2;
  33. float sinAngle = sinf(angle);
  34. float cosAngle = cosf(angle);
  35. w_ = cosAngle;
  36. x_ = normAxis.x_ * sinAngle;
  37. y_ = normAxis.y_ * sinAngle;
  38. z_ = normAxis.z_ * sinAngle;
  39. }
  40. void Quaternion::FromEulerAngles(float x, float y, float z)
  41. {
  42. // Order of rotations: Z first, then X, then Y (mimics typical FPS camera with gimbal lock at top/bottom)
  43. x *= M_DEGTORAD_2;
  44. y *= M_DEGTORAD_2;
  45. z *= M_DEGTORAD_2;
  46. float sinX = sinf(x);
  47. float cosX = cosf(x);
  48. float sinY = sinf(y);
  49. float cosY = cosf(y);
  50. float sinZ = sinf(z);
  51. float cosZ = cosf(z);
  52. w_ = cosY * cosX * cosZ + sinY * sinX * sinZ;
  53. x_ = cosY * sinX * cosZ + sinY * cosX * sinZ;
  54. y_ = sinY * cosX * cosZ - cosY * sinX * sinZ;
  55. z_ = cosY * cosX * sinZ - sinY * sinX * cosZ;
  56. }
  57. void Quaternion::FromRotationTo(const Vector3& start, const Vector3& end)
  58. {
  59. Vector3 normStart = start.Normalized();
  60. Vector3 normEnd = end.Normalized();
  61. float d = normStart.DotProduct(normEnd);
  62. if (d > -1.0f + M_EPSILON)
  63. {
  64. Vector3 c = normStart.CrossProduct(normEnd);
  65. float s = sqrtf((1.0f + d) * 2.0f);
  66. float invS = 1.0f / s;
  67. x_ = c.x_ * invS;
  68. y_ = c.y_ * invS;
  69. z_ = c.z_ * invS;
  70. w_ = 0.5f * s;
  71. }
  72. else
  73. {
  74. Vector3 axis = Vector3::RIGHT.CrossProduct(normStart);
  75. if (axis.Length() < M_EPSILON)
  76. axis = Vector3::UP.CrossProduct(normStart);
  77. FromAngleAxis(180.f, axis);
  78. }
  79. }
  80. void Quaternion::FromAxes(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis)
  81. {
  82. Matrix3 matrix(
  83. xAxis.x_, yAxis.x_, zAxis.x_,
  84. xAxis.y_, yAxis.y_, zAxis.y_,
  85. xAxis.z_, yAxis.z_, zAxis.z_
  86. );
  87. FromRotationMatrix(matrix);
  88. }
  89. void Quaternion::FromRotationMatrix(const Matrix3& matrix)
  90. {
  91. float t = matrix.m00_ + matrix.m11_ + matrix.m22_;
  92. if (t > 0.0f)
  93. {
  94. float invS = 0.5f / sqrtf(1.0f + t);
  95. x_ = (matrix.m21_ - matrix.m12_) * invS;
  96. y_ = (matrix.m02_ - matrix.m20_) * invS;
  97. z_ = (matrix.m10_ - matrix.m01_) * invS;
  98. w_ = 0.25f / invS;
  99. }
  100. else
  101. {
  102. if (matrix.m00_ > matrix.m11_ && matrix.m00_ > matrix.m22_)
  103. {
  104. float invS = 0.5f / sqrtf(1.0f + matrix.m00_ - matrix.m11_ - matrix.m22_);
  105. x_ = 0.25f / invS;
  106. y_ = (matrix.m01_ + matrix.m10_) * invS;
  107. z_ = (matrix.m20_ + matrix.m02_) * invS;
  108. w_ = (matrix.m21_ - matrix.m12_) * invS;
  109. }
  110. else if (matrix.m11_ > matrix.m22_)
  111. {
  112. float invS = 0.5f / sqrtf(1.0f + matrix.m11_ - matrix.m00_ - matrix.m22_);
  113. x_ = (matrix.m01_ + matrix.m10_) * invS;
  114. y_ = 0.25f / invS;
  115. z_ = (matrix.m12_ + matrix.m21_) * invS;
  116. w_ = (matrix.m02_ - matrix.m20_) * invS;
  117. }
  118. else
  119. {
  120. float invS = 0.5f / sqrtf(1.0f + matrix.m22_ - matrix.m00_ - matrix.m11_);
  121. x_ = (matrix.m02_ + matrix.m20_) * invS;
  122. y_ = (matrix.m12_ + matrix.m21_) * invS;
  123. z_ = 0.25f / invS;
  124. w_ = (matrix.m10_ - matrix.m01_) * invS;
  125. }
  126. }
  127. }
  128. bool Quaternion::FromLookRotation(const Vector3& direction, const Vector3& upDirection)
  129. {
  130. Quaternion ret;
  131. Vector3 forward = direction.Normalized();
  132. Vector3 v = forward.CrossProduct(upDirection);
  133. // If direction & upDirection are parallel and crossproduct becomes zero, use FromRotationTo() fallback
  134. if (v.LengthSquared() >= M_EPSILON)
  135. {
  136. v.Normalize();
  137. Vector3 up = v.CrossProduct(forward);
  138. Vector3 right = up.CrossProduct(forward);
  139. ret.FromAxes(right, up, forward);
  140. }
  141. else
  142. ret.FromRotationTo(Vector3::FORWARD, forward);
  143. if (!ret.IsNaN())
  144. {
  145. (*this) = ret;
  146. return true;
  147. }
  148. else
  149. return false;
  150. }
  151. Vector3 Quaternion::EulerAngles() const
  152. {
  153. // Derivation from http://www.geometrictools.com/Documentation/EulerAngles.pdf
  154. // Order of rotations: Z first, then X, then Y
  155. float check = 2.0f * (-y_ * z_ + w_ * x_);
  156. if (check < -0.995f)
  157. {
  158. return Vector3(
  159. -90.0f,
  160. 0.0f,
  161. -atan2f(2.0f * (x_ * z_ - w_ * y_), 1.0f - 2.0f * (y_ * y_ + z_ * z_)) * M_RADTODEG
  162. );
  163. }
  164. else if (check > 0.995f)
  165. {
  166. return Vector3(
  167. 90.0f,
  168. 0.0f,
  169. atan2f(2.0f * (x_ * z_ - w_ * y_), 1.0f - 2.0f * (y_ * y_ + z_ * z_)) * M_RADTODEG
  170. );
  171. }
  172. else
  173. {
  174. return Vector3(
  175. asinf(check) * M_RADTODEG,
  176. atan2f(2.0f * (x_ * z_ + w_ * y_), 1.0f - 2.0f * (x_ * x_ + y_ * y_)) * M_RADTODEG,
  177. atan2f(2.0f * (x_ * y_ + w_ * z_), 1.0f - 2.0f * (x_ * x_ + z_ * z_)) * M_RADTODEG
  178. );
  179. }
  180. }
  181. float Quaternion::YawAngle() const
  182. {
  183. return EulerAngles().y_;
  184. }
  185. float Quaternion::PitchAngle() const
  186. {
  187. return EulerAngles().x_;
  188. }
  189. float Quaternion::RollAngle() const
  190. {
  191. return EulerAngles().z_;
  192. }
  193. Vector3 Quaternion::Axis() const
  194. {
  195. return Vector3(x_, y_, z_) / sqrt(1 - w_ * w_);
  196. }
  197. float Quaternion::Angle() const
  198. {
  199. return 2 * Acos(w_);
  200. }
  201. Matrix3 Quaternion::RotationMatrix() const
  202. {
  203. return Matrix3(
  204. 1.0f - 2.0f * y_ * y_ - 2.0f * z_ * z_,
  205. 2.0f * x_ * y_ - 2.0f * w_ * z_,
  206. 2.0f * x_ * z_ + 2.0f * w_ * y_,
  207. 2.0f * x_ * y_ + 2.0f * w_ * z_,
  208. 1.0f - 2.0f * x_ * x_ - 2.0f * z_ * z_,
  209. 2.0f * y_ * z_ - 2.0f * w_ * x_,
  210. 2.0f * x_ * z_ - 2.0f * w_ * y_,
  211. 2.0f * y_ * z_ + 2.0f * w_ * x_,
  212. 1.0f - 2.0f * x_ * x_ - 2.0f * y_ * y_
  213. );
  214. }
  215. Quaternion Quaternion::Slerp(const Quaternion& rhs, float t) const
  216. {
  217. // Use fast approximation for Emscripten builds
  218. #ifdef __EMSCRIPTEN__
  219. float angle = DotProduct(rhs);
  220. float sign = 1.f; // Multiply by a sign of +/-1 to guarantee we rotate the shorter arc.
  221. if (angle < 0.f)
  222. {
  223. angle = -angle;
  224. sign = -1.f;
  225. }
  226. float a;
  227. float b;
  228. if (angle < 0.999f) // perform spherical linear interpolation.
  229. {
  230. // angle = acos(angle); // After this, angle is in the range pi/2 -> 0 as the original angle variable ranged from 0 -> 1.
  231. angle = (-0.69813170079773212f * angle * angle - 0.87266462599716477f) * angle + 1.5707963267948966f;
  232. float ta = t*angle;
  233. // Manually compute the two sines by using a very rough approximation.
  234. float ta2 = ta*ta;
  235. b = ((5.64311797634681035370e-03f * ta2 - 1.55271410633428644799e-01f) * ta2 + 9.87862135574673806965e-01f) * ta;
  236. a = angle - ta;
  237. float a2 = a*a;
  238. a = ((5.64311797634681035370e-03f * a2 - 1.55271410633428644799e-01f) * a2 + 9.87862135574673806965e-01f) * a;
  239. }
  240. else // If angle is close to taking the denominator to zero, resort to linear interpolation (and normalization).
  241. {
  242. a = 1.f - t;
  243. b = t;
  244. }
  245. // Lerp and renormalize.
  246. return (*this * (a * sign) + rhs * b).Normalized();
  247. #else
  248. // Favor accuracy for native code builds
  249. float cosAngle = DotProduct(rhs);
  250. float sign = 1.0f;
  251. // Enable shortest path rotation
  252. if (cosAngle < 0.0f)
  253. {
  254. cosAngle = -cosAngle;
  255. sign = -1.0f;
  256. }
  257. float angle = acosf(cosAngle);
  258. float sinAngle = sinf(angle);
  259. float t1, t2;
  260. if (sinAngle > 0.001f)
  261. {
  262. float invSinAngle = 1.0f / sinAngle;
  263. t1 = sinf((1.0f - t) * angle) * invSinAngle;
  264. t2 = sinf(t * angle) * invSinAngle;
  265. }
  266. else
  267. {
  268. t1 = 1.0f - t;
  269. t2 = t;
  270. }
  271. return *this * t1 + (rhs * sign) * t2;
  272. #endif
  273. }
  274. Quaternion Quaternion::Nlerp(const Quaternion& rhs, float t, bool shortestPath) const
  275. {
  276. Quaternion result;
  277. float fCos = DotProduct(rhs);
  278. if (fCos < 0.0f && shortestPath)
  279. result = (*this) + (((-rhs) - (*this)) * t);
  280. else
  281. result = (*this) + ((rhs - (*this)) * t);
  282. result.Normalize();
  283. return result;
  284. }
  285. String Quaternion::ToString() const
  286. {
  287. char tempBuffer[CONVERSION_BUFFER_LENGTH];
  288. sprintf(tempBuffer, "%g %g %g %g", w_, x_, y_, z_);
  289. return String(tempBuffer);
  290. }
  291. }