mirror of
https://github.com/harfang3d/harfang3d.git
synced 2024-06-01 22:02:42 +00:00
182 lines
4.6 KiB
C++
182 lines
4.6 KiB
C++
// HARFANG(R) Copyright (C) 2022 NWNC. Released under GPL/LGPL/Commercial Licence, see licence.txt for details.
|
|
|
|
#include "foundation/quaternion.h"
|
|
#include "foundation/math.h"
|
|
#include "foundation/matrix3.h"
|
|
#include "foundation/vector3.h"
|
|
#include "foundation/vector4.h"
|
|
|
|
namespace hg {
|
|
|
|
const Quaternion Quaternion::Identity(0, 0, 0, 1);
|
|
|
|
Quaternion Slerp(const Quaternion &a, const Quaternion &b, float t) {
|
|
float cs = Dot(a, b);
|
|
bool bFlip = false;
|
|
|
|
if (cs < 0.F) {
|
|
cs = -cs;
|
|
bFlip = true;
|
|
}
|
|
|
|
float s;
|
|
if (cs > (1.F - 0.000001F)) {
|
|
s = 1.F - t;
|
|
} else {
|
|
const float theta = ACos(cs);
|
|
const float sn = Sin(theta);
|
|
s = Sin((1.F - t) * theta) / sn;
|
|
t = Sin(t * theta) / sn;
|
|
}
|
|
|
|
if (bFlip) {
|
|
t = -t;
|
|
}
|
|
|
|
return Quaternion(s * a.x + t * b.x, s * a.y + t * b.y, s * a.z + t * b.z, s * a.w + t * b.w);
|
|
}
|
|
|
|
//
|
|
float Dist(const Quaternion &a, const Quaternion &b) {
|
|
const float dx = a.x - b.x, dy = a.y - b.y, dz = a.z - b.z, dw = a.w - b.w;
|
|
return Sqrt((dx * dx) + (dy * dy) + (dz * dz) + (dw * dw));
|
|
}
|
|
|
|
Quaternion Conjugate(const Quaternion &q) {
|
|
return Quaternion(-q.x, -q.y, -q.z, q.w);
|
|
}
|
|
|
|
Quaternion Inverse(const Quaternion &q) {
|
|
const float l = Len2(q);
|
|
return l ? Quaternion(q.x / -l, q.y / -l, q.z / -l, q.w / l) : Quaternion::Identity;
|
|
}
|
|
|
|
float Len(const Quaternion &q) { return Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w); }
|
|
float Len2(const Quaternion &q) { return q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w; }
|
|
|
|
Quaternion Normalize(const Quaternion &q) {
|
|
float l = Len(q);
|
|
return l ? Quaternion(q.x / l, q.y / l, q.z / l, q.w / l) : Quaternion::Identity;
|
|
}
|
|
|
|
//
|
|
Quaternion QuaternionLookAt(const Vec3 &at) { return QuaternionFromMatrix3(Mat3LookAt(at)); }
|
|
|
|
//
|
|
static size_t quaternion_inext[3] = {1, 2, 0};
|
|
|
|
Quaternion QuaternionFromMatrix3(const Mat3 &m) {
|
|
// from "Quaternion Calculus and Fast Animation"
|
|
float _x = 0, _y = 0, _z = 0, _w = 1;
|
|
float trace = m.m[0][0] + m.m[1][1] + m.m[2][2];
|
|
|
|
if (trace > 0.0) {
|
|
// |w| > 1/2, may as well choose w > 1/2
|
|
float root = Sqrt(trace + 1.0f); // 2w
|
|
_w = 0.5f * root;
|
|
root = 0.5f / root; // 1/(4w)
|
|
_x = (m.m[2][1] - m.m[1][2]) * root;
|
|
_y = (m.m[0][2] - m.m[2][0]) * root;
|
|
_z = (m.m[1][0] - m.m[0][1]) * root;
|
|
} else {
|
|
// |w| <= 1/2
|
|
size_t i = 0;
|
|
if (m.m[1][1] > m.m[0][0])
|
|
i = 1;
|
|
if (m.m[2][2] > m.m[i][i])
|
|
i = 2;
|
|
size_t j = quaternion_inext[i];
|
|
size_t k = quaternion_inext[j];
|
|
|
|
float root = Sqrt(m.m[i][i] - m.m[j][j] - m.m[k][k] + 1.0f);
|
|
float *quat[3] = {&_x, &_y, &_z};
|
|
*quat[i] = 0.5f * root;
|
|
root = 0.5f / root;
|
|
_w = (m.m[k][j] - m.m[j][k]) * root;
|
|
*quat[j] = (m.m[j][i] + m.m[i][j]) * root;
|
|
*quat[k] = (m.m[k][i] + m.m[i][k]) * root;
|
|
}
|
|
return {_x, _y, _z, _w};
|
|
}
|
|
|
|
//
|
|
Quaternion QuaternionFromAxisAngle(float a, const Vec3 &v) {
|
|
float sn = Sin(a * 0.5f), cs = Cos(a * 0.5f);
|
|
return Normalize(Quaternion(v.x * sn, v.y * sn, v.z * sn, cs));
|
|
}
|
|
|
|
Quaternion QuaternionFromEuler(float x, float y, float z, RotationOrder rorder) {
|
|
Quaternion r,
|
|
qx = QuaternionFromAxisAngle(x, Vec3::Right),
|
|
qy = QuaternionFromAxisAngle(y, Vec3::Up),
|
|
qz = QuaternionFromAxisAngle(z, Vec3::Front);
|
|
|
|
switch (rorder) {
|
|
case RO_ZYX:
|
|
r = qz * qy * qx;
|
|
break;
|
|
case RO_YZX:
|
|
r = qy * qz * qx;
|
|
break;
|
|
case RO_ZXY:
|
|
r = qz * qx * qy;
|
|
break;
|
|
case RO_XZY:
|
|
r = qx * qz * qy;
|
|
break;
|
|
case RO_XYZ:
|
|
r = qx * qy * qz;
|
|
break;
|
|
case RO_XY:
|
|
r = qx * qy;
|
|
break;
|
|
default: // RO_YXZ
|
|
r = qy * qx * qz;
|
|
break;
|
|
}
|
|
return Normalize(r);
|
|
}
|
|
|
|
//
|
|
Mat3 ToMatrix3(const Quaternion &q) {
|
|
float sqw = q.w * q.w, sqx = q.x * q.x, sqy = q.y * q.y, sqz = q.z * q.z;
|
|
|
|
Mat3 m;
|
|
|
|
float invs = 1.f / (sqx + sqy + sqz + sqw);
|
|
m.m[0][0] = (sqx - sqy - sqz + sqw) * invs; // Since sqw + sqx + sqy + sqz = 1 / invs * invs.
|
|
m.m[1][1] = (-sqx + sqy - sqz + sqw) * invs;
|
|
m.m[2][2] = (-sqx - sqy + sqz + sqw) * invs;
|
|
|
|
float tmp1 = q.x * q.y;
|
|
float tmp2 = q.z * q.w;
|
|
m.m[1][0] = 2.f * (tmp1 + tmp2) * invs;
|
|
m.m[0][1] = 2.f * (tmp1 - tmp2) * invs;
|
|
|
|
tmp1 = q.x * q.z;
|
|
tmp2 = q.y * q.w;
|
|
m.m[2][0] = 2.f * (tmp1 - tmp2) * invs;
|
|
m.m[0][2] = 2.f * (tmp1 + tmp2) * invs;
|
|
tmp1 = q.y * q.z;
|
|
tmp2 = q.x * q.w;
|
|
m.m[2][1] = 2.f * (tmp1 + tmp2) * invs;
|
|
m.m[1][2] = 2.f * (tmp1 - tmp2) * invs;
|
|
|
|
return m;
|
|
}
|
|
|
|
Vec3 ToEuler(const Quaternion &q, RotationOrder rorder) { return ToEuler(ToMatrix3(q), rorder); }
|
|
Quaternion QuaternionFromEuler(const Vec3 &v, RotationOrder order) { return QuaternionFromEuler(v.x, v.y, v.z, order); }
|
|
|
|
Vec3 Rotate(const Quaternion &q, const Vec3 &v) {
|
|
Vec3 u(q.x, q.y, q.z);
|
|
const float s = q.w;
|
|
return 2.F * Dot(u, v) * u + (s * s - Dot(u, u)) * v + 2.F * s * Cross(u, v);
|
|
}
|
|
|
|
Vec4 Rotate(const Quaternion &q, const Vec4 &v) {
|
|
return Vec4(Rotate(q, Vec3(v)), v.w);
|
|
}
|
|
|
|
} // namespace hg
|