| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- //
- // Copyright (c) 2008-2013 the Urho3D project.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #include "Precompiled.h"
- #include "Quaternion.h"
- #include <cstdio>
- namespace Urho3D
- {
- const Quaternion Quaternion::IDENTITY;
- void Quaternion::FromAngleAxis(float angle, const Vector3& axis)
- {
- Vector3 normAxis = axis.Normalized();
- angle *= M_DEGTORAD_2;
- float sinAngle = sinf(angle);
- float cosAngle = cosf(angle);
-
- w_ = cosAngle;
- x_ = normAxis.x_ * sinAngle;
- y_ = normAxis.y_ * sinAngle;
- z_ = normAxis.z_ * sinAngle;
- }
- void Quaternion::FromEulerAngles(float x, float y, float z)
- {
- // Order of rotations: Z first, then X, then Y (mimics typical FPS camera with gimbal lock at top/bottom)
- x *= M_DEGTORAD_2;
- y *= M_DEGTORAD_2;
- z *= M_DEGTORAD_2;
- float sinX = sinf(x);
- float cosX = cosf(x);
- float sinY = sinf(y);
- float cosY = cosf(y);
- float sinZ = sinf(z);
- float cosZ = cosf(z);
-
- w_ = cosY * cosX * cosZ + sinY * sinX * sinZ;
- x_ = cosY * sinX * cosZ + sinY * cosX * sinZ;
- y_ = sinY * cosX * cosZ - cosY * sinX * sinZ;
- z_ = cosY * cosX * sinZ - sinY * sinX * cosZ;
- }
- void Quaternion::FromRotationTo(const Vector3& start, const Vector3& end)
- {
- Vector3 normStart = start.Normalized();
- Vector3 normEnd = end.Normalized();
- float d = normStart.DotProduct(normEnd);
-
- if (d > -1.0f + M_EPSILON)
- {
- Vector3 c = normStart.CrossProduct(normEnd);
- float s = sqrtf((1.0f + d) * 2.0f);
- float invS = 1.0f / s;
-
- x_ = c.x_ * invS;
- y_ = c.y_ * invS;
- z_ = c.z_ * invS;
- w_ = 0.5f * s;
- }
- else
- {
- Vector3 axis = Vector3::RIGHT.CrossProduct(normStart);
- if (axis.Length() < M_EPSILON)
- axis = Vector3::UP.CrossProduct(normStart);
-
- FromAngleAxis(180.f, axis);
- }
- }
- void Quaternion::FromAxes(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis)
- {
- Matrix3 matrix(
- xAxis.x_, yAxis.x_, zAxis.x_,
- xAxis.y_, yAxis.y_, zAxis.y_,
- xAxis.z_, yAxis.z_, zAxis.z_
- );
-
- FromRotationMatrix(matrix);
- }
- void Quaternion::FromRotationMatrix(const Matrix3& matrix)
- {
- float t = matrix.m00_ + matrix.m11_ + matrix.m22_;
-
- if (t > 0.0f)
- {
- float invS = 0.5f / sqrtf(1.0f + t);
-
- x_ = (matrix.m21_ - matrix.m12_) * invS;
- y_ = (matrix.m02_ - matrix.m20_) * invS;
- z_ = (matrix.m10_ - matrix.m01_) * invS;
- w_ = 0.25f / invS;
- }
- else
- {
- if (matrix.m00_ > matrix.m11_ && matrix.m00_ > matrix.m22_)
- {
- float invS = 0.5f / sqrtf(1.0f + matrix.m00_ - matrix.m11_ - matrix.m22_);
-
- x_ = 0.25f / invS;
- y_ = (matrix.m01_ + matrix.m10_) * invS;
- z_ = (matrix.m20_ + matrix.m02_) * invS;
- w_ = (matrix.m21_ - matrix.m12_) * invS;
- }
- else if (matrix.m11_ > matrix.m22_)
- {
- float invS = 0.5f / sqrtf(1.0f + matrix.m11_ - matrix.m00_ - matrix.m22_);
-
- x_ = (matrix.m01_ + matrix.m10_) * invS;
- y_ = 0.25f / invS;
- z_ = (matrix.m12_ + matrix.m21_) * invS;
- w_ = (matrix.m02_ - matrix.m20_) * invS;
- }
- else
- {
- float invS = 0.5f / sqrtf(1.0f + matrix.m22_ - matrix.m00_ - matrix.m11_);
-
- x_ = (matrix.m02_ + matrix.m20_) * invS;
- y_ = (matrix.m12_ + matrix.m21_) * invS;
- z_ = 0.25f / invS;
- w_ = (matrix.m10_ - matrix.m01_) * invS;
- }
- }
- }
- void Quaternion::FromLookRotation(const Vector3& direction, const Vector3& upDirection)
- {
- Vector3 forward = direction.Normalized();
- Vector3 v = forward.CrossProduct(upDirection).Normalized();
- Vector3 up = v.CrossProduct(forward);
- Vector3 right = up.CrossProduct(forward);
- Quaternion ret;
- ret.w_ = sqrtf(1.0f + right.x_ + up.y_ + forward.z_) * 0.5f;
- float w4Recip = 1.0f / (4.0f * ret.w_);
- ret.x_ = (up.z_ - forward.y_) * w4Recip;
- ret.y_ = (forward.x_ - right.z_) * w4Recip;
- ret.z_ = (right.y_ - up.x_) * w4Recip;
- (*this) = ret;
- }
- Vector3 Quaternion::EulerAngles() const
- {
- // Derivation from http://www.geometrictools.com/Documentation/EulerAngles.pdf
- // Order of rotations: Z first, then X, then Y
- float check = 2.0f * (-y_ * z_ + w_ * x_);
-
- if (check < -0.995f)
- {
- return Vector3(
- -90.0f,
- 0.0f,
- -atan2f(2.0f * (x_ * z_ - w_ * y_), 1.0f - 2.0f * (y_ * y_ + z_ * z_)) * M_RADTODEG
- );
- }
- else if (check > 0.995f)
- {
- return Vector3(
- 90.0f,
- 0.0f,
- atan2f(2.0f * (x_ * z_ - w_ * y_), 1.0f - 2.0f * (y_ * y_ + z_ * z_)) * M_RADTODEG
- );
- }
- else
- {
- return Vector3(
- asinf(check) * M_RADTODEG,
- atan2f(2.0f * (x_ * z_ + w_ * y_), 1.0f - 2.0f * (x_ * x_ + y_ * y_)) * M_RADTODEG,
- atan2f(2.0f * (x_ * y_ + w_ * z_), 1.0f - 2.0f * (x_ * x_ + z_ * z_)) * M_RADTODEG
- );
- }
- }
- float Quaternion::YawAngle() const
- {
- return EulerAngles().y_;
- }
- float Quaternion::PitchAngle() const
- {
- return EulerAngles().x_;
- }
- float Quaternion::RollAngle() const
- {
- return EulerAngles().z_;
- }
- Matrix3 Quaternion::RotationMatrix() const
- {
- return Matrix3(
- 1.0f - 2.0f * y_ * y_ - 2.0f * z_ * z_,
- 2.0f * x_ * y_ - 2.0f * w_ * z_,
- 2.0f * x_ * z_ + 2.0f * w_ * y_,
- 2.0f * x_ * y_ + 2.0f * w_ * z_,
- 1.0f - 2.0f * x_ * x_ - 2.0f * z_ * z_,
- 2.0f * y_ * z_ - 2.0f * w_ * x_,
- 2.0f * x_ * z_ - 2.0f * w_ * y_,
- 2.0f * y_ * z_ + 2.0f * w_ * x_,
- 1.0f - 2.0f * x_ * x_ - 2.0f * y_ * y_
- );
- }
- Quaternion Quaternion::Slerp(Quaternion rhs, float t) const
- {
- float cosAngle = DotProduct(rhs);
- // Enable shortest path rotation
- if (cosAngle < 0.0f)
- {
- cosAngle = -cosAngle;
- rhs = -rhs;
- }
-
- float angle = acosf(cosAngle);
- float sinAngle = sinf(angle);
- float t1, t2;
-
- if (sinAngle > 0.001f)
- {
- float invSinAngle = 1.0f / sinAngle;
- t1 = sinf((1.0f - t) * angle) * invSinAngle;
- t2 = sinf(t * angle) * invSinAngle;
- }
- else
- {
- t1 = 1.0f - t;
- t2 = t;
- }
-
- return *this * t1 + rhs * t2;
- }
- Quaternion Quaternion::Nlerp(Quaternion rhs, float t, bool shortestPath) const
- {
- Quaternion result;
- float fCos = DotProduct(rhs);
- if (fCos < 0.0f && shortestPath)
- result = (*this) + (((-rhs) - (*this)) * t);
- else
- result = (*this) + ((rhs - (*this)) * t);
- result.Normalize();
- return result;
- }
- String Quaternion::ToString() const
- {
- char tempBuffer[CONVERSION_BUFFER_LENGTH];
- sprintf(tempBuffer, "%g %g %g %g", w_, x_, y_, z_);
- return String(tempBuffer);
- }
- }
|