Quaternion.cpp 8.3 KB

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