1use crate::{NearlyEqual, Point, Vector3, Vector4};
2
3#[derive(Copy, Clone, Debug, PartialEq)]
5#[repr(C)]
6pub struct Matrix4(pub [Vector4; 4]);
7
8impl Matrix4 {
9 pub const fn from_1d_array(a: [f32; 16]) -> Self {
11 Self([
12 Vector4::new(a[0], a[1], a[2], a[3]),
13 Vector4::new(a[4], a[5], a[6], a[4]),
14 Vector4::new(a[8], a[9], a[10], a[11]),
15 Vector4::new(a[12], a[13], a[14], a[15]),
16 ])
17 }
18
19 pub const fn from_2d_array(a: [[f32; 4]; 4]) -> Self {
21 Self([
22 Vector4::new(a[0][0], a[0][1], a[0][2], a[0][3]),
23 Vector4::new(a[1][0], a[1][1], a[1][2], a[1][3]),
24 Vector4::new(a[2][0], a[2][1], a[2][2], a[2][3]),
25 Vector4::new(a[3][0], a[3][1], a[3][2], a[3][3]),
26 ])
27 }
28
29 pub const fn identity() -> Self {
31 Self([
32 Vector4::new(1.0, 0.0, 0.0, 0.0),
33 Vector4::new(0.0, 1.0, 0.0, 0.0),
34 Vector4::new(0.0, 0.0, 1.0, 0.0),
35 Vector4::new(0.0, 0.0, 0.0, 1.0),
36 ])
37 }
38
39 pub const fn zero() -> Self {
41 Self([
42 Vector4::new(0.0, 0.0, 0.0, 0.0),
43 Vector4::new(0.0, 0.0, 0.0, 0.0),
44 Vector4::new(0.0, 0.0, 0.0, 0.0),
45 Vector4::new(0.0, 0.0, 0.0, 0.0),
46 ])
47 }
48
49 pub fn look_at(eye: Point, target: Point, up: Vector3) -> Self {
51 let z_axis = (target - eye).normalized();
52 let x_axis = z_axis.cross(up).normalized();
53 let y_axis = x_axis.cross(z_axis);
54
55 let eye_vec = eye.into();
56
57 Self([
58 Vector4::new(x_axis.x, y_axis.x, -z_axis.x, 0.0),
59 Vector4::new(x_axis.y, y_axis.y, -z_axis.y, 0.0),
60 Vector4::new(x_axis.z, y_axis.z, -z_axis.z, 0.0),
61 Vector4::new(
62 -x_axis.dot(eye_vec),
63 -y_axis.dot(eye_vec),
64 z_axis.dot(eye_vec),
65 1.0,
66 ),
67 ])
68 }
69
70 pub fn perspective(aspect_ratio: f32, fov_radians: f32, znear: f32, zfar: f32) -> Self {
72 let f = 1.0 / (fov_radians / 2.0).tan();
73
74 Self([
75 Vector4::new(f / aspect_ratio, 0.0, 0.0, 0.0),
76 Vector4::new(0.0, f, 0.0, 0.0),
77 Vector4::new(0.0, 0.0, (zfar + znear) / (znear - zfar), -1.0),
78 Vector4::new(0.0, 0.0, (2.0 * zfar * znear) / (znear - zfar), 0.0),
79 ])
80 }
81
82 pub fn orthographic(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Self {
84 Self([
85 Vector4::new(2.0 / (right - left), 0.0, 0.0, 0.0),
86 Vector4::new(0.0, 2.0 / (top - bottom), 0.0, 0.0),
87 Vector4::new(0.0, 0.0, -2.0 / (far - near), -1.0),
88 Vector4::new(
89 -(right + left) / (right - left),
90 -(top + bottom) / (top - bottom),
91 -(far + near) / (far - near),
92 1.0,
93 ),
94 ])
95 }
96
97 pub fn translation(v: Vector3) -> Self {
99 Self([
100 Vector4::new(1.0, 0.0, 0.0, 0.0),
101 Vector4::new(0.0, 1.0, 0.0, 0.0),
102 Vector4::new(0.0, 0.0, 1.0, 0.0),
103 Vector4::new(v.x, v.y, v.z, 1.0),
104 ])
105 }
106
107 pub fn rotation_x(angle_radians: f32) -> Self {
109 Self([
110 Vector4::new(1.0, 0.0, 0.0, 0.0),
111 Vector4::new(0.0, angle_radians.cos(), -angle_radians.sin(), 0.0),
112 Vector4::new(0.0, angle_radians.sin(), angle_radians.cos(), 0.0),
113 Vector4::new(0.0, 0.0, 0.0, 1.0),
114 ])
115 }
116
117 pub fn rotation_y(angle_radians: f32) -> Self {
119 Self([
120 Vector4::new(angle_radians.cos(), 0.0, angle_radians.sin(), 0.0),
121 Vector4::new(0.0, 1.0, 0.0, 0.0),
122 Vector4::new(-angle_radians.sin(), 0.0, angle_radians.cos(), 0.0),
123 Vector4::new(0.0, 0.0, 0.0, 1.0),
124 ])
125 }
126
127 pub fn rotation_z(angle_radians: f32) -> Self {
129 Self([
130 Vector4::new(angle_radians.cos(), -angle_radians.sin(), 0.0, 0.0),
131 Vector4::new(angle_radians.sin(), angle_radians.cos(), 0.0, 0.0),
132 Vector4::new(0.0, 0.0, 1.0, 0.0),
133 Vector4::new(0.0, 0.0, 0.0, 1.0),
134 ])
135 }
136
137 pub fn rotation_axis_angle(axis: Vector3, angle_radians: f32) -> Self {
139 let sin = angle_radians.sin();
140 let cos = angle_radians.cos();
141 let k = 1.0 - cos;
142
143 Self([
144 Vector4::new(
145 axis.x * axis.x * k + cos,
146 axis.y * axis.x * k - sin * axis.z,
147 axis.z * axis.x * k + sin * axis.y,
148 0.0,
149 ),
150 Vector4::new(
151 axis.x * axis.y * k + sin * axis.z,
152 axis.y * axis.y * k + cos,
153 axis.z * axis.y * k - sin * axis.x,
154 0.0,
155 ),
156 Vector4::new(
157 axis.x * axis.z * k - sin * axis.y,
158 axis.y * axis.z * k + sin * axis.x,
159 axis.z * axis.z * k + cos,
160 0.0,
161 ),
162 Vector4::new(0.0, 0.0, 0.0, 1.0),
163 ])
164 }
165
166 pub fn rotation_from_vector_to_vector(start: Vector3, end: Vector3) -> Self {
168 let axis = -start.cross(end);
169
170 let cos = start.dot(end);
171 let k = 1.0 / (1.0 + cos);
172
173 Self([
174 Vector4::new(
175 axis.x * axis.x * k + cos,
176 axis.y * axis.x * k - axis.z,
177 axis.z * axis.x * k + axis.y,
178 0.0,
179 ),
180 Vector4::new(
181 axis.x * axis.y * k + axis.z,
182 axis.y * axis.y * k + cos,
183 axis.z * axis.y * k - axis.x,
184 0.0,
185 ),
186 Vector4::new(
187 axis.x * axis.z * k - axis.y,
188 axis.y * axis.z * k + axis.x,
189 axis.z * axis.z * k + cos,
190 0.0,
191 ),
192 Vector4::new(0.0, 0.0, 0.0, 1.0),
193 ])
194 }
195
196 pub fn uniform_scale(scale: f32) -> Self {
198 Self([
199 Vector4::new(scale, 0.0, 0.0, 0.0),
200 Vector4::new(0.0, scale, 0.0, 0.0),
201 Vector4::new(0.0, 0.0, scale, 0.0),
202 Vector4::new(0.0, 0.0, 0.0, 1.0),
203 ])
204 }
205
206 pub fn row(&self, i: usize) -> Vector4 {
208 Vector4::new(self.0[0][i], self.0[1][i], self.0[2][i], self.0[3][i])
209 }
210 pub fn column(&self, i: usize) -> Vector4 {
212 self.0[i]
213 }
214
215 pub fn transpose(&self) -> Self {
217 let mut r = Self::zero();
218
219 for i in 0..4 {
220 for j in 0..4 {
221 r.0[i][j] = self.0[j][i];
222 }
223 }
224
225 r
226 }
227
228 pub fn invert(&self) -> Self {
230 let mut inv = Matrix4::zero();
231
232 inv.0[0][0] = self.0[1][1] * self.0[2][2] * self.0[3][3]
233 - self.0[1][1] * self.0[2][3] * self.0[3][2]
234 - self.0[2][1] * self.0[1][2] * self.0[3][3]
235 + self.0[2][1] * self.0[1][3] * self.0[3][2]
236 + self.0[3][1] * self.0[1][2] * self.0[2][3]
237 - self.0[3][1] * self.0[1][3] * self.0[2][2];
238
239 inv.0[1][0] = -self.0[1][0] * self.0[2][2] * self.0[3][3]
240 + self.0[1][0] * self.0[2][3] * self.0[3][2]
241 + self.0[2][0] * self.0[1][2] * self.0[3][3]
242 - self.0[2][0] * self.0[1][3] * self.0[3][2]
243 - self.0[3][0] * self.0[1][2] * self.0[2][3]
244 + self.0[3][0] * self.0[1][3] * self.0[2][2];
245
246 inv.0[2][0] = self.0[1][0] * self.0[2][1] * self.0[3][3]
247 - self.0[1][0] * self.0[2][3] * self.0[3][1]
248 - self.0[2][0] * self.0[1][1] * self.0[3][3]
249 + self.0[2][0] * self.0[1][3] * self.0[3][1]
250 + self.0[3][0] * self.0[1][1] * self.0[2][3]
251 - self.0[3][0] * self.0[1][3] * self.0[2][1];
252
253 inv.0[3][0] = -self.0[1][0] * self.0[2][1] * self.0[3][2]
254 + self.0[1][0] * self.0[2][2] * self.0[3][1]
255 + self.0[2][0] * self.0[1][1] * self.0[3][2]
256 - self.0[2][0] * self.0[1][2] * self.0[3][1]
257 - self.0[3][0] * self.0[1][1] * self.0[2][2]
258 + self.0[3][0] * self.0[1][2] * self.0[2][1];
259
260 inv.0[0][1] = -self.0[0][1] * self.0[2][2] * self.0[3][3]
261 + self.0[0][1] * self.0[2][3] * self.0[3][2]
262 + self.0[2][1] * self.0[0][2] * self.0[3][3]
263 - self.0[2][1] * self.0[0][3] * self.0[3][2]
264 - self.0[3][1] * self.0[0][2] * self.0[2][3]
265 + self.0[3][1] * self.0[0][3] * self.0[2][2];
266
267 inv.0[1][1] = self.0[0][0] * self.0[2][2] * self.0[3][3]
268 - self.0[0][0] * self.0[2][3] * self.0[3][2]
269 - self.0[2][0] * self.0[0][2] * self.0[3][3]
270 + self.0[2][0] * self.0[0][3] * self.0[3][2]
271 + self.0[3][0] * self.0[0][2] * self.0[2][3]
272 - self.0[3][0] * self.0[0][3] * self.0[2][2];
273
274 inv.0[2][1] = -self.0[0][0] * self.0[2][1] * self.0[3][3]
275 + self.0[0][0] * self.0[2][3] * self.0[3][1]
276 + self.0[2][0] * self.0[0][1] * self.0[3][3]
277 - self.0[2][0] * self.0[0][3] * self.0[3][1]
278 - self.0[3][0] * self.0[0][1] * self.0[2][3]
279 + self.0[3][0] * self.0[0][3] * self.0[2][1];
280
281 inv.0[3][1] = self.0[0][0] * self.0[2][1] * self.0[3][2]
282 - self.0[0][0] * self.0[2][2] * self.0[3][1]
283 - self.0[2][0] * self.0[0][1] * self.0[3][2]
284 + self.0[2][0] * self.0[0][2] * self.0[3][1]
285 + self.0[3][0] * self.0[0][1] * self.0[2][2]
286 - self.0[3][0] * self.0[0][2] * self.0[2][1];
287
288 inv.0[0][2] = self.0[0][1] * self.0[1][2] * self.0[3][3]
289 - self.0[0][1] * self.0[1][3] * self.0[3][2]
290 - self.0[1][1] * self.0[0][2] * self.0[3][3]
291 + self.0[1][1] * self.0[0][3] * self.0[3][2]
292 + self.0[3][1] * self.0[0][2] * self.0[1][3]
293 - self.0[3][1] * self.0[0][3] * self.0[1][2];
294
295 inv.0[1][2] = -self.0[0][0] * self.0[1][2] * self.0[3][3]
296 + self.0[0][0] * self.0[1][3] * self.0[3][2]
297 + self.0[1][0] * self.0[0][2] * self.0[3][3]
298 - self.0[1][0] * self.0[0][3] * self.0[3][2]
299 - self.0[3][0] * self.0[0][2] * self.0[1][3]
300 + self.0[3][0] * self.0[0][3] * self.0[1][2];
301
302 inv.0[2][2] = self.0[0][0] * self.0[1][1] * self.0[3][3]
303 - self.0[0][0] * self.0[1][3] * self.0[3][1]
304 - self.0[1][0] * self.0[0][1] * self.0[3][3]
305 + self.0[1][0] * self.0[0][3] * self.0[3][1]
306 + self.0[3][0] * self.0[0][1] * self.0[1][3]
307 - self.0[3][0] * self.0[0][3] * self.0[1][1];
308
309 inv.0[3][2] = -self.0[0][0] * self.0[1][1] * self.0[3][2]
310 + self.0[0][0] * self.0[1][2] * self.0[3][1]
311 + self.0[1][0] * self.0[0][1] * self.0[3][2]
312 - self.0[1][0] * self.0[0][2] * self.0[3][1]
313 - self.0[3][0] * self.0[0][1] * self.0[1][2]
314 + self.0[3][0] * self.0[0][2] * self.0[1][1];
315
316 inv.0[0][3] = -self.0[0][1] * self.0[1][2] * self.0[2][3]
317 + self.0[0][1] * self.0[1][3] * self.0[2][2]
318 + self.0[1][1] * self.0[0][2] * self.0[2][3]
319 - self.0[1][1] * self.0[0][3] * self.0[2][2]
320 - self.0[2][1] * self.0[0][2] * self.0[1][3]
321 + self.0[2][1] * self.0[0][3] * self.0[1][2];
322
323 inv.0[1][3] = self.0[0][0] * self.0[1][2] * self.0[2][3]
324 - self.0[0][0] * self.0[1][3] * self.0[2][2]
325 - self.0[1][0] * self.0[0][2] * self.0[2][3]
326 + self.0[1][0] * self.0[0][3] * self.0[2][2]
327 + self.0[2][0] * self.0[0][2] * self.0[1][3]
328 - self.0[2][0] * self.0[0][3] * self.0[1][2];
329
330 inv.0[2][3] = -self.0[0][0] * self.0[1][1] * self.0[2][3]
331 + self.0[0][0] * self.0[1][3] * self.0[2][1]
332 + self.0[1][0] * self.0[0][1] * self.0[2][3]
333 - self.0[1][0] * self.0[0][3] * self.0[2][1]
334 - self.0[2][0] * self.0[0][1] * self.0[1][3]
335 + self.0[2][0] * self.0[0][3] * self.0[1][1];
336
337 inv.0[3][3] = self.0[0][0] * self.0[1][1] * self.0[2][2]
338 - self.0[0][0] * self.0[1][2] * self.0[2][1]
339 - self.0[1][0] * self.0[0][1] * self.0[2][2]
340 + self.0[1][0] * self.0[0][2] * self.0[2][1]
341 + self.0[2][0] * self.0[0][1] * self.0[1][2]
342 - self.0[2][0] * self.0[0][2] * self.0[1][1];
343
344 let mut det = self.0[0][0] * inv.0[0][0]
345 + self.0[0][1] * inv.0[1][0]
346 + self.0[0][2] * inv.0[2][0]
347 + self.0[0][3] * inv.0[3][0];
348 det = 1.0 / det;
349
350 for i in 0..4 {
351 for j in 0..4 {
352 inv.0[i][j] *= det;
353 }
354 }
355
356 inv
357 }
358
359 pub fn as_slice(&self) -> &[f32] {
360 unsafe {
361 std::slice::from_raw_parts(
362 &self.0[0][0],
363 std::mem::size_of::<Self>() / std::mem::size_of::<f32>(),
364 )
365 }
366 }
367}
368
369impl NearlyEqual for &Matrix4 {
370 fn nearly_equals(self, rhs: Self) -> bool {
371 for i in 0..4 {
372 if !self.0[i].nearly_equals(&rhs.0[i]) {
373 return false;
374 }
375 }
376
377 true
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 use crate::*;
384
385 #[test]
386 fn identity() {
387 let m = Matrix4::identity();
388 let p = Point::new(1.0, 2.0, 3.0);
389
390 assert_eq!(p, m * p);
391 assert_eq!(m, m.transpose());
392 assert_eq!(m, m.invert());
393 }
394
395 #[test]
396 fn invert() {
397 let m = Matrix4::from_2d_array([
398 [3.0, 2.0, 1.0, 1.0],
399 [2.0, 3.0, 2.0, 2.0],
400 [1.0, 2.0, 3.0, 3.0],
401 [0.0, 1.0, 1.0, 0.0],
402 ]);
403
404 assert_eq!(m.invert() * m, Matrix4::identity());
405
406 let n = Matrix4([
407 Vector4 {
408 x: 0.9742785,
409 y: 0.0,
410 z: 0.0,
411 w: 0.0,
412 },
413 Vector4 {
414 x: 0.0,
415 y: 1.7320507,
416 z: 0.0,
417 w: 0.0,
418 },
419 Vector4 {
420 x: 0.0,
421 y: 0.0,
422 z: -1.0002,
423 w: -1.0,
424 },
425 Vector4 {
426 x: 0.0,
427 y: 0.0,
428 z: -2.0002,
429 w: 0.0,
430 },
431 ]);
432 let inverse = Matrix4([
433 Vector4 {
434 x: 1.0264006,
435 y: -0.0,
436 z: -0.0,
437 w: -0.0,
438 },
439 Vector4 {
440 x: -0.0,
441 y: 0.5773504,
442 z: -0.0,
443 w: -0.0,
444 },
445 Vector4 {
446 x: -0.0,
447 y: -0.0,
448 z: -0.0,
449 w: -0.49995005,
450 },
451 Vector4 {
452 x: -0.0,
453 y: -0.0,
454 z: -1.0000001,
455 w: 0.50005007,
456 },
457 ]);
458 assert_eq!(n.invert(), inverse);
459 }
460
461 #[test]
462 fn translate() {
463 let m = Matrix4::translation(Vector3::new(10.0, 1.0, 0.0));
464 assert_eq!(m * Point::zero(), Point::new(10.0, 1.0, 0.0));
465
466 let n = Matrix4::translation(Vector3::new(-2.0, -5.0, 0.0));
467 assert_eq!(n * Point::zero(), Point::new(-2.0, -5.0, 0.0));
468
469 let t = m * n;
470 assert_eq!(t * Point::zero(), Point::new(8.0, -4.0, 0.0));
471 }
472
473 #[test]
474 fn rotate_axis_angle() {
475 let m =
476 Matrix4::rotation_axis_angle(Vector3::new(0.0, 1.0, 0.0), std::f32::consts::FRAC_PI_2);
477 assert_eq!(m * Point::zero(), Point::zero());
478 assert_nearly_eq!(m * Point::new(1.0, 0.0, 0.0), &Point::new(0.0, 0.0, 1.0));
479
480 let m =
481 Matrix4::rotation_axis_angle(Vector3::new(1.0, 0.0, 0.0), std::f32::consts::FRAC_PI_2);
482 assert_nearly_eq!(m * Point::new(0.0, 0.0, 1.0), &Point::new(0.0, 1.0, 0.0));
483
484 let m =
485 Matrix4::rotation_axis_angle(Vector3::new(0.0, 0.0, 1.0), std::f32::consts::FRAC_PI_2);
486 assert_nearly_eq!(m * Point::new(1.0, 0.0, 0.0), &Point::new(0.0, -1.0, 0.0));
487 }
488
489 #[test]
490 fn rotation_from_vector_to_vector() {
491 let m = Matrix4::rotation_from_vector_to_vector(
492 Vector3::new(1.0, 0.0, 0.0),
493 Vector3::new(0.0, 0.0, 1.0),
494 );
495 assert_eq!(m * Point::zero(), Point::zero());
496 assert_nearly_eq!(m * Point::new(1.0, 0.0, 0.0), &Point::new(0.0, 0.0, 1.0));
497 assert_nearly_eq!(m * Point::new(0.0, 0.0, 1.0), &Point::new(-1.0, 0.0, 0.0));
498 }
499
500 #[test]
501 fn slice() {
502 let a = [
503 3.0, 2.0, 1.0, 1.0, 2.0, 3.0, 2.0, 2.0, 1.0, 2.0, 3.0, 3.0, 0.0, 1.0, 1.0, 0.0,
504 ];
505 let m = Matrix4::from_1d_array(a);
506
507 assert_eq!(m.as_slice(), &a);
508 }
509}