2012-07-31 14:40:21 +10:00
|
|
|
/*
|
2015-04-13 18:05:28 +10:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2012-07-31 14:40:21 +10:00
|
|
|
*
|
2015-04-13 18:05:28 +10:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2012-07-31 14:40:21 +10:00
|
|
|
*
|
|
|
|
* Copyright 2011 Danny Robson <danny@nerdcruft.net>
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2016-08-10 17:42:52 +10:00
|
|
|
#include "./quaternion.hpp"
|
2012-07-31 14:40:21 +10:00
|
|
|
|
2016-08-10 17:42:52 +10:00
|
|
|
#include "./debug.hpp"
|
2016-08-11 14:58:46 +10:00
|
|
|
#include "./vector.hpp"
|
2015-07-13 16:27:35 +10:00
|
|
|
|
2016-10-11 23:47:57 +11:00
|
|
|
#include <cmath>
|
2015-07-13 16:27:35 +10:00
|
|
|
|
2012-07-31 14:40:21 +10:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
using util::quaternion;
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2016-08-10 17:42:52 +10:00
|
|
|
template<> const quaternion<4, float> quaternion<4, float>::IDENTITY = { 1, 0, 0, 0 };
|
|
|
|
template<> const quaternion<4, double> quaternion<4, double>::IDENTITY = { 1, 0, 0, 0 };
|
2012-07-31 14:40:21 +10:00
|
|
|
|
|
|
|
|
2015-07-13 16:27:35 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2016-08-10 17:42:52 +10:00
|
|
|
template <size_t S, typename T>
|
|
|
|
quaternion<S,T>
|
2016-10-12 23:00:47 +11:00
|
|
|
quaternion<S,T>::angle_axis (const T radians, const vector<3,T> axis)
|
2016-08-10 17:42:52 +10:00
|
|
|
{
|
2016-08-11 14:58:46 +10:00
|
|
|
CHECK (is_normalised (axis));
|
2012-07-31 14:40:21 +10:00
|
|
|
|
2016-08-10 17:42:52 +10:00
|
|
|
auto w = std::cos (radians / 2);
|
|
|
|
auto xyz = std::sin (radians / 2) * axis;
|
|
|
|
|
2012-07-31 14:40:21 +10:00
|
|
|
return {
|
2016-08-10 17:42:52 +10:00
|
|
|
w, xyz.x, xyz.y, xyz.z
|
2012-07-31 14:40:21 +10:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-15 20:10:56 +11:00
|
|
|
//-----------------------------------------------------------------------------
|
2016-08-10 17:42:52 +10:00
|
|
|
template <size_t S, typename T>
|
|
|
|
quaternion<S,T>
|
2016-10-12 23:00:47 +11:00
|
|
|
quaternion<S,T>::from_euler (vector<3,T> angles)
|
2016-08-10 17:42:52 +10:00
|
|
|
{
|
2016-10-12 23:00:47 +11:00
|
|
|
auto half = angles / 2;
|
|
|
|
|
|
|
|
auto c = cos (half);
|
|
|
|
auto s = sin (half);
|
2012-07-31 14:40:21 +10:00
|
|
|
|
|
|
|
return {
|
2016-10-12 23:00:47 +11:00
|
|
|
c.x * c.y * c.z - s.x * s.y * s.z,
|
|
|
|
s.x * c.y * c.z + c.x * s.y * s.z,
|
|
|
|
c.x * s.y * c.z - s.x * c.y * s.z,
|
|
|
|
c.x * c.y * s.z + s.x * s.y * c.z,
|
2012-07-31 14:40:21 +10:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-12 23:00:47 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// vector-to-vector rotation algorithm from:
|
|
|
|
// http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
|
|
|
|
template <size_t S, typename T>
|
|
|
|
quaternion<S,T>
|
|
|
|
quaternion<S,T>::from_to (const vector<3,T> u, const vector<3,T> v)
|
|
|
|
{
|
|
|
|
CHECK (is_normalised (u));
|
|
|
|
CHECK (is_normalised (v));
|
|
|
|
|
|
|
|
auto norm_u_norm_v = std::sqrt(dot(u, u) * dot(v, v));
|
|
|
|
auto real_part = norm_u_norm_v + dot(u, v);
|
|
|
|
util::vector<3,T> w;
|
|
|
|
|
|
|
|
if (real_part < 1.e-6f * norm_u_norm_v)
|
|
|
|
{
|
|
|
|
/* If u and v are exactly opposite, rotate 180 degrees
|
|
|
|
* around an arbitrary orthogonal axis. Axis normalisation
|
|
|
|
* can happen later, when we normalise the quaternion. */
|
|
|
|
real_part = 0.0f;
|
|
|
|
w = std::abs(u.x) > std::abs(u.z) ?
|
|
|
|
util::vector3<T> (-u.y, u.x, 0.f) :
|
|
|
|
util::vector3<T> (0.f, -u.z, u.y);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Otherwise, build quaternion the standard way. */
|
|
|
|
w = cross(u, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
return normalised (util::quaternion<4,T> (real_part, w.x, w.y, w.z));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-15 21:33:45 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <typename T>
|
|
|
|
quaternion<4,T>
|
|
|
|
util::conjugate (quaternion<4,T> q)
|
|
|
|
{
|
|
|
|
return { q.w, -q.x, -q.y, -q.z };
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-12 23:00:47 +11:00
|
|
|
//-----------------------------------------------------------------------------
|
2016-09-15 21:33:45 +10:00
|
|
|
template quaternion<4,float> util::conjugate (quaternion<4,float>);
|
|
|
|
|
|
|
|
|
2015-07-13 16:27:35 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2016-08-10 17:42:52 +10:00
|
|
|
template <size_t S, typename T>
|
|
|
|
quaternion<S,T>
|
|
|
|
util::operator* (const quaternion<S,T> a, const quaternion<S,T> b)
|
2015-07-13 16:27:35 +10:00
|
|
|
{
|
|
|
|
return {
|
2016-08-10 17:42:52 +10:00
|
|
|
a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z,
|
|
|
|
a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
|
|
|
|
a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
|
|
|
|
a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,
|
2015-07-13 16:27:35 +10:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-08-10 17:42:52 +10:00
|
|
|
template quaternion<4,float> util::operator* (quaternion<4,float>, quaternion<4,float>);
|
2015-07-13 16:27:35 +10:00
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2016-08-10 17:42:52 +10:00
|
|
|
template <size_t S, typename T>
|
|
|
|
quaternion<S,T>
|
|
|
|
util::operator/ (const quaternion<S,T> a, const quaternion<S,T> b)
|
2015-07-13 16:27:35 +10:00
|
|
|
{
|
2016-08-11 14:58:46 +10:00
|
|
|
CHECK (is_normalised (a));
|
|
|
|
CHECK (is_normalised (b));
|
2015-07-13 16:27:35 +10:00
|
|
|
|
2016-08-10 17:42:52 +10:00
|
|
|
return quaternion<S,T> {
|
|
|
|
a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z,
|
|
|
|
- a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
|
|
|
|
- a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
|
|
|
|
- a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,
|
2015-07-13 16:27:35 +10:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-08-10 17:42:52 +10:00
|
|
|
template quaternion<4,float> util::operator/ (quaternion<4,float>, quaternion<4,float>);
|
2015-07-13 16:27:35 +10:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2016-08-10 17:42:52 +10:00
|
|
|
template <size_t S, typename T>
|
|
|
|
util::matrix4<T>
|
|
|
|
quaternion<S, T>::as_matrix (void) const
|
2015-07-13 16:27:35 +10:00
|
|
|
{
|
2016-08-11 14:58:46 +10:00
|
|
|
CHECK (is_normalised (*this));
|
2015-07-13 16:27:35 +10:00
|
|
|
|
2016-08-10 17:42:52 +10:00
|
|
|
const T wx = this->w * this->x, wy = this->w * this->y, wz = this->w * this->z;
|
|
|
|
const T xx = this->x * this->x, xy = this->x * this->y, xz = this->x * this->z;
|
|
|
|
const T yy = this->y * this->y, yz = this->y * this->z, zz = this->z * this->z;
|
2015-07-13 16:27:35 +10:00
|
|
|
|
|
|
|
return { {
|
2016-09-14 16:48:31 +10:00
|
|
|
{ 1 - 2 * (yy + zz), 2 * (xy - wz), 2 * (xz + wy), 0 },
|
|
|
|
{ 2 * (xy + wz), 1 - 2 * (xx + zz), 2 * (yz - wx), 0 },
|
2016-08-10 17:42:52 +10:00
|
|
|
{ 2 * (xz - wy), 2 * (yz + wx), 1 - 2 * (xx + yy), 0 },
|
2015-07-13 16:27:35 +10:00
|
|
|
{ 0, 0, 0, 1 }
|
|
|
|
} };
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-12 23:00:47 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion
|
|
|
|
template <typename T>
|
|
|
|
util::vector3<T>
|
|
|
|
util::rotate (vector3<T> v, quaternion<4,T> q)
|
|
|
|
{
|
|
|
|
CHECK (is_normalised (v));
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
util::vector3<T> u { q.x, q.y, q.z };
|
|
|
|
return v + 2 * cross (u, cross (u, v) + q.w * v);
|
|
|
|
#else
|
|
|
|
// Verbosely:
|
|
|
|
quaternionf p { 0, v.x, v.y, v.z };
|
|
|
|
auto p_ = q * p * conjugate (q);
|
|
|
|
return { p_.x, p_.y, p_.z };
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template util::vector3f util::rotate (util::vector3f, util::quaternionf);
|
|
|
|
template util::vector3d util::rotate (util::vector3d, util::quaterniond);
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2016-10-17 18:56:10 +11:00
|
|
|
// based on the implementation at:
|
|
|
|
// http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/
|
2016-10-12 23:00:47 +11:00
|
|
|
template <size_t S, typename T>
|
|
|
|
quaternion<S,T>
|
|
|
|
quaternion<S,T>::look (vector<3,T> fwd, vector<3,T> up)
|
|
|
|
{
|
|
|
|
CHECK (is_normalised (fwd));
|
|
|
|
CHECK (is_normalised (up));
|
|
|
|
|
2016-10-17 18:56:10 +11:00
|
|
|
constexpr util::vector3<T> FWD { 0, 0, -1 };
|
|
|
|
constexpr util::vector3<T> UP { 0, 1, 0 };
|
2016-10-12 23:00:47 +11:00
|
|
|
|
2016-10-17 18:56:10 +11:00
|
|
|
// find the rotation from the world fwd to the object fwd
|
|
|
|
auto q1 = from_to (FWD, fwd);
|
2016-10-12 23:00:47 +11:00
|
|
|
|
2016-10-17 18:56:10 +11:00
|
|
|
// orthogonalise the up vector
|
|
|
|
auto right = cross (fwd, up);
|
|
|
|
auto orthup = normalised (cross (right, fwd));
|
2016-10-12 23:00:47 +11:00
|
|
|
|
2016-10-17 18:56:10 +11:00
|
|
|
// recompute the up vector in object space
|
|
|
|
auto newup = rotate (UP, q1);
|
2016-10-12 23:00:47 +11:00
|
|
|
|
2016-10-17 18:56:10 +11:00
|
|
|
// find rotation from object up to world up
|
|
|
|
auto q2 = from_to (newup, orthup);
|
|
|
|
|
|
|
|
return q2 * q1;
|
2016-10-12 23:00:47 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-13 16:27:35 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2016-08-10 17:42:52 +10:00
|
|
|
template <size_t S, typename T>
|
2015-07-13 16:27:35 +10:00
|
|
|
std::ostream&
|
2016-08-10 17:42:52 +10:00
|
|
|
util::operator<< (std::ostream &os, const quaternion<S,T> q)
|
2015-07-13 16:27:35 +10:00
|
|
|
{
|
2016-08-10 17:42:52 +10:00
|
|
|
return os << "[" << q.w << ", " << q.x << ", " << q.y << ", " << q.z << "]";
|
2015-07-13 16:27:35 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-11 15:01:07 +10:00
|
|
|
//-----------------------------------------------------------------------------
|
2016-08-10 17:42:52 +10:00
|
|
|
template std::ostream& util::operator<< (std::ostream&, quaternion<4,float>);
|
|
|
|
template std::ostream& util::operator<< (std::ostream&, quaternion<4,double>);
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace util { namespace debug {
|
|
|
|
template <size_t S, typename T>
|
|
|
|
struct validator<quaternion<S,T>> {
|
|
|
|
static bool is_valid (const quaternion<S,T> &q)
|
|
|
|
{
|
2016-08-11 14:58:46 +10:00
|
|
|
return is_normalised (q);
|
2016-08-10 17:42:52 +10:00
|
|
|
}
|
|
|
|
};
|
|
|
|
} }
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template bool util::debug::is_valid(const quaternion<4,float>&);
|
|
|
|
template bool util::debug::is_valid(const quaternion<4,double>&);
|
2016-08-11 15:01:07 +10:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
template struct util::quaternion<4,float>;
|
|
|
|
template struct util::quaternion<4,double>;
|