Quaternion.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "Quaternion.h"
  25. #include <cstdio>
  26. const Quaternion Quaternion::IDENTITY;
  27. void Quaternion::FromAngleAxis(float angle, const Vector3& axis)
  28. {
  29. Vector3 normAxis = axis.Normalized();
  30. float sinAngle = sinf((angle * M_DEGTORAD) * 0.5f);
  31. float cosAngle = cosf((angle * M_DEGTORAD) * 0.5f);
  32. w_ = cosAngle;
  33. x_ = normAxis.x_ * sinAngle;
  34. y_ = normAxis.y_ * sinAngle;
  35. z_ = normAxis.z_ * sinAngle;
  36. }
  37. void Quaternion::FromEulerAngles(float x, float y, float z)
  38. {
  39. // Order of rotations: Z first, then X, then Y (mimics typical FPS camera with gimbal lock at top/bottom)
  40. float sinX = sinf((x * M_DEGTORAD) * 0.5f);
  41. float cosX = cosf((x * M_DEGTORAD) * 0.5f);
  42. float sinY = sinf((y * M_DEGTORAD) * 0.5f);
  43. float cosY = cosf((y * M_DEGTORAD) * 0.5f);
  44. float sinZ = sinf((z * M_DEGTORAD) * 0.5f);
  45. float cosZ = cosf((z * M_DEGTORAD) * 0.5f);
  46. w_ = cosY * cosX * cosZ + sinY * sinX * sinZ;
  47. x_ = cosY * sinX * cosZ + sinY * cosX * sinZ;
  48. y_ = sinY * cosX * cosZ - cosY * sinX * sinZ;
  49. z_ = cosY * cosX * sinZ - sinY * sinX * cosZ;
  50. }
  51. void Quaternion::FromRotationTo(const Vector3& start, const Vector3& end)
  52. {
  53. Vector3 normStart = start.Normalized();
  54. Vector3 normEnd = end.Normalized();
  55. float d = normStart.DotProduct(normEnd);
  56. if (d > -1.0f + M_EPSILON)
  57. {
  58. Vector3 c = normStart.CrossProduct(normEnd);
  59. float s = sqrtf((1.0f + d) * 2.0f);
  60. float invS = 1.0f / s;
  61. x_ = c.x_ * invS;
  62. y_ = c.y_ * invS;
  63. z_ = c.z_ * invS;
  64. w_ = 0.5f * s;
  65. }
  66. else
  67. {
  68. Vector3 axis = Vector3::RIGHT.CrossProduct(normStart);
  69. if (axis.Length() < M_EPSILON)
  70. axis = Vector3::UP.CrossProduct(normStart);
  71. float angle = 180.0f;
  72. Vector3 normAxis = axis.Normalized();
  73. float sinAngle = sinf((angle * M_DEGTORAD) * 0.5f);
  74. float cosAngle = cosf((angle * M_DEGTORAD) * 0.5f);
  75. w_ = cosAngle;
  76. x_ = normAxis.x_ * sinAngle;
  77. y_ = normAxis.y_ * sinAngle;
  78. z_ = normAxis.z_ * sinAngle;
  79. }
  80. }
  81. void Quaternion::FromAxes(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis)
  82. {
  83. Matrix3 matrix(
  84. xAxis.x_, yAxis.x_, zAxis.x_,
  85. xAxis.y_, yAxis.y_, zAxis.y_,
  86. xAxis.z_, yAxis.z_, zAxis.z_
  87. );
  88. FromRotationMatrix(matrix);
  89. }
  90. void Quaternion::FromRotationMatrix(const Matrix3& matrix)
  91. {
  92. float t = matrix.m00_ + matrix.m11_ + matrix.m22_;
  93. if (t > 0.0f)
  94. {
  95. float s = 0.5f / sqrtf(1.0f + t);
  96. x_ = (matrix.m21_ - matrix.m12_) * s;
  97. y_ = (matrix.m02_ - matrix.m20_) * s;
  98. z_ = (matrix.m10_ - matrix.m01_) * s;
  99. w_ = 0.25f / s;
  100. }
  101. else
  102. {
  103. if (matrix.m00_ > matrix.m11_ && matrix.m00_ > matrix.m22_)
  104. {
  105. float s = sqrtf(1.0f + matrix.m00_ - matrix.m11_ - matrix.m22_) * 2.0f;
  106. float invS = 1.0f / s;
  107. x_ = 0.25f * s;
  108. y_ = (matrix.m01_ + matrix.m10_) * invS;
  109. z_ = (matrix.m20_ + matrix.m02_) * invS;
  110. w_ = (matrix.m21_ - matrix.m12_) * invS;
  111. }
  112. else if (matrix.m11_ > matrix.m22_)
  113. {
  114. float s = sqrtf(1.0f + matrix.m11_ - matrix.m00_ - matrix.m22_) * 2.0f;
  115. float invS = 1.0f / s;
  116. x_ = (matrix.m01_ + matrix.m10_) * invS;
  117. y_ = 0.25f * s;
  118. z_ = (matrix.m12_ + matrix.m21_) * invS;
  119. w_ = (matrix.m02_ - matrix.m20_) * invS;
  120. }
  121. else
  122. {
  123. float s = sqrtf(1.0f + matrix.m22_ - matrix.m00_ - matrix.m11_) * 2.0f;
  124. float invS = 1.0f / s;
  125. x_ = (matrix.m02_ + matrix.m20_) * invS;
  126. y_ = (matrix.m12_ + matrix.m21_) * invS;
  127. z_ = 0.25f * s;
  128. w_ = (matrix.m10_ - matrix.m01_) * invS;
  129. }
  130. }
  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. Matrix3 Quaternion::RotationMatrix() const
  175. {
  176. return Matrix3(
  177. 1.0f - 2.0f * y_ * y_ - 2.0f * z_ * z_,
  178. 2.0f * x_ * y_ - 2.0f * w_ * z_,
  179. 2.0f * x_ * z_ + 2.0f * w_ * y_,
  180. 2.0f * x_ * y_ + 2.0f * w_ * z_,
  181. 1.0f - 2.0f * x_ * x_ - 2.0f * z_ * z_,
  182. 2.0f * y_ * z_ - 2.0f * w_ * x_,
  183. 2.0f * x_ * z_ - 2.0f * w_ * y_,
  184. 2.0f * y_ * z_ + 2.0f * w_ * x_,
  185. 1.0f - 2.0f * x_ * x_ - 2.0f * y_ * y_
  186. );
  187. }
  188. Quaternion Quaternion::Slerp(Quaternion rhs, float t) const
  189. {
  190. float cosAngle = DotProduct(rhs);
  191. // Enable shortest path rotation
  192. if (cosAngle < 0.0f)
  193. {
  194. cosAngle = -cosAngle;
  195. rhs = -rhs;
  196. }
  197. float angle = acosf(cosAngle);
  198. float sinAngle = sinf(angle);
  199. float t1, t2;
  200. if (sinAngle > 0.001f)
  201. {
  202. float invSinAngle = 1.0f / sinAngle;
  203. t1 = sinf((1.0f - t) * angle) * invSinAngle;
  204. t2 = sinf(t * angle) * invSinAngle;
  205. }
  206. else
  207. {
  208. t1 = 1.0f - t;
  209. t2 = t;
  210. }
  211. return *this * t1 + rhs * t2;
  212. }
  213. String Quaternion::ToString() const
  214. {
  215. char tempBuffer[CONVERSION_BUFFER_LENGTH];
  216. sprintf(tempBuffer, "%g %g %g %g", w_, x_, y_, z_);
  217. return String(tempBuffer);
  218. }