diff options
Diffstat (limited to 'include/math/quat.h')
-rw-r--r-- | include/math/quat.h | 87 |
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. |
8 | typedef struct quat { | 10 | typedef 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. | ||
12 | static inline quat qunit() { | 15 | static 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. | ||
16 | static inline quat qmake(R x, R y, R z, R w) { | 20 | static 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. | ||
20 | static inline quat quat_from_array(const R xyzw[4]) { | 25 | static 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. | ||
30 | static 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. | ||
35 | static 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. |
25 | static inline quat qmake_rot(R angle, R x, R y, R z) { | 40 | static 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. | ||
53 | static 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. |
38 | static inline quat qmul(quat q1, quat q2) { | 58 | static 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. | ||
85 | static 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. | ||
90 | static 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. | ||
95 | static 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. | ||
100 | static 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. | ||
105 | static 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. | ||
111 | static 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. |
65 | static inline mat4 mat4_from_quat(quat q) { | 116 | static 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. | ||
135 | static 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 | } | ||