1use std::fmt;
4use std::ops::{Add, Mul, Neg, Sub};
5
6use super::single::InvariantF32;
7use super::{Matrix3x2, Quaternion, Vector3};
8
9#[derive(Clone, Copy, Debug, PartialEq, Default)]
15#[repr(C)]
16pub struct Matrix4x4 {
17 pub m11: f32,
19 pub m12: f32,
21 pub m13: f32,
23 pub m14: f32,
25 pub m21: f32,
27 pub m22: f32,
29 pub m23: f32,
31 pub m24: f32,
33 pub m31: f32,
35 pub m32: f32,
37 pub m33: f32,
39 pub m34: f32,
41 pub m41: f32,
43 pub m42: f32,
45 pub m43: f32,
47 pub m44: f32,
49}
50
51impl Matrix4x4 {
52 pub const IDENTITY: Matrix4x4 = Matrix4x4::new(
54 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
55 );
56
57 #[inline]
59 #[allow(clippy::too_many_arguments)]
60 pub const fn new(
61 m11: f32,
62 m12: f32,
63 m13: f32,
64 m14: f32,
65 m21: f32,
66 m22: f32,
67 m23: f32,
68 m24: f32,
69 m31: f32,
70 m32: f32,
71 m33: f32,
72 m34: f32,
73 m41: f32,
74 m42: f32,
75 m43: f32,
76 m44: f32,
77 ) -> Self {
78 Self {
79 m11,
80 m12,
81 m13,
82 m14,
83 m21,
84 m22,
85 m23,
86 m24,
87 m31,
88 m32,
89 m33,
90 m34,
91 m41,
92 m42,
93 m43,
94 m44,
95 }
96 }
97
98 #[inline]
100 pub const fn from_matrix3x2(value: Matrix3x2) -> Self {
101 Self::new(
102 value.m11, value.m12, 0.0, 0.0, value.m21, value.m22, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, value.m31,
103 value.m32, 0.0, 1.0,
104 )
105 }
106
107 #[inline]
109 pub fn is_identity(&self) -> bool {
110 *self == Matrix4x4::IDENTITY
111 }
112
113 #[inline]
115 pub fn translation(&self) -> Vector3 {
116 Vector3::new(self.m41, self.m42, self.m43)
117 }
118
119 #[inline]
121 pub fn set_translation(&mut self, value: Vector3) {
122 self.m41 = value.x;
123 self.m42 = value.y;
124 self.m43 = value.z;
125 }
126
127 pub fn create_from_axis_angle(axis: Vector3, angle: f32) -> Matrix4x4 {
132 let q = Quaternion::create_from_axis_angle(axis, angle);
133 Matrix4x4::create_from_quaternion(q)
134 }
135
136 pub fn create_from_quaternion(quaternion: Quaternion) -> Matrix4x4 {
138 let mut result = Matrix4x4::IDENTITY;
139
140 let xx = quaternion.x * quaternion.x;
141 let yy = quaternion.y * quaternion.y;
142 let zz = quaternion.z * quaternion.z;
143
144 let xy = quaternion.x * quaternion.y;
145 let wz = quaternion.z * quaternion.w;
146 let xz = quaternion.z * quaternion.x;
147 let wy = quaternion.y * quaternion.w;
148 let yz = quaternion.y * quaternion.z;
149 let wx = quaternion.x * quaternion.w;
150
151 result.m11 = 1.0 - 2.0 * (yy + zz);
152 result.m12 = 2.0 * (xy + wz);
153 result.m13 = 2.0 * (xz - wy);
154
155 result.m21 = 2.0 * (xy - wz);
156 result.m22 = 1.0 - 2.0 * (zz + xx);
157 result.m23 = 2.0 * (yz + wx);
158
159 result.m31 = 2.0 * (xz + wy);
160 result.m32 = 2.0 * (yz - wx);
161 result.m33 = 1.0 - 2.0 * (yy + xx);
162
163 result
164 }
165
166 #[inline]
168 pub fn create_from_yaw_pitch_roll(yaw: f32, pitch: f32, roll: f32) -> Matrix4x4 {
169 let q = Quaternion::create_from_yaw_pitch_roll(yaw, pitch, roll);
170 Matrix4x4::create_from_quaternion(q)
171 }
172
173 pub fn create_look_at(camera_position: Vector3, camera_target: Vector3, camera_up_vector: Vector3) -> Matrix4x4 {
181 let zaxis = Vector3::normalize(camera_position - camera_target);
182 let xaxis = Vector3::normalize(Vector3::cross(camera_up_vector, zaxis));
183 let yaxis = Vector3::cross(zaxis, xaxis);
184
185 Matrix4x4::new(
190 xaxis.x,
191 yaxis.x,
192 zaxis.x,
193 0.0,
194 xaxis.y,
195 yaxis.y,
196 zaxis.y,
197 0.0,
198 xaxis.z,
199 yaxis.z,
200 zaxis.z,
201 0.0,
202 -Vector3::dot(xaxis, camera_position),
203 -Vector3::dot(yaxis, camera_position),
204 -Vector3::dot(zaxis, camera_position),
205 1.0,
206 )
207 }
208
209 pub fn create_perspective_field_of_view(
225 field_of_view: f32,
226 aspect_ratio: f32,
227 near_plane_distance: f32,
228 far_plane_distance: f32,
229 ) -> Matrix4x4 {
230 if field_of_view <= 0.0 {
233 panic!("field_of_view ('{field_of_view}') must be greater than '0'.");
234 }
235 if field_of_view >= std::f32::consts::PI {
236 panic!("field_of_view ('{field_of_view}') must be less than '{}'.", std::f32::consts::PI);
237 }
238 if near_plane_distance <= 0.0 {
239 panic!("near_plane_distance ('{near_plane_distance}') must be greater than '0'.");
240 }
241 if far_plane_distance <= 0.0 {
242 panic!("far_plane_distance ('{far_plane_distance}') must be greater than '0'.");
243 }
244 if near_plane_distance >= far_plane_distance {
245 panic!("near_plane_distance ('{near_plane_distance}') must be less than '{far_plane_distance}'.");
246 }
247
248 let height = 1.0 / (field_of_view * 0.5).tan();
249 let width = height / aspect_ratio;
250 let range = if far_plane_distance == f32::INFINITY {
251 -1.0
252 } else {
253 far_plane_distance / (near_plane_distance - far_plane_distance)
254 };
255
256 Matrix4x4::new(
261 width,
262 0.0,
263 0.0,
264 0.0,
265 0.0,
266 height,
267 0.0,
268 0.0,
269 0.0,
270 0.0,
271 range,
272 -1.0,
273 0.0,
274 0.0,
275 range * near_plane_distance,
276 0.0,
277 )
278 }
279
280 pub fn create_rotation_x(radians: f32) -> Matrix4x4 {
282 let c = radians.cos();
283 let s = radians.sin();
284
285 let mut result = Matrix4x4::IDENTITY;
290 result.m22 = c;
291 result.m23 = s;
292 result.m32 = -s;
293 result.m33 = c;
294 result
295 }
296
297 pub fn create_rotation_x_at(radians: f32, center_point: Vector3) -> Matrix4x4 {
299 let c = radians.cos();
300 let s = radians.sin();
301
302 let y = center_point.y * (1.0 - c) + center_point.z * s;
303 let z = center_point.z * (1.0 - c) - center_point.y * s;
304
305 let mut result = Matrix4x4::IDENTITY;
310 result.m22 = c;
311 result.m23 = s;
312 result.m32 = -s;
313 result.m33 = c;
314 result.m42 = y;
315 result.m43 = z;
316 result
317 }
318
319 pub fn create_rotation_y(radians: f32) -> Matrix4x4 {
321 let c = radians.cos();
322 let s = radians.sin();
323
324 let mut result = Matrix4x4::IDENTITY;
329 result.m11 = c;
330 result.m13 = -s;
331 result.m31 = s;
332 result.m33 = c;
333 result
334 }
335
336 pub fn create_rotation_y_at(radians: f32, center_point: Vector3) -> Matrix4x4 {
338 let c = radians.cos();
339 let s = radians.sin();
340
341 let x = center_point.x * (1.0 - c) - center_point.z * s;
342 let z = center_point.z * (1.0 - c) + center_point.x * s;
343
344 let mut result = Matrix4x4::IDENTITY;
349 result.m11 = c;
350 result.m13 = -s;
351 result.m31 = s;
352 result.m33 = c;
353 result.m41 = x;
354 result.m43 = z;
355 result
356 }
357
358 pub fn create_rotation_z(radians: f32) -> Matrix4x4 {
360 let c = radians.cos();
361 let s = radians.sin();
362
363 let mut result = Matrix4x4::IDENTITY;
368 result.m11 = c;
369 result.m12 = s;
370 result.m21 = -s;
371 result.m22 = c;
372 result
373 }
374
375 pub fn create_rotation_z_at(radians: f32, center_point: Vector3) -> Matrix4x4 {
377 let c = radians.cos();
378 let s = radians.sin();
379
380 let x = center_point.x * (1.0 - c) + center_point.y * s;
381 let y = center_point.y * (1.0 - c) - center_point.x * s;
382
383 let mut result = Matrix4x4::IDENTITY;
388 result.m11 = c;
389 result.m12 = s;
390 result.m21 = -s;
391 result.m22 = c;
392 result.m41 = x;
393 result.m42 = y;
394 result
395 }
396
397 #[inline]
399 pub fn create_scale(scales: Vector3) -> Matrix4x4 {
400 Matrix4x4::create_scale_xyz(scales.x, scales.y, scales.z)
401 }
402
403 #[inline]
405 pub fn create_scale_xyz(x_scale: f32, y_scale: f32, z_scale: f32) -> Matrix4x4 {
406 let mut result = Matrix4x4::IDENTITY;
407 result.m11 = x_scale;
408 result.m22 = y_scale;
409 result.m33 = z_scale;
410 result
411 }
412
413 #[inline]
415 pub fn create_scale_uniform(scale: f32) -> Matrix4x4 {
416 Matrix4x4::create_scale_xyz(scale, scale, scale)
417 }
418
419 pub fn create_scale_at(scales: Vector3, center_point: Vector3) -> Matrix4x4 {
421 let tx = center_point.x * (1.0 - scales.x);
422 let ty = center_point.y * (1.0 - scales.y);
423 let tz = center_point.z * (1.0 - scales.z);
424
425 let mut result = Matrix4x4::IDENTITY;
426 result.m11 = scales.x;
427 result.m22 = scales.y;
428 result.m33 = scales.z;
429 result.m41 = tx;
430 result.m42 = ty;
431 result.m43 = tz;
432 result
433 }
434
435 #[inline]
437 pub fn create_translation(position: Vector3) -> Matrix4x4 {
438 Matrix4x4::create_translation_xyz(position.x, position.y, position.z)
439 }
440
441 #[inline]
443 pub fn create_translation_xyz(x_position: f32, y_position: f32, z_position: f32) -> Matrix4x4 {
444 let mut result = Matrix4x4::IDENTITY;
445 result.m41 = x_position;
446 result.m42 = y_position;
447 result.m43 = z_position;
448 result
449 }
450
451 pub fn get_determinant(&self) -> f32 {
453 let a = self.m11;
458 let b = self.m12;
459 let c = self.m13;
460 let d = self.m14;
461 let e = self.m21;
462 let f = self.m22;
463 let g = self.m23;
464 let h = self.m24;
465 let i = self.m31;
466 let j = self.m32;
467 let k = self.m33;
468 let l = self.m34;
469 let m = self.m41;
470 let n = self.m42;
471 let o = self.m43;
472 let p = self.m44;
473
474 let kp_lo = k * p - l * o;
475 let jp_ln = j * p - l * n;
476 let jo_kn = j * o - k * n;
477 let ip_lm = i * p - l * m;
478 let io_km = i * o - k * m;
479 let in_jm = i * n - j * m;
480
481 a * (f * kp_lo - g * jp_ln + h * jo_kn) - b * (e * kp_lo - g * ip_lm + h * io_km)
482 + c * (e * jp_ln - f * ip_lm + h * in_jm)
483 - d * (e * jo_kn - f * io_km + g * in_jm)
484 }
485
486 #[allow(clippy::field_reassign_with_default)]
490 pub fn invert(matrix: Matrix4x4) -> Option<Matrix4x4> {
491 let a = matrix.m11;
492 let b = matrix.m12;
493 let c = matrix.m13;
494 let d = matrix.m14;
495 let e = matrix.m21;
496 let f = matrix.m22;
497 let g = matrix.m23;
498 let h = matrix.m24;
499 let i = matrix.m31;
500 let j = matrix.m32;
501 let k = matrix.m33;
502 let l = matrix.m34;
503 let m = matrix.m41;
504 let n = matrix.m42;
505 let o = matrix.m43;
506 let p = matrix.m44;
507
508 let kp_lo = k * p - l * o;
509 let jp_ln = j * p - l * n;
510 let jo_kn = j * o - k * n;
511 let ip_lm = i * p - l * m;
512 let io_km = i * o - k * m;
513 let in_jm = i * n - j * m;
514
515 let a11 = f * kp_lo - g * jp_ln + h * jo_kn;
516 let a12 = -(e * kp_lo - g * ip_lm + h * io_km);
517 let a13 = e * jp_ln - f * ip_lm + h * in_jm;
518 let a14 = -(e * jo_kn - f * io_km + g * in_jm);
519
520 let det = a * a11 + b * a12 + c * a13 + d * a14;
521
522 if det.abs() < f32::from_bits(1) {
525 return None;
526 }
527
528 let inv_det = 1.0 / det;
529
530 let mut result = Matrix4x4::default();
531
532 result.m11 = a11 * inv_det;
533 result.m21 = a12 * inv_det;
534 result.m31 = a13 * inv_det;
535 result.m41 = a14 * inv_det;
536
537 result.m12 = -(b * kp_lo - c * jp_ln + d * jo_kn) * inv_det;
538 result.m22 = (a * kp_lo - c * ip_lm + d * io_km) * inv_det;
539 result.m32 = -(a * jp_ln - b * ip_lm + d * in_jm) * inv_det;
540 result.m42 = (a * jo_kn - b * io_km + c * in_jm) * inv_det;
541
542 let gp_ho = g * p - h * o;
543 let fp_hn = f * p - h * n;
544 let fo_gn = f * o - g * n;
545 let ep_hm = e * p - h * m;
546 let eo_gm = e * o - g * m;
547 let en_fm = e * n - f * m;
548
549 result.m13 = (b * gp_ho - c * fp_hn + d * fo_gn) * inv_det;
550 result.m23 = -(a * gp_ho - c * ep_hm + d * eo_gm) * inv_det;
551 result.m33 = (a * fp_hn - b * ep_hm + d * en_fm) * inv_det;
552 result.m43 = -(a * fo_gn - b * eo_gm + c * en_fm) * inv_det;
553
554 let gl_hk = g * l - h * k;
555 let fl_hj = f * l - h * j;
556 let fk_gj = f * k - g * j;
557 let el_hi = e * l - h * i;
558 let ek_gi = e * k - g * i;
559 let ej_fi = e * j - f * i;
560
561 result.m14 = -(b * gl_hk - c * fl_hj + d * fk_gj) * inv_det;
562 result.m24 = (a * gl_hk - c * el_hi + d * ek_gi) * inv_det;
563 result.m34 = -(a * fl_hj - b * el_hi + d * ej_fi) * inv_det;
564 result.m44 = (a * fk_gj - b * ek_gi + c * ej_fi) * inv_det;
565
566 Some(result)
567 }
568
569 pub fn invert_or_nan(matrix: Matrix4x4) -> Matrix4x4 {
572 const NAN: f32 = f32::NAN;
573 Matrix4x4::invert(matrix).unwrap_or(Matrix4x4::new(
574 NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN,
575 ))
576 }
577
578 pub fn lerp(matrix1: Matrix4x4, matrix2: Matrix4x4, amount: f32) -> Matrix4x4 {
581 Matrix4x4::new(
582 matrix1.m11 + (matrix2.m11 - matrix1.m11) * amount,
583 matrix1.m12 + (matrix2.m12 - matrix1.m12) * amount,
584 matrix1.m13 + (matrix2.m13 - matrix1.m13) * amount,
585 matrix1.m14 + (matrix2.m14 - matrix1.m14) * amount,
586 matrix1.m21 + (matrix2.m21 - matrix1.m21) * amount,
587 matrix1.m22 + (matrix2.m22 - matrix1.m22) * amount,
588 matrix1.m23 + (matrix2.m23 - matrix1.m23) * amount,
589 matrix1.m24 + (matrix2.m24 - matrix1.m24) * amount,
590 matrix1.m31 + (matrix2.m31 - matrix1.m31) * amount,
591 matrix1.m32 + (matrix2.m32 - matrix1.m32) * amount,
592 matrix1.m33 + (matrix2.m33 - matrix1.m33) * amount,
593 matrix1.m34 + (matrix2.m34 - matrix1.m34) * amount,
594 matrix1.m41 + (matrix2.m41 - matrix1.m41) * amount,
595 matrix1.m42 + (matrix2.m42 - matrix1.m42) * amount,
596 matrix1.m43 + (matrix2.m43 - matrix1.m43) * amount,
597 matrix1.m44 + (matrix2.m44 - matrix1.m44) * amount,
598 )
599 }
600
601 pub fn transpose(matrix: Matrix4x4) -> Matrix4x4 {
603 Matrix4x4::new(
604 matrix.m11, matrix.m21, matrix.m31, matrix.m41, matrix.m12, matrix.m22, matrix.m32, matrix.m42,
605 matrix.m13, matrix.m23, matrix.m33, matrix.m43, matrix.m14, matrix.m24, matrix.m34, matrix.m44,
606 )
607 }
608
609 fn to_array(self) -> [f32; 16] {
610 [
611 self.m11, self.m12, self.m13, self.m14, self.m21, self.m22, self.m23, self.m24, self.m31, self.m32,
612 self.m33, self.m34, self.m41, self.m42, self.m43, self.m44,
613 ]
614 }
615
616 fn from_array(v: [f32; 16]) -> Matrix4x4 {
617 Matrix4x4::new(
618 v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14],
619 v[15],
620 )
621 }
622}
623
624impl Add for Matrix4x4 {
625 type Output = Matrix4x4;
626 fn add(self, value2: Matrix4x4) -> Matrix4x4 {
627 let (a, b) = (self.to_array(), value2.to_array());
628 Matrix4x4::from_array(std::array::from_fn(|i| a[i] + b[i]))
629 }
630}
631
632impl Sub for Matrix4x4 {
633 type Output = Matrix4x4;
634 fn sub(self, value2: Matrix4x4) -> Matrix4x4 {
635 let (a, b) = (self.to_array(), value2.to_array());
636 Matrix4x4::from_array(std::array::from_fn(|i| a[i] - b[i]))
637 }
638}
639
640impl Neg for Matrix4x4 {
641 type Output = Matrix4x4;
642 fn neg(self) -> Matrix4x4 {
643 let a = self.to_array();
644 Matrix4x4::from_array(std::array::from_fn(|i| -a[i]))
645 }
646}
647
648impl Mul for Matrix4x4 {
649 type Output = Matrix4x4;
650 fn mul(self, value2: Matrix4x4) -> Matrix4x4 {
651 let value1 = self;
652 Matrix4x4::new(
653 value1.m11 * value2.m11 + value1.m12 * value2.m21 + value1.m13 * value2.m31 + value1.m14 * value2.m41,
655 value1.m11 * value2.m12 + value1.m12 * value2.m22 + value1.m13 * value2.m32 + value1.m14 * value2.m42,
656 value1.m11 * value2.m13 + value1.m12 * value2.m23 + value1.m13 * value2.m33 + value1.m14 * value2.m43,
657 value1.m11 * value2.m14 + value1.m12 * value2.m24 + value1.m13 * value2.m34 + value1.m14 * value2.m44,
658 value1.m21 * value2.m11 + value1.m22 * value2.m21 + value1.m23 * value2.m31 + value1.m24 * value2.m41,
660 value1.m21 * value2.m12 + value1.m22 * value2.m22 + value1.m23 * value2.m32 + value1.m24 * value2.m42,
661 value1.m21 * value2.m13 + value1.m22 * value2.m23 + value1.m23 * value2.m33 + value1.m24 * value2.m43,
662 value1.m21 * value2.m14 + value1.m22 * value2.m24 + value1.m23 * value2.m34 + value1.m24 * value2.m44,
663 value1.m31 * value2.m11 + value1.m32 * value2.m21 + value1.m33 * value2.m31 + value1.m34 * value2.m41,
665 value1.m31 * value2.m12 + value1.m32 * value2.m22 + value1.m33 * value2.m32 + value1.m34 * value2.m42,
666 value1.m31 * value2.m13 + value1.m32 * value2.m23 + value1.m33 * value2.m33 + value1.m34 * value2.m43,
667 value1.m31 * value2.m14 + value1.m32 * value2.m24 + value1.m33 * value2.m34 + value1.m34 * value2.m44,
668 value1.m41 * value2.m11 + value1.m42 * value2.m21 + value1.m43 * value2.m31 + value1.m44 * value2.m41,
670 value1.m41 * value2.m12 + value1.m42 * value2.m22 + value1.m43 * value2.m32 + value1.m44 * value2.m42,
671 value1.m41 * value2.m13 + value1.m42 * value2.m23 + value1.m43 * value2.m33 + value1.m44 * value2.m43,
672 value1.m41 * value2.m14 + value1.m42 * value2.m24 + value1.m43 * value2.m34 + value1.m44 * value2.m44,
673 )
674 }
675}
676
677impl Mul<f32> for Matrix4x4 {
679 type Output = Matrix4x4;
680 fn mul(self, value2: f32) -> Matrix4x4 {
681 let a = self.to_array();
682 Matrix4x4::from_array(std::array::from_fn(|i| a[i] * value2))
683 }
684}
685
686impl fmt::Display for Matrix4x4 {
687 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
688 write!(
689 f,
690 "{{ {{M11:{} M12:{} M13:{} M14:{}}} {{M21:{} M22:{} M23:{} M24:{}}} {{M31:{} M32:{} M33:{} M34:{}}} {{M41:{} M42:{} M43:{} M44:{}}} }}",
691 InvariantF32(self.m11),
692 InvariantF32(self.m12),
693 InvariantF32(self.m13),
694 InvariantF32(self.m14),
695 InvariantF32(self.m21),
696 InvariantF32(self.m22),
697 InvariantF32(self.m23),
698 InvariantF32(self.m24),
699 InvariantF32(self.m31),
700 InvariantF32(self.m32),
701 InvariantF32(self.m33),
702 InvariantF32(self.m34),
703 InvariantF32(self.m41),
704 InvariantF32(self.m42),
705 InvariantF32(self.m43),
706 InvariantF32(self.m44)
707 )
708 }
709}