qua: matrix to quaternion by qrotate and look_at functions

This commit is contained in:
BlackMATov
2021-01-28 07:12:08 +07:00
parent ddac471c19
commit cae39ff1fb
2 changed files with 91 additions and 0 deletions

View File

@@ -761,6 +761,62 @@ namespace vmath_hpp
{ {
// qrotate // qrotate
template < typename T >
[[nodiscard]] qua<T> qrotate(const mat<T, 3>& m) {
/// REFERENCE:
/// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
const T wv = m[0][0] + m[1][1] + m[2][2];
const T xv = m[0][0] - m[1][1] - m[2][2];
const T yv = m[1][1] - m[0][0] - m[2][2];
const T zv = m[2][2] - m[0][0] - m[1][1];
struct pmv {
const T v{};
const int i{};
};
const auto [mv,mi] = max<pmv>({
{wv, 0},
{xv, 1},
{yv, 2},
{zv, 3},
}, [](auto&& l, auto&& r){
return l.v < r.v;
});
const T qv = T(0.5) * sqrt(T(1) + mv);
const T rqv = T(0.25) * rcp(qv);
switch ( mi ) {
default:
case 0:
return {
(m[1][2] - m[2][1]) * rqv,
(m[2][0] - m[0][2]) * rqv,
(m[0][1] - m[1][0]) * rqv,
qv};
case 1:
return {
qv,
(m[1][0] + m[0][1]) * rqv,
(m[2][0] + m[0][2]) * rqv,
(m[1][2] - m[2][1]) * rqv};
case 2:
return {
(m[1][0] + m[0][1]) * rqv,
qv,
(m[2][1] + m[1][2]) * rqv,
(m[2][0] - m[0][2]) * rqv};
case 3:
return {
(m[2][0] + m[0][2]) * rqv,
(m[2][1] + m[1][2]) * rqv,
qv,
(m[0][1] - m[1][0]) * rqv};
}
}
template < typename T > template < typename T >
[[nodiscard]] qua<T> qrotate(T angle, const vec<T, 3>& axis) { [[nodiscard]] qua<T> qrotate(T angle, const vec<T, 3>& axis) {
/// REFERENCE: /// REFERENCE:
@@ -808,4 +864,30 @@ namespace vmath_hpp
[[nodiscard]] qua<T> qrotate_z(const qua<T>& q, T angle) { [[nodiscard]] qua<T> qrotate_z(const qua<T>& q, T angle) {
return qrotate(q, angle, unit3_z<T>); return qrotate(q, angle, unit3_z<T>);
} }
// look_at
template < typename T >
[[nodiscard]] qua<T> qlook_at_lh(const vec<T, 3>& dir, const vec<T, 3>& up) {
const vec az = normalize(dir);
const vec ax = normalize(cross(up, az));
const vec ay = cross(az, ax);
return qrotate(mat{
ax.x, ay.x, az.x,
ax.y, ay.y, az.y,
ax.z, ay.z, az.z});
}
template < typename T >
[[nodiscard]] qua<T> qlook_at_rh(const vec<T, 3>& dir, const vec<T, 3>& up) {
const vec az = normalize(-dir);
const vec ax = normalize(cross(up, az));
const vec ay = cross(az, ax);
return qrotate(mat{
ax.x, ay.x, az.x,
ax.y, ay.y, az.y,
ax.z, ay.z, az.z});
}
} }

View File

@@ -337,4 +337,13 @@ TEST_CASE("vmath/ext") {
REQUIRE(qrotate_y(12.3f) == qrotate(12.3f, unit3_y<float> * 2.f)); REQUIRE(qrotate_y(12.3f) == qrotate(12.3f, unit3_y<float> * 2.f));
REQUIRE(qrotate_z(12.3f) == qrotate(12.3f, unit3_z<float> * 2.f)); REQUIRE(qrotate_z(12.3f) == qrotate(12.3f, unit3_z<float> * 2.f));
} }
SECTION("quaternion look_at") {
REQUIRE(all(approx(
qlook_at_lh(float3(1.f,2.f,3.f), float3(0,1,0)),
qrotate(float3x3(look_at_lh(float3(), float3(1.f,2.f,3.f), float3(0,1,0)))))));
REQUIRE(all(approx(
qlook_at_rh(float3(1.f,2.f,3.f), float3(0,1,0)),
qrotate(float3x3(look_at_rh(float3(), float3(1.f,2.f,3.f), float3(0,1,0)))))));
}
} }