Skip to main content

mittens_engine/utils/
math.rs

1const PERLIN_PERM: [u8; 256] = [
2    151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
3    142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
4    203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175,
5    74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230,
6    220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76,
7    132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173,
8    186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206,
9    59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163,
10    70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232,
11    178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162,
12    241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204,
13    176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141,
14    128, 195, 78, 66, 215, 61, 156, 180,
15];
16
17fn perlin_fade(t: f64) -> f64 {
18    t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
19}
20
21fn perlin_lerp(a: f64, b: f64, t: f64) -> f64 {
22    a + (b - a) * t
23}
24
25fn perlin_hash(x: i32, y: i32, z: i32) -> u8 {
26    let x = PERLIN_PERM[(x & 255) as usize] as usize;
27    let y = PERLIN_PERM[((x + (y & 255) as usize) & 255) as usize] as usize;
28    PERLIN_PERM[((y + (z & 255) as usize) & 255) as usize]
29}
30
31fn perlin_grad(hash: u8, x: f64, y: f64, z: f64) -> f64 {
32    match hash & 0x0f {
33        0x0 => x + y,
34        0x1 => -x + y,
35        0x2 => x - y,
36        0x3 => -x - y,
37        0x4 => x + z,
38        0x5 => -x + z,
39        0x6 => x - z,
40        0x7 => -x - z,
41        0x8 => y + z,
42        0x9 => -y + z,
43        0xa => y - z,
44        0xb => -y - z,
45        0xc => y + x,
46        0xd => -y + z,
47        0xe => y - x,
48        _ => -y - z,
49    }
50}
51
52pub fn perlin(x: f64, y: f64, z: Option<f64>) -> f64 {
53    let z = z.unwrap_or(0.0);
54
55    let xi0 = x.floor() as i32;
56    let yi0 = y.floor() as i32;
57    let zi0 = z.floor() as i32;
58    let xi1 = xi0 + 1;
59    let yi1 = yi0 + 1;
60    let zi1 = zi0 + 1;
61
62    let xf0 = x - xi0 as f64;
63    let yf0 = y - yi0 as f64;
64    let zf0 = z - zi0 as f64;
65    let xf1 = xf0 - 1.0;
66    let yf1 = yf0 - 1.0;
67    let zf1 = zf0 - 1.0;
68
69    let u = perlin_fade(xf0);
70    let v = perlin_fade(yf0);
71    let w = perlin_fade(zf0);
72
73    let x00 = perlin_lerp(
74        perlin_grad(perlin_hash(xi0, yi0, zi0), xf0, yf0, zf0),
75        perlin_grad(perlin_hash(xi1, yi0, zi0), xf1, yf0, zf0),
76        u,
77    );
78    let x10 = perlin_lerp(
79        perlin_grad(perlin_hash(xi0, yi1, zi0), xf0, yf1, zf0),
80        perlin_grad(perlin_hash(xi1, yi1, zi0), xf1, yf1, zf0),
81        u,
82    );
83    let x01 = perlin_lerp(
84        perlin_grad(perlin_hash(xi0, yi0, zi1), xf0, yf0, zf1),
85        perlin_grad(perlin_hash(xi1, yi0, zi1), xf1, yf0, zf1),
86        u,
87    );
88    let x11 = perlin_lerp(
89        perlin_grad(perlin_hash(xi0, yi1, zi1), xf0, yf1, zf1),
90        perlin_grad(perlin_hash(xi1, yi1, zi1), xf1, yf1, zf1),
91        u,
92    );
93
94    let y0 = perlin_lerp(x00, x10, v);
95    let y1 = perlin_lerp(x01, x11, v);
96    perlin_lerp(y0, y1, w).clamp(-1.0, 1.0)
97}
98
99pub fn quat_mul(a: [f32; 4], b: [f32; 4]) -> [f32; 4] {
100    let (ax, ay, az, aw) = (a[0], a[1], a[2], a[3]);
101    let (bx, by, bz, bw) = (b[0], b[1], b[2], b[3]);
102    [
103        aw * bx + ax * bw + ay * bz - az * by,
104        aw * by - ax * bz + ay * bw + az * bx,
105        aw * bz + ax * by - ay * bx + az * bw,
106        aw * bw - ax * bx - ay * by - az * bz,
107    ]
108}
109
110pub fn quat_conjugate(q: [f32; 4]) -> [f32; 4] {
111    [-q[0], -q[1], -q[2], q[3]]
112}
113
114pub fn quat_rotate_vec3(q: [f32; 4], v: [f32; 3]) -> [f32; 3] {
115    // v' = q * (v,0) * conj(q)
116    let vq = [v[0], v[1], v[2], 0.0f32];
117    let t = quat_mul(q, vq);
118    let r = quat_mul(t, quat_conjugate(q));
119    [r[0], r[1], r[2]]
120}
121
122pub fn vec3_normalize(v: [f32; 3]) -> [f32; 3] {
123    let len2 = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
124    if len2 <= 0.0 {
125        return [0.0, 0.0, 0.0];
126    }
127    let inv = len2.sqrt().recip();
128    [v[0] * inv, v[1] * inv, v[2] * inv]
129}
130
131pub fn vec3_len(v: [f32; 3]) -> f32 {
132    (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt()
133}
134
135pub fn vec3_dot(a: [f32; 3], b: [f32; 3]) -> f32 {
136    a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
137}
138
139pub fn vec3_cross(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
140    [
141        a[1] * b[2] - a[2] * b[1],
142        a[2] * b[0] - a[0] * b[2],
143        a[0] * b[1] - a[1] * b[0],
144    ]
145}
146
147pub fn vec3_scale(v: [f32; 3], s: f32) -> [f32; 3] {
148    [v[0] * s, v[1] * s, v[2] * s]
149}
150
151pub fn vec3_negate(v: [f32; 3]) -> [f32; 3] {
152    [-v[0], -v[1], -v[2]]
153}
154
155pub fn vec3_sub(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
156    [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
157}
158
159pub fn vec3_reflect(dir: [f32; 3], plane_normal: [f32; 3]) -> [f32; 3] {
160    let dist = vec3_dot(dir, plane_normal);
161    vec3_sub(dir, vec3_scale(plane_normal, 2.0 * dist))
162}
163
164pub fn vec3_reflect_point(
165    point: [f32; 3],
166    plane_pos: [f32; 3],
167    plane_normal: [f32; 3],
168) -> [f32; 3] {
169    let offset = vec3_sub(point, plane_pos);
170    let dist = vec3_dot(offset, plane_normal);
171    vec3_sub(point, vec3_scale(plane_normal, 2.0 * dist))
172}
173
174pub fn mat4_identity() -> [[f32; 4]; 4] {
175    [
176        [1.0, 0.0, 0.0, 0.0],
177        [0.0, 1.0, 0.0, 0.0],
178        [0.0, 0.0, 1.0, 0.0],
179        [0.0, 0.0, 0.0, 1.0],
180    ]
181}
182
183pub fn mat4_mul(a: [[f32; 4]; 4], b: [[f32; 4]; 4]) -> [[f32; 4]; 4] {
184    let mut out = [[0.0f32; 4]; 4];
185    for c in 0..4 {
186        for r in 0..4 {
187            out[c][r] =
188                a[0][r] * b[c][0] + a[1][r] * b[c][1] + a[2][r] * b[c][2] + a[3][r] * b[c][3];
189        }
190    }
191    out
192}
193
194pub fn mat4_mul_vec4(m: [[f32; 4]; 4], v: [f32; 4]) -> [f32; 4] {
195    [
196        m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0] * v[3],
197        m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1] * v[3],
198        m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2] * v[3],
199        m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3] * v[3],
200    ]
201}
202
203pub fn vec3_add(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
204    [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
205}
206
207pub fn vec3_lerp(a: [f32; 3], b: [f32; 3], t: f32) -> [f32; 3] {
208    vec3_add(a, vec3_scale(vec3_sub(b, a), t))
209}
210
211pub fn quat_normalize(q: [f32; 4]) -> [f32; 4] {
212    let len2 = q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3];
213    if len2 < 1e-12 {
214        return [0.0, 0.0, 0.0, 1.0];
215    }
216    let inv = len2.sqrt().recip();
217    [q[0] * inv, q[1] * inv, q[2] * inv, q[3] * inv]
218}
219
220pub fn quat_rotation_y(yaw: f32) -> [f32; 4] {
221    let half = yaw * 0.5;
222    [0.0, half.sin(), 0.0, half.cos()]
223}
224
225/// Normalised linear interpolation between two unit quaternions.
226/// Ensures shortest-path by negating `b` if the dot product is negative.
227pub fn quat_nlerp(a: [f32; 4], b: [f32; 4], t: f32) -> [f32; 4] {
228    let dot = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
229    let b = if dot < 0.0 {
230        [-b[0], -b[1], -b[2], -b[3]]
231    } else {
232        b
233    };
234    quat_normalize([
235        a[0] + (b[0] - a[0]) * t,
236        a[1] + (b[1] - a[1]) * t,
237        a[2] + (b[2] - a[2]) * t,
238        a[3] + (b[3] - a[3]) * t,
239    ])
240}
241
242/// Minimum-arc quaternion rotating unit vector `from` to unit vector `to`.
243pub fn shortest_arc_quat(from: [f32; 3], to: [f32; 3]) -> [f32; 4] {
244    let d = vec3_dot(from, to);
245    if d < -0.9999 {
246        let perp = if from[0].abs() < 0.9 {
247            [1.0, 0.0, 0.0]
248        } else {
249            [0.0, 1.0, 0.0]
250        };
251        let axis = vec3_normalize(vec3_cross(from, perp));
252        return [axis[0], axis[1], axis[2], 0.0];
253    }
254    let c = vec3_cross(from, to);
255    quat_normalize([c[0], c[1], c[2], 1.0 + d])
256}
257
258/// Extract a unit quaternion from a column-major 4x4 world matrix (may have scale).
259pub fn mat_to_quat(m: [[f32; 4]; 4]) -> [f32; 4] {
260    fn col_len(m: [[f32; 4]; 4], c: usize) -> f32 {
261        (m[c][0] * m[c][0] + m[c][1] * m[c][1] + m[c][2] * m[c][2])
262            .sqrt()
263            .max(1e-9)
264    }
265    let s0 = col_len(m, 0).recip();
266    let s1 = col_len(m, 1).recip();
267    let s2 = col_len(m, 2).recip();
268    let r00 = m[0][0] * s0;
269    let r10 = m[0][1] * s0;
270    let r20 = m[0][2] * s0;
271    let r01 = m[1][0] * s1;
272    let r11 = m[1][1] * s1;
273    let r21 = m[1][2] * s1;
274    let r02 = m[2][0] * s2;
275    let r12 = m[2][1] * s2;
276    let r22 = m[2][2] * s2;
277    let trace = r00 + r11 + r22;
278    if trace > 0.0 {
279        let s = 0.5 / (trace + 1.0).sqrt();
280        quat_normalize([(r21 - r12) * s, (r02 - r20) * s, (r10 - r01) * s, 0.25 / s])
281    } else if r00 > r11 && r00 > r22 {
282        let s = 2.0 * (1.0 + r00 - r11 - r22).sqrt();
283        quat_normalize([0.25 * s, (r01 + r10) / s, (r02 + r20) / s, (r21 - r12) / s])
284    } else if r11 > r22 {
285        let s = 2.0 * (1.0 + r11 - r00 - r22).sqrt();
286        quat_normalize([(r01 + r10) / s, 0.25 * s, (r12 + r21) / s, (r02 - r20) / s])
287    } else {
288        let s = 2.0 * (1.0 + r22 - r00 - r11).sqrt();
289        quat_normalize([(r02 + r20) / s, (r12 + r21) / s, 0.25 * s, (r10 - r01) / s])
290    }
291}
292
293pub fn quat_from_axis_angle(axis: [f32; 3], angle_rad: f32) -> [f32; 4] {
294    let axis = vec3_normalize(axis);
295    let (s, c) = (0.5 * angle_rad).sin_cos();
296    [axis[0] * s, axis[1] * s, axis[2] * s, c]
297}
298
299/// Extract rotation axis and angle (radians) from a quaternion.
300/// Returns (axis, angle) where angle is in range [-π, π].
301/// For near-zero rotations, returns ([0, 0, 1], 0).
302pub fn quat_to_axis_angle(q: [f32; 4]) -> ([f32; 3], f32) {
303    let [x, y, z, w] = q;
304    let len_sq = x * x + y * y + z * z;
305
306    if len_sq < 1e-10 {
307        // Near identity quaternion
308        return ([0.0, 0.0, 1.0], 0.0);
309    }
310
311    let sin_half_angle = len_sq.sqrt();
312    let angle = 2.0 * sin_half_angle.atan2(w);
313    let axis = if sin_half_angle > 1e-6 {
314        [x / sin_half_angle, y / sin_half_angle, z / sin_half_angle]
315    } else {
316        [0.0, 0.0, 1.0]
317    };
318
319    (axis, angle)
320}
321
322/// Invert a column-major 4x4 matrix.
323///
324/// Returns `None` if the matrix is singular.
325pub fn mat4_inverse(m: [[f32; 4]; 4]) -> Option<[[f32; 4]; 4]> {
326    // Generic 4x4 inverse via Gauss-Jordan elimination on an augmented matrix.
327    // Treat input as row-major for elimination convenience by transposing access.
328    let mut a = [[0.0f32; 8]; 4];
329    for r in 0..4 {
330        for c in 0..4 {
331            // Convert column-major m[c][r] into row-major a[r][c].
332            a[r][c] = m[c][r];
333        }
334        a[r][4 + r] = 1.0;
335    }
336
337    for i in 0..4 {
338        // Find pivot.
339        let mut pivot_row = i;
340        let mut pivot_val = a[i][i].abs();
341        for r in (i + 1)..4 {
342            let v = a[r][i].abs();
343            if v > pivot_val {
344                pivot_val = v;
345                pivot_row = r;
346            }
347        }
348        if pivot_val == 0.0 {
349            return None;
350        }
351        if pivot_row != i {
352            a.swap(i, pivot_row);
353        }
354
355        // Normalize pivot row.
356        let inv_pivot = 1.0 / a[i][i];
357        for c in i..8 {
358            a[i][c] *= inv_pivot;
359        }
360
361        // Eliminate other rows.
362        for r in 0..4 {
363            if r == i {
364                continue;
365            }
366            let factor = a[r][i];
367            if factor == 0.0 {
368                continue;
369            }
370            for c in i..8 {
371                a[r][c] -= factor * a[i][c];
372            }
373        }
374    }
375
376    // Extract inverse (row-major) and convert back to column-major.
377    let mut inv = [[0.0f32; 4]; 4];
378    for r in 0..4 {
379        for c in 0..4 {
380            inv[c][r] = a[r][4 + c];
381        }
382    }
383    Some(inv)
384}