Quaternion.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. //
  2. // Copyright (c) 2008-2014 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 "Quaternion.h"
  24. #include <cstdio>
  25. namespace Urho3D
  26. {
  27. const Quaternion Quaternion::IDENTITY;
  28. void Quaternion::FromAngleAxis(float angle, const Vector3& axis)
  29. {
  30. Vector3 normAxis = axis.Normalized();
  31. angle *= M_DEGTORAD_2;
  32. float sinAngle = sinf(angle);
  33. float cosAngle = cosf(angle);
  34. w_ = cosAngle;
  35. x_ = normAxis.x_ * sinAngle;
  36. y_ = normAxis.y_ * sinAngle;
  37. z_ = normAxis.z_ * sinAngle;
  38. }
  39. void Quaternion::FromEulerAngles(float x, float y, float z)
  40. {
  41. // Order of rotations: Z first, then X, then Y (mimics typical FPS camera with gimbal lock at top/bottom)
  42. x *= M_DEGTORAD_2;
  43. y *= M_DEGTORAD_2;
  44. z *= M_DEGTORAD_2;
  45. float sinX = sinf(x);
  46. float cosX = cosf(x);
  47. float sinY = sinf(y);
  48. float cosY = cosf(y);
  49. float sinZ = sinf(z);
  50. float cosZ = cosf(z);
  51. w_ = cosY * cosX * cosZ + sinY * sinX * sinZ;
  52. x_ = cosY * sinX * cosZ + sinY * cosX * sinZ;
  53. y_ = sinY * cosX * cosZ - cosY * sinX * sinZ;
  54. z_ = cosY * cosX * sinZ - sinY * sinX * cosZ;
  55. }
  56. void Quaternion::FromRotationTo(const Vector3& start, const Vector3& end)
  57. {
  58. Vector3 normStart = start.Normalized();
  59. Vector3 normEnd = end.Normalized();
  60. float d = normStart.DotProduct(normEnd);
  61. if (d > -1.0f + M_EPSILON)
  62. {
  63. Vector3 c = normStart.CrossProduct(normEnd);
  64. float s = sqrtf((1.0f + d) * 2.0f);
  65. float invS = 1.0f / s;
  66. x_ = c.x_ * invS;
  67. y_ = c.y_ * invS;
  68. z_ = c.z_ * invS;
  69. w_ = 0.5f * s;
  70. }
  71. else
  72. {
  73. Vector3 axis = Vector3::RIGHT.CrossProduct(normStart);
  74. if (axis.Length() < M_EPSILON)
  75. axis = Vector3::UP.CrossProduct(normStart);
  76. FromAngleAxis(180.f, axis);
  77. }
  78. }
  79. void Quaternion::FromAxes(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis)
  80. {
  81. Matrix3 matrix(
  82. xAxis.x_, yAxis.x_, zAxis.x_,
  83. xAxis.y_, yAxis.y_, zAxis.y_,
  84. xAxis.z_, yAxis.z_, zAxis.z_
  85. );
  86. FromRotationMatrix(matrix);
  87. }
  88. void Quaternion::FromRotationMatrix(const Matrix3& matrix)
  89. {
  90. float t = matrix.m00_ + matrix.m11_ + matrix.m22_;
  91. if (t > 0.0f)
  92. {
  93. float invS = 0.5f / sqrtf(1.0f + t);
  94. x_ = (matrix.m21_ - matrix.m12_) * invS;
  95. y_ = (matrix.m02_ - matrix.m20_) * invS;
  96. z_ = (matrix.m10_ - matrix.m01_) * invS;
  97. w_ = 0.25f / invS;
  98. }
  99. else
  100. {
  101. if (matrix.m00_ > matrix.m11_ && matrix.m00_ > matrix.m22_)
  102. {
  103. float invS = 0.5f / sqrtf(1.0f + matrix.m00_ - matrix.m11_ - matrix.m22_);
  104. x_ = 0.25f / invS;
  105. y_ = (matrix.m01_ + matrix.m10_) * invS;
  106. z_ = (matrix.m20_ + matrix.m02_) * invS;
  107. w_ = (matrix.m21_ - matrix.m12_) * invS;
  108. }
  109. else if (matrix.m11_ > matrix.m22_)
  110. {
  111. float invS = 0.5f / sqrtf(1.0f + matrix.m11_ - matrix.m00_ - matrix.m22_);
  112. x_ = (matrix.m01_ + matrix.m10_) * invS;
  113. y_ = 0.25f / invS;
  114. z_ = (matrix.m12_ + matrix.m21_) * invS;
  115. w_ = (matrix.m02_ - matrix.m20_) * invS;
  116. }
  117. else
  118. {
  119. float invS = 0.5f / sqrtf(1.0f + matrix.m22_ - matrix.m00_ - matrix.m11_);
  120. x_ = (matrix.m02_ + matrix.m20_) * invS;
  121. y_ = (matrix.m12_ + matrix.m21_) * invS;
  122. z_ = 0.25f / invS;
  123. w_ = (matrix.m10_ - matrix.m01_) * invS;
  124. }
  125. }
  126. }
  127. bool Quaternion::FromLookRotation(const Vector3& direction, const Vector3& upDirection)
  128. {
  129. Vector3 forward = direction.Normalized();
  130. Vector3 v = forward.CrossProduct(upDirection).Normalized();
  131. Vector3 up = v.CrossProduct(forward);
  132. Vector3 right = up.CrossProduct(forward);
  133. Quaternion ret;
  134. ret.w_ = sqrtf(1.0f + right.x_ + up.y_ + forward.z_) * 0.5f;
  135. float w4Recip = 1.0f / (4.0f * ret.w_);
  136. ret.x_ = (up.z_ - forward.y_) * w4Recip;
  137. ret.y_ = (forward.x_ - right.z_) * w4Recip;
  138. ret.z_ = (right.y_ - up.x_) * w4Recip;
  139. if (!ret.IsNaN())
  140. {
  141. (*this) = ret;
  142. return true;
  143. }
  144. else
  145. return false;
  146. }
  147. Vector3 Quaternion::EulerAngles() const
  148. {
  149. // Derivation from http://www.geometrictools.com/Documentation/EulerAngles.pdf
  150. // Order of rotations: Z first, then X, then Y
  151. float check = 2.0f * (-y_ * z_ + w_ * x_);
  152. if (check < -0.995f)
  153. {
  154. return Vector3(
  155. -90.0f,
  156. 0.0f,
  157. -atan2f(2.0f * (x_ * z_ - w_ * y_), 1.0f - 2.0f * (y_ * y_ + z_ * z_)) * M_RADTODEG
  158. );
  159. }
  160. else if (check > 0.995f)
  161. {
  162. return Vector3(
  163. 90.0f,
  164. 0.0f,
  165. atan2f(2.0f * (x_ * z_ - w_ * y_), 1.0f - 2.0f * (y_ * y_ + z_ * z_)) * M_RADTODEG
  166. );
  167. }
  168. else
  169. {
  170. return Vector3(
  171. asinf(check) * M_RADTODEG,
  172. atan2f(2.0f * (x_ * z_ + w_ * y_), 1.0f - 2.0f * (x_ * x_ + y_ * y_)) * M_RADTODEG,
  173. atan2f(2.0f * (x_ * y_ + w_ * z_), 1.0f - 2.0f * (x_ * x_ + z_ * z_)) * M_RADTODEG
  174. );
  175. }
  176. }
  177. float Quaternion::YawAngle() const
  178. {
  179. return EulerAngles().y_;
  180. }
  181. float Quaternion::PitchAngle() const
  182. {
  183. return EulerAngles().x_;
  184. }
  185. float Quaternion::RollAngle() const
  186. {
  187. return EulerAngles().z_;
  188. }
  189. Matrix3 Quaternion::RotationMatrix() const
  190. {
  191. return Matrix3(
  192. 1.0f - 2.0f * y_ * y_ - 2.0f * z_ * z_,
  193. 2.0f * x_ * y_ - 2.0f * w_ * z_,
  194. 2.0f * x_ * z_ + 2.0f * w_ * y_,
  195. 2.0f * x_ * y_ + 2.0f * w_ * z_,
  196. 1.0f - 2.0f * x_ * x_ - 2.0f * z_ * z_,
  197. 2.0f * y_ * z_ - 2.0f * w_ * x_,
  198. 2.0f * x_ * z_ - 2.0f * w_ * y_,
  199. 2.0f * y_ * z_ + 2.0f * w_ * x_,
  200. 1.0f - 2.0f * x_ * x_ - 2.0f * y_ * y_
  201. );
  202. }
  203. Quaternion Quaternion::Slerp(Quaternion rhs, float t) const
  204. {
  205. float cosAngle = DotProduct(rhs);
  206. // Enable shortest path rotation
  207. if (cosAngle < 0.0f)
  208. {
  209. cosAngle = -cosAngle;
  210. rhs = -rhs;
  211. }
  212. float angle = acosf(cosAngle);
  213. float sinAngle = sinf(angle);
  214. float t1, t2;
  215. if (sinAngle > 0.001f)
  216. {
  217. float invSinAngle = 1.0f / sinAngle;
  218. t1 = sinf((1.0f - t) * angle) * invSinAngle;
  219. t2 = sinf(t * angle) * invSinAngle;
  220. }
  221. else
  222. {
  223. t1 = 1.0f - t;
  224. t2 = t;
  225. }
  226. return *this * t1 + rhs * t2;
  227. }
  228. Quaternion Quaternion::Nlerp(Quaternion rhs, float t, bool shortestPath) const
  229. {
  230. Quaternion result;
  231. float fCos = DotProduct(rhs);
  232. if (fCos < 0.0f && shortestPath)
  233. result = (*this) + (((-rhs) - (*this)) * t);
  234. else
  235. result = (*this) + ((rhs - (*this)) * t);
  236. result.Normalize();
  237. return result;
  238. }
  239. String Quaternion::ToString() const
  240. {
  241. char tempBuffer[CONVERSION_BUFFER_LENGTH];
  242. sprintf(tempBuffer, "%g %g %g %g", w_, x_, y_, z_);
  243. return String(tempBuffer);
  244. }
  245. }