aboutsummaryrefslogtreecommitdiff
path: root/include/math/quat.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/math/quat.h')
-rw-r--r--include/math/quat.h87
1 files changed, 85 insertions, 2 deletions
diff --git a/include/math/quat.h b/include/math/quat.h
index b284567..a154044 100644
--- a/include/math/quat.h
+++ b/include/math/quat.h
@@ -4,23 +4,38 @@
4#include "mat4.h" 4#include "mat4.h"
5#include "vec3.h" 5#include "vec3.h"
6 6
7#include <assert.h>
8
7/// A quaternion. 9/// A quaternion.
8typedef struct quat { 10typedef struct quat {
9 R w, x, y, z; 11 R x, y, z, w;
10} quat; 12} quat;
11 13
14/// Return the unit/identity quaternion.
12static inline quat qunit() { 15static inline quat qunit() {
13 return (quat){.x = 0.0, .y = 0.0, .z = 0.0, .w = 0.0}; 16 return (quat){.x = 0.0, .y = 0.0, .z = 0.0, .w = 1.0};
14} 17}
15 18
19/// Construct a quaternion.
16static inline quat qmake(R x, R y, R z, R w) { 20static inline quat qmake(R x, R y, R z, R w) {
17 return (quat){.x = x, .y = y, .z = z, .w = w}; 21 return (quat){.x = x, .y = y, .z = z, .w = w};
18} 22}
19 23
24/// Construct a quaternion from an array.
20static inline quat quat_from_array(const R xyzw[4]) { 25static inline quat quat_from_array(const R xyzw[4]) {
21 return (quat){.x = xyzw[0], .y = xyzw[1], .z = xyzw[2], .w = xyzw[3]}; 26 return (quat){.x = xyzw[0], .y = xyzw[1], .z = xyzw[2], .w = xyzw[3]};
22} 27}
23 28
29/// Construct a quaternion from a 3D point.
30static inline quat quat_from_vec3(vec3 p) {
31 return (quat){.x = p.x, .y = p.y, .z = p.z, .w = 0.0};
32}
33
34/// Get a 3D point back from a quaternion.
35static inline vec3 vec3_from_quat(quat q) {
36 return (vec3){.x = q.x, .y = q.y, .z = q.z};
37}
38
24/// Construct a rotation quaternion. 39/// Construct a rotation quaternion.
25static inline quat qmake_rot(R angle, R x, R y, R z) { 40static inline quat qmake_rot(R angle, R x, R y, R z) {
26 const R a = angle * 0.5; 41 const R a = angle * 0.5;
@@ -34,6 +49,11 @@ static inline quat qmake_rot(R angle, R x, R y, R z) {
34 return (quat){x / mag, y / mag, z / mag, w}; 49 return (quat){x / mag, y / mag, z / mag, w};
35} 50}
36 51
52/// Add two quaternions.
53static inline quat qadd(quat a, quat b) {
54 return (quat){.x = a.x + b.x, .y = a.y + b.y, .z = a.z + b.z, .w = a.w + b.w};
55}
56
37/// Multiply two quaternions. 57/// Multiply two quaternions.
38static inline quat qmul(quat q1, quat q2) { 58static inline quat qmul(quat q1, quat q2) {
39 const R x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y; 59 const R x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
@@ -61,6 +81,37 @@ static inline vec3 qrot(quat q, vec3 v) {
61 return vec3_make(u.x, u.y, u.z); 81 return vec3_make(u.x, u.y, u.z);
62} 82}
63 83
84/// Negate the quaternion.
85static inline quat qneg(quat q) {
86 return (quat){.x = -q.x, .y = -q.y, .z = -q.z, .w = -q.w};
87}
88
89/// Return the dot product of two quaternions.
90static inline R qdot(quat a, quat b) {
91 return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
92}
93
94/// Return the quaternion's magnitude.
95static inline R qnorm(quat q) {
96 return sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
97}
98
99/// Return the quaternion's squared magnitude.
100static inline R qnorm2(quat q) {
101 return q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w;
102}
103
104/// Normalize the quaternion.
105static inline quat qnormalize(quat q) {
106 const R n = qnorm(q);
107 return (quat){.x = q.x / n, .y = q.y / n, .z = q.z / n, .w = q.w / n};
108}
109
110/// Scale the quaternion.
111static inline quat qscale(quat q, R s) {
112 return (quat){.x = s * q.x, .y = s * q.y, .z = s * q.z, .w = s * q.w};
113}
114
64/// Get a 4x4 rotation matrix from a quaternion. 115/// Get a 4x4 rotation matrix from a quaternion.
65static inline mat4 mat4_from_quat(quat q) { 116static inline mat4 mat4_from_quat(quat q) {
66 const R xx = q.x * q.x; 117 const R xx = q.x * q.x;
@@ -77,3 +128,35 @@ static inline mat4 mat4_from_quat(quat q) {
77 1 - 2 * xx - 2 * zz, 2 * yz - 2 * wx, 0, 2 * xz - 2 * wy, 2 * yz + 2 * wx, 128 1 - 2 * xx - 2 * zz, 2 * yz - 2 * wx, 0, 2 * xz - 2 * wy, 2 * yz + 2 * wx,
78 1 - 2 * xx - 2 * yy, 0, 0, 0, 0, 1); 129 1 - 2 * xx - 2 * yy, 0, 0, 0, 0, 1);
79} 130}
131
132/// Interpolate two unit quaternions using spherical linear interpolation.
133///
134/// Note: You might want to normalize the result.
135static inline quat qslerp(quat a, quat b, R t) {
136 assert(0.0 <= t);
137 assert(t <= 1.0);
138 const R eps = 1e-5;
139 (void)eps;
140 assert(R_eq(qnorm2(a), 1.0, eps));
141 assert(R_eq(qnorm2(b), 1.0, eps));
142 R dot = qdot(a, b);
143 // Make the rotation path follow the "short way", i.e., ensure that:
144 // -90 <= angle <= 90
145 if (dot < 0.0) {
146 dot = -dot;
147 b = qneg(b);
148 }
149 // For numerical stability, perform linear interpolation when the two
150 // quaternions are close to each other.
151 R ta, tb;
152 if (1.0 - dot > 1e-6) {
153 const R theta = acos(dot);
154 const R sin_theta = sqrt(1 - dot * dot);
155 ta = sin(theta * (1.0 - t)) / sin_theta;
156 tb = sin(theta * t) / sin_theta;
157 } else { // Linear interpolation.
158 ta = 1.0 - t;
159 tb = t;
160 }
161 return qadd(qscale(a, ta), qscale(b, tb));
162}