1use super::{Quaternion, Vec2, Vec3, Vec4, EPSILON};
18use bincode::{Decode, Encode};
19use serde::{Deserialize, Serialize};
20use std::ops::{Index, IndexMut, Mul};
21
22#[derive(Debug, Clone, Copy, PartialEq)]
29#[repr(C)]
30pub struct Mat3 {
31 pub cols: [Vec3; 3],
33}
34
35impl Mat3 {
36 pub const IDENTITY: Self = Self {
38 cols: [Vec3::X, Vec3::Y, Vec3::Z],
39 };
40
41 pub const ZERO: Self = Self {
43 cols: [Vec3::ZERO; 3],
44 };
45
46 #[inline]
48 pub fn from_cols(c0: Vec3, c1: Vec3, c2: Vec3) -> Self {
49 Self { cols: [c0, c1, c2] }
50 }
51
52 #[allow(dead_code)]
54 #[inline]
55 fn get_row(&self, index: usize) -> Vec3 {
56 Vec3 {
57 x: self.cols[0].get(index),
58 y: self.cols[1].get(index),
59 z: self.cols[2].get(index),
60 }
61 }
62
63 #[inline]
67 pub fn from_scale_vec2(scale: Vec2) -> Self {
68 Self::from_scale(Vec3::new(scale.x, scale.y, 1.0))
69 }
70
71 #[inline]
73 pub fn from_scale(scale: Vec3) -> Self {
74 Self {
75 cols: [
76 Vec3::new(scale.x, 0.0, 0.0),
77 Vec3::new(0.0, scale.y, 0.0),
78 Vec3::new(0.0, 0.0, scale.z),
79 ],
80 }
81 }
82
83 #[inline]
89 pub fn from_rotation_x(angle_radians: f32) -> Self {
90 let (s, c) = angle_radians.sin_cos();
91 Self {
92 cols: [
93 Vec3::new(1.0, 0.0, 0.0),
94 Vec3::new(0.0, c, s),
95 Vec3::new(0.0, -s, c),
96 ],
97 }
98 }
99
100 #[inline]
106 pub fn from_rotation_y(angle_radians: f32) -> Self {
107 let (s, c) = angle_radians.sin_cos();
108 Self {
109 cols: [
110 Vec3::new(c, 0.0, -s),
111 Vec3::new(0.0, 1.0, 0.0),
112 Vec3::new(s, 0.0, c),
113 ],
114 }
115 }
116
117 #[inline]
123 pub fn from_rotation_z(angle_radians: f32) -> Self {
124 let (s, c) = angle_radians.sin_cos();
125 Self {
126 cols: [
127 Vec3::new(c, s, 0.0),
128 Vec3::new(-s, c, 0.0),
129 Vec3::new(0.0, 0.0, 1.0),
130 ],
131 }
132 }
133
134 #[inline]
141 pub fn from_axis_angle(axis: Vec3, angle_radians: f32) -> Self {
142 let (s, c) = angle_radians.sin_cos();
143 let t = 1.0 - c;
144 let x = axis.x;
145 let y = axis.y;
146 let z = axis.z;
147 Self {
148 cols: [
149 Vec3::new(t * x * x + c, t * x * y + s * z, t * x * z - s * y),
150 Vec3::new(t * y * x - s * z, t * y * y + c, t * y * z + s * x),
151 Vec3::new(t * z * x + s * y, t * z * y - s * x, t * z * z + c),
152 ],
153 }
154 }
155
156 #[inline]
159 pub fn from_quat(q: Quaternion) -> Self {
160 let q = q.normalize();
161 let x = q.x;
162 let y = q.y;
163 let z = q.z;
164 let w = q.w;
165 let x2 = x + x;
166 let y2 = y + y;
167 let z2 = z + z;
168 let xx = x * x2;
169 let xy = x * y2;
170 let xz = x * z2;
171 let yy = y * y2;
172 let yz = y * z2;
173 let zz = z * z2;
174 let wx = w * x2;
175 let wy = w * y2;
176 let wz = w * z2;
177
178 Self::from_cols(
179 Vec3::new(1.0 - (yy + zz), xy + wz, xz - wy),
180 Vec3::new(xy - wz, 1.0 - (xx + zz), yz + wx),
181 Vec3::new(xz + wy, yz - wx, 1.0 - (xx + yy)),
182 )
183 }
184
185 #[inline]
188 pub fn from_mat4(m4: &Mat4) -> Self {
189 Self::from_cols(
190 m4.cols[0].truncate(),
191 m4.cols[1].truncate(),
192 m4.cols[2].truncate(),
193 )
194 }
195
196 #[inline]
201 pub fn determinant(&self) -> f32 {
202 let c0 = self.cols[0];
203 let c1 = self.cols[1];
204 let c2 = self.cols[2];
205 c0.x * (c1.y * c2.z - c2.y * c1.z) - c1.x * (c0.y * c2.z - c2.y * c0.z)
206 + c2.x * (c0.y * c1.z - c1.y * c0.z)
207 }
208
209 #[inline]
211 pub fn transpose(&self) -> Self {
212 Self::from_cols(
213 Vec3::new(self.cols[0].x, self.cols[1].x, self.cols[2].x),
214 Vec3::new(self.cols[0].y, self.cols[1].y, self.cols[2].y),
215 Vec3::new(self.cols[0].z, self.cols[1].z, self.cols[2].z),
216 )
217 }
218
219 pub fn inverse(&self) -> Option<Self> {
224 let c0 = self.cols[0];
225 let c1 = self.cols[1];
226 let c2 = self.cols[2];
227 let m00 = c1.y * c2.z - c2.y * c1.z;
228 let m10 = c2.y * c0.z - c0.y * c2.z;
229 let m20 = c0.y * c1.z - c1.y * c0.z;
230 let det = c0.x * m00 + c1.x * m10 + c2.x * m20;
231
232 if det.abs() < EPSILON {
233 return None;
234 }
235
236 let inv_det = 1.0 / det;
237 let m01 = c2.x * c1.z - c1.x * c2.z;
238 let m11 = c0.x * c2.z - c2.x * c0.z;
239 let m21 = c1.x * c0.z - c0.x * c1.z;
240 let m02 = c1.x * c2.y - c2.x * c1.y;
241 let m12 = c2.x * c0.y - c0.x * c2.y;
242 let m22 = c0.x * c1.y - c1.x * c0.y;
243
244 Some(Self::from_cols(
245 Vec3::new(m00, m10, m20) * inv_det,
246 Vec3::new(m01, m11, m21) * inv_det,
247 Vec3::new(m02, m12, m22) * inv_det,
248 ))
249 }
250
251 #[inline]
254 pub fn to_mat4(&self) -> Mat4 {
255 Mat4::from_cols(
256 Vec4::from_vec3(self.cols[0], 0.0),
257 Vec4::from_vec3(self.cols[1], 0.0),
258 Vec4::from_vec3(self.cols[2], 0.0),
259 Vec4::W,
260 )
261 }
262}
263
264impl Default for Mat3 {
267 #[inline]
269 fn default() -> Self {
270 Self::IDENTITY
271 }
272}
273
274impl Mul<Mat3> for Mat3 {
275 type Output = Self;
276 #[inline]
278 fn mul(self, rhs: Mat3) -> Self::Output {
279 Self::from_cols(self * rhs.cols[0], self * rhs.cols[1], self * rhs.cols[2])
280 }
281}
282
283impl Mul<Vec3> for Mat3 {
284 type Output = Vec3;
285 #[inline]
287 fn mul(self, v: Vec3) -> Self::Output {
288 self.cols[0] * v.x + self.cols[1] * v.y + self.cols[2] * v.z
289 }
290}
291
292impl Index<usize> for Mat3 {
293 type Output = Vec3;
294 #[inline]
296 fn index(&self, index: usize) -> &Self::Output {
297 &self.cols[index]
298 }
299}
300
301impl IndexMut<usize> for Mat3 {
302 #[inline]
304 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
305 &mut self.cols[index]
306 }
307}
308
309#[derive(
340 Debug,
341 Clone,
342 Copy,
343 PartialEq,
344 bytemuck::Pod,
345 bytemuck::Zeroable,
346 Serialize,
347 Deserialize,
348 Encode,
349 Decode,
350)]
351#[repr(C)]
352pub struct Mat4 {
353 pub cols: [Vec4; 4],
355}
356
357impl Mat4 {
358 pub const IDENTITY: Self = Self {
360 cols: [Vec4::X, Vec4::Y, Vec4::Z, Vec4::W],
361 };
362
363 pub const ZERO: Self = Self {
365 cols: [Vec4::ZERO; 4],
366 };
367
368 #[inline]
370 pub fn from_cols(c0: Vec4, c1: Vec4, c2: Vec4, c3: Vec4) -> Self {
371 Self {
372 cols: [c0, c1, c2, c3],
373 }
374 }
375
376 #[inline]
378 pub fn get_row(&self, index: usize) -> Vec4 {
379 Vec4 {
380 x: self.cols[0].get(index),
381 y: self.cols[1].get(index),
382 z: self.cols[2].get(index),
383 w: self.cols[3].get(index),
384 }
385 }
386
387 #[inline]
389 pub const fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
390 [
391 [
392 self.cols[0].x,
393 self.cols[0].y,
394 self.cols[0].z,
395 self.cols[0].w,
396 ],
397 [
398 self.cols[1].x,
399 self.cols[1].y,
400 self.cols[1].z,
401 self.cols[1].w,
402 ],
403 [
404 self.cols[2].x,
405 self.cols[2].y,
406 self.cols[2].z,
407 self.cols[2].w,
408 ],
409 [
410 self.cols[3].x,
411 self.cols[3].y,
412 self.cols[3].z,
413 self.cols[3].w,
414 ],
415 ]
416 }
417
418 #[inline]
424 pub fn from_translation(v: Vec3) -> Self {
425 Self {
426 cols: [
427 Vec4::new(1.0, 0.0, 0.0, 0.0),
428 Vec4::new(0.0, 1.0, 0.0, 0.0),
429 Vec4::new(0.0, 0.0, 1.0, 0.0),
430 Vec4::new(v.x, v.y, v.z, 1.0),
431 ],
432 }
433 }
434
435 #[inline]
437 pub fn from_scale(scale: Vec3) -> Self {
438 Self {
439 cols: [
440 Vec4::new(scale.x, 0.0, 0.0, 0.0),
441 Vec4::new(0.0, scale.y, 0.0, 0.0),
442 Vec4::new(0.0, 0.0, scale.z, 0.0),
443 Vec4::new(0.0, 0.0, 0.0, 1.0),
444 ],
445 }
446 }
447
448 #[inline]
454 pub fn from_rotation_x(angle: f32) -> Self {
455 let c = angle.cos();
456 let s = angle.sin();
457 Self {
458 cols: [
459 Vec4::new(1.0, 0.0, 0.0, 0.0),
460 Vec4::new(0.0, c, s, 0.0),
461 Vec4::new(0.0, -s, c, 0.0),
462 Vec4::new(0.0, 0.0, 0.0, 1.0),
463 ],
464 }
465 }
466
467 #[inline]
473 pub fn from_rotation_y(angle: f32) -> Self {
474 let c = angle.cos();
475 let s = angle.sin();
476 Self {
477 cols: [
478 Vec4::new(c, 0.0, -s, 0.0),
479 Vec4::new(0.0, 1.0, 0.0, 0.0),
480 Vec4::new(s, 0.0, c, 0.0),
481 Vec4::new(0.0, 0.0, 0.0, 1.0),
482 ],
483 }
484 }
485
486 #[inline]
492 pub fn from_rotation_z(angle: f32) -> Self {
493 let c = angle.cos();
494 let s = angle.sin();
495 Self {
496 cols: [
497 Vec4::new(c, s, 0.0, 0.0),
498 Vec4::new(-s, c, 0.0, 0.0),
499 Vec4::new(0.0, 0.0, 1.0, 0.0),
500 Vec4::new(0.0, 0.0, 0.0, 1.0),
501 ],
502 }
503 }
504
505 #[inline]
512 pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
513 let c = angle.cos();
514 let s = angle.sin();
515 let t = 1.0 - c;
516 let x = axis.x;
517 let y = axis.y;
518 let z = axis.z;
519
520 Self {
521 cols: [
522 Vec4::new(t * x * x + c, t * x * y - s * z, t * x * z + s * y, 0.0),
523 Vec4::new(t * y * x + s * z, t * y * y + c, t * y * z - s * x, 0.0),
524 Vec4::new(t * z * x - s * y, t * z * y + s * x, t * z * z + c, 0.0),
525 Vec4::new(0.0, 0.0, 0.0, 1.0),
526 ],
527 }
528 }
529
530 #[inline]
532 pub fn from_quat(q: Quaternion) -> Self {
533 let x = q.x;
534 let y = q.y;
535 let z = q.z;
536 let w = q.w;
537 let x2 = x + x;
538 let y2 = y + y;
539 let z2 = z + z;
540 let xx = x * x2;
541 let xy = x * y2;
542 let xz = x * z2;
543 let yy = y * y2;
544 let yz = y * z2;
545 let zz = z * z2;
546 let wx = w * x2;
547 let wy = w * y2;
548 let wz = w * z2;
549
550 Self::from_cols(
551 Vec4::new(1.0 - (yy + zz), xy + wz, xz - wy, 0.0),
552 Vec4::new(xy - wz, 1.0 - (xx + zz), yz + wx, 0.0),
553 Vec4::new(xz + wy, yz - wx, 1.0 - (xx + yy), 0.0),
554 Vec4::W,
555 )
556 }
557
558 #[inline]
567 pub fn perspective_rh_zo(
568 fov_y_radians: f32,
569 aspect_ratio: f32,
570 z_near: f32,
571 z_far: f32,
572 ) -> Self {
573 assert!(z_near > 0.0 && z_far > z_near);
574 let tan_half_fovy = (fov_y_radians / 2.0).tan();
575 let f = 1.0 / tan_half_fovy;
576 let aa = f / aspect_ratio;
577 let bb = f;
578 let cc = z_far / (z_near - z_far);
579 let dd = (z_near * z_far) / (z_near - z_far);
580
581 Self::from_cols(
582 Vec4::new(aa, 0.0, 0.0, 0.0),
583 Vec4::new(0.0, bb, 0.0, 0.0),
584 Vec4::new(0.0, 0.0, cc, -1.0),
585 Vec4::new(0.0, 0.0, dd, 0.0),
586 )
587 }
588
589 #[inline]
591 pub fn orthographic_rh_zo(
592 left: f32,
593 right: f32,
594 bottom: f32,
595 top: f32,
596 z_near: f32,
597 z_far: f32,
598 ) -> Self {
599 let rml = right - left;
600 let rpl = right + left;
601 let tmb = top - bottom;
602 let tpb = top + bottom;
603 let fmn = z_far - z_near;
604 let aa = 2.0 / rml;
605 let bb = 2.0 / tmb;
606 let cc = -1.0 / fmn;
607 let dd = -rpl / rml;
608 let ee = -tpb / tmb;
609 let ff = -z_near / fmn;
610
611 Self::from_cols(
612 Vec4::new(aa, 0.0, 0.0, 0.0),
613 Vec4::new(0.0, bb, 0.0, 0.0),
614 Vec4::new(0.0, 0.0, cc, 0.0),
615 Vec4::new(dd, ee, ff, 1.0),
616 )
617 }
618
619 #[inline]
632 pub fn look_at_rh(eye: Vec3, target: Vec3, up: Vec3) -> Option<Self> {
633 let forward = target - eye;
634 if forward.length_squared() < crate::math::EPSILON * crate::math::EPSILON {
635 return None;
636 }
637 let f = forward.normalize();
638 let s = f.cross(up);
639 if s.length_squared() < crate::math::EPSILON * crate::math::EPSILON {
640 return None;
641 }
642 let s = s.normalize();
643 let u = s.cross(f);
644
645 Some(Self::from_cols(
646 Vec4::new(s.x, u.x, -f.x, 0.0),
647 Vec4::new(s.y, u.y, -f.y, 0.0),
648 Vec4::new(s.z, u.z, -f.z, 0.0),
649 Vec4::new(-eye.dot(s), -eye.dot(u), eye.dot(f), 1.0),
650 ))
651 }
652
653 pub fn cube_face_view_proj(
665 position: crate::math::Vec3,
666 face: crate::math::CubeFace,
667 far: f32,
668 ) -> Mat4 {
669 const NEAR: f32 = 0.1;
670 let proj = Mat4::perspective_rh_zo(crate::math::FRAC_PI_2, 1.0, NEAR, far);
671 let view = Mat4::look_at_rh(position, position + face.forward(), face.up())
672 .unwrap_or(Mat4::IDENTITY);
673 proj * view
674 }
675
676 pub fn cube_face_view_projs(position: crate::math::Vec3, far: f32) -> [Mat4; 6] {
685 crate::math::CubeFace::ALL.map(|f| Mat4::cube_face_view_proj(position, f, far))
686 }
687
688 #[inline]
690 pub fn transpose(&self) -> Self {
691 Self::from_cols(
692 Vec4::new(
693 self.cols[0].x,
694 self.cols[1].x,
695 self.cols[2].x,
696 self.cols[3].x,
697 ),
698 Vec4::new(
699 self.cols[0].y,
700 self.cols[1].y,
701 self.cols[2].y,
702 self.cols[3].y,
703 ),
704 Vec4::new(
705 self.cols[0].z,
706 self.cols[1].z,
707 self.cols[2].z,
708 self.cols[3].z,
709 ),
710 Vec4::new(
711 self.cols[0].w,
712 self.cols[1].w,
713 self.cols[2].w,
714 self.cols[3].w,
715 ),
716 )
717 }
718
719 pub fn determinant(&self) -> f32 {
721 let c0 = self.cols[0];
722 let c1 = self.cols[1];
723 let c2 = self.cols[2];
724 let c3 = self.cols[3];
725
726 let m00 = c1.y * (c2.z * c3.w - c3.z * c2.w) - c2.y * (c1.z * c3.w - c3.z * c1.w)
727 + c3.y * (c1.z * c2.w - c2.z * c1.w);
728 let m01 = c0.y * (c2.z * c3.w - c3.z * c2.w) - c2.y * (c0.z * c3.w - c3.z * c0.w)
729 + c3.y * (c0.z * c2.w - c2.z * c0.w);
730 let m02 = c0.y * (c1.z * c3.w - c3.z * c1.w) - c1.y * (c0.z * c3.w - c3.z * c0.w)
731 + c3.y * (c0.z * c1.w - c1.z * c0.w);
732 let m03 = c0.y * (c1.z * c2.w - c2.z * c1.w) - c1.y * (c0.z * c2.w - c2.z * c0.w)
733 + c2.y * (c0.z * c1.w - c1.z * c0.w);
734
735 c0.x * m00 - c1.x * m01 + c2.x * m02 - c3.x * m03
736 }
737
738 pub fn inverse(&self) -> Option<Self> {
741 let c0 = self.cols[0];
742 let c1 = self.cols[1];
743 let c2 = self.cols[2];
744 let c3 = self.cols[3];
745
746 let a00 = c1.y * (c2.z * c3.w - c3.z * c2.w) - c2.y * (c1.z * c3.w - c3.z * c1.w)
747 + c3.y * (c1.z * c2.w - c2.z * c1.w);
748 let a01 = -(c1.x * (c2.z * c3.w - c3.z * c2.w) - c2.x * (c1.z * c3.w - c3.z * c1.w)
749 + c3.x * (c1.z * c2.w - c2.z * c1.w));
750 let a02 = c1.x * (c2.y * c3.w - c3.y * c2.w) - c2.x * (c1.y * c3.w - c3.y * c1.w)
751 + c3.x * (c1.y * c2.w - c2.y * c1.w);
752 let a03 = -(c1.x * (c2.y * c3.z - c3.y * c2.z) - c2.x * (c1.y * c3.z - c3.y * c1.z)
753 + c3.x * (c1.y * c2.z - c2.y * c1.z));
754
755 let a10 = -(c0.y * (c2.z * c3.w - c3.z * c2.w) - c2.y * (c0.z * c3.w - c3.z * c0.w)
756 + c3.y * (c0.z * c2.w - c2.z * c0.w));
757 let a11 = c0.x * (c2.z * c3.w - c3.z * c2.w) - c2.x * (c0.z * c3.w - c3.z * c0.w)
758 + c3.x * (c0.z * c2.w - c2.z * c0.w);
759 let a12 = -(c0.x * (c2.y * c3.w - c3.y * c2.w) - c2.x * (c0.y * c3.w - c3.y * c0.w)
760 + c3.x * (c0.y * c2.w - c2.y * c0.w));
761 let a13 = c0.x * (c2.y * c3.z - c3.y * c2.z) - c2.x * (c0.y * c3.z - c3.y * c0.z)
762 + c3.x * (c0.y * c2.z - c2.y * c0.z);
763
764 let a20 = c0.y * (c1.z * c3.w - c3.z * c1.w) - c1.y * (c0.z * c3.w - c3.z * c0.w)
765 + c3.y * (c0.z * c1.w - c1.z * c0.w);
766 let a21 = -(c0.x * (c1.z * c3.w - c3.z * c1.w) - c1.x * (c0.z * c3.w - c3.z * c0.w)
767 + c3.x * (c0.z * c1.w - c1.z * c0.w));
768 let a22 = c0.x * (c1.y * c3.w - c3.y * c1.w) - c1.x * (c0.y * c3.w - c3.y * c0.w)
769 + c3.x * (c0.y * c1.w - c1.y * c0.w);
770 let a23 = -(c0.x * (c1.y * c3.z - c3.y * c1.z) - c1.x * (c0.y * c3.z - c3.y * c0.z)
771 + c3.x * (c0.y * c1.z - c1.y * c0.z));
772
773 let a30 = -(c0.y * (c1.z * c2.w - c2.z * c1.w) - c1.y * (c0.z * c2.w - c2.z * c0.w)
774 + c2.y * (c0.z * c1.w - c1.z * c0.w));
775 let a31 = c0.x * (c1.z * c2.w - c2.z * c1.w) - c1.x * (c0.z * c2.w - c2.z * c0.w)
776 + c2.x * (c0.z * c1.w - c1.z * c0.w);
777 let a32 = -(c0.x * (c1.y * c2.w - c2.y * c1.w) - c1.x * (c0.y * c2.w - c2.y * c0.w)
778 + c2.x * (c0.y * c1.w - c1.y * c0.w));
779 let a33 = c0.x * (c1.y * c2.z - c2.y * c1.z) - c1.x * (c0.y * c2.z - c2.y * c0.z)
780 + c2.x * (c0.y * c1.z - c1.y * c0.z);
781
782 let det = c0.x * a00 + c1.x * a10 + c2.x * a20 + c3.x * a30;
783 if det.abs() < crate::math::EPSILON {
784 return None;
785 }
786 let inv_det = 1.0 / det;
787
788 Some(Self::from_cols(
789 Vec4::new(a00 * inv_det, a10 * inv_det, a20 * inv_det, a30 * inv_det),
790 Vec4::new(a01 * inv_det, a11 * inv_det, a21 * inv_det, a31 * inv_det),
791 Vec4::new(a02 * inv_det, a12 * inv_det, a22 * inv_det, a32 * inv_det),
792 Vec4::new(a03 * inv_det, a13 * inv_det, a23 * inv_det, a33 * inv_det),
793 ))
794 }
795
796 #[inline]
805 pub fn affine_inverse(&self) -> Option<Self> {
806 let c0 = self.cols[0].truncate();
807 let c1 = self.cols[1].truncate();
808 let c2 = self.cols[2].truncate();
809 let translation = self.cols[3].truncate();
810 let det3x3 = c0.x * (c1.y * c2.z - c2.y * c1.z) - c1.x * (c0.y * c2.z - c2.y * c0.z)
811 + c2.x * (c0.y * c1.z - c1.y * c0.z);
812
813 if det3x3.abs() < crate::math::EPSILON {
814 return None;
815 }
816
817 let inv_det3x3 = 1.0 / det3x3;
818 let inv00 = (c1.y * c2.z - c2.y * c1.z) * inv_det3x3;
819 let inv10 = -(c2.y * c0.z - c0.y * c2.z) * inv_det3x3;
820 let inv20 = (c0.y * c1.z - c1.y * c0.z) * inv_det3x3;
821 let inv01 = -(c2.x * c1.z - c1.x * c2.z) * inv_det3x3;
822 let inv11 = (c0.x * c2.z - c2.x * c0.z) * inv_det3x3;
823 let inv21 = -(c1.x * c0.z - c0.x * c1.z) * inv_det3x3;
824 let inv02 = (c1.x * c2.y - c2.x * c1.y) * inv_det3x3;
825 let inv12 = -(c2.x * c0.y - c0.x * c2.y) * inv_det3x3;
826 let inv22 = (c0.x * c1.y - c1.x * c0.y) * inv_det3x3;
827 let inv_tx = -(inv00 * translation.x + inv01 * translation.y + inv02 * translation.z);
828 let inv_ty = -(inv10 * translation.x + inv11 * translation.y + inv12 * translation.z);
829 let inv_tz = -(inv20 * translation.x + inv21 * translation.y + inv22 * translation.z);
830
831 Some(Self::from_cols(
832 Vec4::new(inv00, inv10, inv20, 0.0),
833 Vec4::new(inv01, inv11, inv21, 0.0),
834 Vec4::new(inv02, inv12, inv22, 0.0),
835 Vec4::new(inv_tx, inv_ty, inv_tz, 1.0),
836 ))
837 }
838
839 #[inline]
843 pub fn transform_point(&self, p: Vec3) -> Vec3 {
844 (*self * Vec4::from_vec3(p, 1.0)).truncate()
845 }
846
847 #[inline]
852 pub fn transform_vector(&self, v: Vec3) -> Vec3 {
853 (*self * Vec4::from_vec3(v, 0.0)).truncate()
854 }
855}
856
857impl Default for Mat4 {
860 #[inline]
862 fn default() -> Self {
863 Self::IDENTITY
864 }
865}
866
867impl Mul<Mat4> for Mat4 {
868 type Output = Self;
869 #[inline]
871 fn mul(self, rhs: Mat4) -> Self::Output {
872 let mut result_cols = [Vec4 {
873 x: 0.0,
874 y: 0.0,
875 z: 0.0,
876 w: 0.0,
877 }; 4];
878 for (c_idx, target_col_ref_mut) in result_cols.iter_mut().enumerate().take(4) {
879 let col_from_rhs = rhs.cols[c_idx];
880 *target_col_ref_mut = Vec4 {
881 x: self.get_row(0).dot(col_from_rhs),
882 y: self.get_row(1).dot(col_from_rhs),
883 z: self.get_row(2).dot(col_from_rhs),
884 w: self.get_row(3).dot(col_from_rhs),
885 };
886 }
887 Mat4 { cols: result_cols }
888 }
889}
890
891impl Mul<Vec4> for Mat4 {
892 type Output = Vec4;
893 #[inline]
895 fn mul(self, rhs: Vec4) -> Self::Output {
896 self.cols[0] * rhs.x + self.cols[1] * rhs.y + self.cols[2] * rhs.z + self.cols[3] * rhs.w
897 }
898}
899
900#[cfg(test)]
903mod tests {
904 use super::*;
905 use crate::math::{approx_eq, matrix::Mat4, quaternion::Quaternion, vector::Vec3, PI};
906
907 fn vec3_approx_eq(a: Vec3, b: Vec3) -> bool {
908 approx_eq(a.x, b.x) && approx_eq(a.y, b.y) && approx_eq(a.z, b.z)
909 }
910
911 fn mat3_approx_eq(a: Mat3, b: Mat3) -> bool {
912 vec3_approx_eq(a.cols[0], b.cols[0])
913 && vec3_approx_eq(a.cols[1], b.cols[1])
914 && vec3_approx_eq(a.cols[2], b.cols[2])
915 }
916
917 fn vec4_approx_eq(a: Vec4, b: Vec4) -> bool {
918 approx_eq(a.x, b.x) && approx_eq(a.y, b.y) && approx_eq(a.z, b.z) && approx_eq(a.w, b.w)
919 }
920
921 fn mat4_approx_eq(a: Mat4, b: Mat4) -> bool {
922 vec4_approx_eq(a.cols[0], b.cols[0])
923 && vec4_approx_eq(a.cols[1], b.cols[1])
924 && vec4_approx_eq(a.cols[2], b.cols[2])
925 && vec4_approx_eq(a.cols[3], b.cols[3])
926 }
927
928 #[test]
931 fn test_mat3_identity_default() {
932 assert_eq!(Mat3::default(), Mat3::IDENTITY);
933
934 let m = Mat3::from_scale(Vec3::new(1.0, 2.0, 3.0));
935 assert!(mat3_approx_eq(m * Mat3::IDENTITY, m));
936 assert!(mat3_approx_eq(Mat3::IDENTITY * m, m));
937 }
938
939 #[test]
940 fn test_mat3_from_scale() {
941 let s = Vec3::new(2.0, -3.0, 0.5);
942 let m = Mat3::from_scale(s);
943 let v = Vec3::new(1.0, 1.0, 1.0);
944 assert!(vec3_approx_eq(m * v, s)); }
946
947 #[test]
948 fn test_mat3_rotations() {
949 let angle = PI / 6.0; let mx = Mat3::from_rotation_x(angle);
951 let my = Mat3::from_rotation_y(angle);
952 let mz = Mat3::from_rotation_z(angle);
953
954 let p = Vec3::Y; let expected_px = Vec3::new(0.0, angle.cos(), angle.sin());
956 assert!(vec3_approx_eq(mx * p, expected_px));
957
958 let p = Vec3::X; let expected_py = Vec3::new(angle.cos(), 0.0, -angle.sin()); assert!(vec3_approx_eq(my * p, expected_py));
961
962 let p = Vec3::X; let expected_pz = Vec3::new(angle.cos(), angle.sin(), 0.0);
964 assert!(vec3_approx_eq(mz * p, expected_pz));
965 }
966
967 #[test]
968 fn test_mat3_from_axis_angle() {
969 let axis = Vec3::new(1.0, 1.0, 1.0).normalize();
970 let angle = 1.2 * PI;
971 let m = Mat3::from_axis_angle(axis, angle);
972
973 let v = Vec3::X;
975 let v_rotated = m * v;
976
977 assert!(approx_eq(v_rotated.length(), v.length()));
979
980 assert!(v_rotated.distance_squared(v) > EPSILON);
982 }
983
984 #[test]
985 fn test_mat3_from_quat() {
986 let axis = Vec3::new(1.0, -2.0, 3.0).normalize();
987 let angle = PI / 7.0;
988 let q = Quaternion::from_axis_angle(axis, angle);
989 let m_from_q = Mat3::from_quat(q);
990
991 let v = Vec3::new(0.5, 1.0, -0.2);
992 let v_rotated_q = q * v;
993 let v_rotated_m = m_from_q * v;
994
995 assert!(vec3_approx_eq(v_rotated_q, v_rotated_m));
996 }
997
998 #[test]
999 fn test_mat3_determinant() {
1000 assert!(approx_eq(Mat3::IDENTITY.determinant(), 1.0));
1001 assert!(approx_eq(Mat3::ZERO.determinant(), 0.0));
1002
1003 let m_scale = Mat3::from_scale(Vec3::new(2.0, 3.0, 4.0));
1004 assert!(approx_eq(m_scale.determinant(), 24.0));
1005
1006 let m_rot = Mat3::from_rotation_y(PI / 5.0);
1007 assert!(approx_eq(m_rot.determinant(), 1.0)); }
1009
1010 #[test]
1011 fn test_mat3_transpose() {
1012 let m = Mat3::from_cols(
1013 Vec3::new(1.0, 2.0, 3.0),
1014 Vec3::new(4.0, 5.0, 6.0),
1015 Vec3::new(7.0, 8.0, 9.0),
1016 );
1017 let mt = m.transpose();
1018 let expected_mt = Mat3::from_cols(
1019 Vec3::new(1.0, 4.0, 7.0),
1020 Vec3::new(2.0, 5.0, 8.0),
1021 Vec3::new(3.0, 6.0, 9.0),
1022 );
1023
1024 assert!(mat3_approx_eq(mt, expected_mt));
1025 assert!(mat3_approx_eq(m.transpose().transpose(), m)); }
1027
1028 #[test]
1029 fn test_mat3_inverse() {
1030 let m = Mat3::from_rotation_z(PI / 3.0) * Mat3::from_scale(Vec3::new(1.0, 2.0, 0.5));
1031 let inv_m = m.inverse().expect("Matrix should be invertible");
1032 let identity = m * inv_m;
1033 assert!(
1034 mat3_approx_eq(identity, Mat3::IDENTITY),
1035 "M * inv(M) should be Identity"
1036 );
1037
1038 let singular = Mat3::from_scale(Vec3::new(1.0, 0.0, 1.0));
1039 assert!(
1040 singular.inverse().is_none(),
1041 "Singular matrix inverse should be None"
1042 );
1043 }
1044
1045 #[test]
1046 fn test_mat3_mul_vec3() {
1047 let m = Mat3::from_rotation_z(PI / 2.0); let v = Vec3::X; let expected_v = Vec3::Y; assert!(vec3_approx_eq(m * v, expected_v));
1051 }
1052
1053 #[test]
1054 fn test_mat3_mul_mat3() {
1055 let rot90z = Mat3::from_rotation_z(PI / 2.0);
1056 let rot180z = rot90z * rot90z;
1057 let expected_rot180z = Mat3::from_rotation_z(PI);
1058 assert!(mat3_approx_eq(rot180z, expected_rot180z));
1059 }
1060
1061 #[test]
1062 fn test_mat3_conversions() {
1063 let m4 = Mat4::from_translation(Vec3::new(10., 20., 30.)) * Mat4::from_rotation_x(PI / 4.0);
1064 let m3 = Mat3::from_mat4(&m4);
1065 let m4_again = m3.to_mat4();
1066
1067 let v = Vec3::Y;
1069 let v_rot_m3 = m3 * v;
1070 let v_rot_m4 = Mat4::from_rotation_x(PI / 4.0) * Vec4::from_vec3(v, 0.0); assert!(vec3_approx_eq(v_rot_m3, v_rot_m4.truncate()));
1072
1073 let origin = Vec4::new(0.0, 0.0, 0.0, 1.0);
1075 let transformed_origin = m4_again * origin;
1076 assert!(approx_eq(transformed_origin.x, 0.0));
1077 assert!(approx_eq(transformed_origin.y, 0.0));
1078 assert!(approx_eq(transformed_origin.z, 0.0));
1079 assert!(approx_eq(transformed_origin.w, 1.0));
1080 }
1081
1082 #[test]
1083 fn test_mat3_index() {
1084 let mut m = Mat3::from_cols(Vec3::X, Vec3::Y, Vec3::Z);
1085 assert_eq!(m[0], Vec3::X);
1086 assert_eq!(m[1], Vec3::Y);
1087 assert_eq!(m[2], Vec3::Z);
1088 m[0] = Vec3::ONE;
1089 assert_eq!(m.cols[0], Vec3::ONE);
1090 }
1091
1092 #[test]
1093 #[should_panic]
1094 fn test_mat3_index_out_of_bounds() {
1095 let m = Mat3::IDENTITY;
1096 let _ = m[3]; }
1098
1099 #[test]
1104 fn test_identity() {
1105 assert_eq!(Mat4::default(), Mat4::IDENTITY);
1106 let m = Mat4::from_translation(Vec3::new(1.0, 2.0, 3.0));
1107 assert!(mat4_approx_eq(m * Mat4::IDENTITY, m));
1108 assert!(mat4_approx_eq(Mat4::IDENTITY * m, m));
1109 }
1110
1111 #[test]
1112 fn test_from_quat() {
1113 let axis = Vec3::new(1.0, 2.0, 3.0).normalize();
1114 let angle = PI / 5.0;
1115 let q = Quaternion::from_axis_angle(axis, angle);
1116 let m_from_q = Mat4::from_quat(q);
1117
1118 let v = Vec3::new(5.0, -1.0, 2.0);
1119
1120 let v_rotated_q = q * v; let v4 = Vec4::from_vec3(v, 1.0);
1123 let v_rotated_m4 = m_from_q * v4;
1124 let v_rotated_m = v_rotated_m4.truncate();
1125
1126 assert!(approx_eq(v_rotated_q.x, v_rotated_m.x));
1128 assert!(approx_eq(v_rotated_q.y, v_rotated_m.y));
1129 assert!(approx_eq(v_rotated_q.z, v_rotated_m.z));
1130 }
1131
1132 #[test]
1133 fn test_translation() {
1134 let t = Vec3::new(1.0, 2.0, 3.0);
1135 let m = Mat4::from_translation(t);
1136 let p = Vec4::new(1.0, 1.0, 1.0, 1.0);
1137 let expected_p = Vec4::new(2.0, 3.0, 4.0, 1.0);
1138
1139 assert!(vec4_approx_eq(m * p, expected_p));
1140 }
1141
1142 #[test]
1143 fn test_scale() {
1144 let s = Vec3::new(2.0, 3.0, 4.0);
1145 let m = Mat4::from_scale(s);
1146 let p = Vec4::new(1.0, 1.0, 1.0, 1.0);
1147 let expected_p = Vec4::new(2.0, 3.0, 4.0, 1.0);
1148 assert!(vec4_approx_eq(m * p, expected_p));
1149 }
1150
1151 #[test]
1152 fn test_rotation_x() {
1153 let angle = PI / 2.0; let m = Mat4::from_rotation_x(angle);
1155 let p = Vec4::new(0.0, 1.0, 0.0, 1.0); let expected_p = Vec4::new(0.0, 0.0, 1.0, 1.0); assert!(vec4_approx_eq(m * p, expected_p));
1158 }
1159
1160 #[test]
1161 fn test_rotation_y() {
1162 let angle = PI / 2.0; let m = Mat4::from_rotation_y(angle);
1164 let p = Vec4::new(1.0, 0.0, 0.0, 1.0); let expected_p = Vec4::new(0.0, 0.0, -1.0, 1.0); assert!(vec4_approx_eq(m * p, expected_p));
1168 }
1169
1170 #[test]
1171 fn test_rotation_z() {
1172 let angle = PI / 2.0; let m = Mat4::from_rotation_z(angle);
1174 let p = Vec4::new(1.0, 0.0, 0.0, 1.0); let expected_p = Vec4::new(0.0, 1.0, 0.0, 1.0); assert!(vec4_approx_eq(m * p, expected_p));
1177 }
1178
1179 #[test]
1180 fn test_transpose() {
1181 let m = Mat4::from_cols(
1182 Vec4::new(1., 2., 3., 4.),
1183 Vec4::new(5., 6., 7., 8.),
1184 Vec4::new(9., 10., 11., 12.),
1185 Vec4::new(13., 14., 15., 16.),
1186 );
1187 let mt = m.transpose();
1188 let expected_mt = Mat4::from_cols(
1189 Vec4::new(1., 5., 9., 13.),
1190 Vec4::new(2., 6., 10., 14.),
1191 Vec4::new(3., 7., 11., 15.),
1192 Vec4::new(4., 8., 12., 16.),
1193 );
1194 assert_eq!(mt.cols[0], expected_mt.cols[0]); assert_eq!(mt.cols[1], expected_mt.cols[1]);
1196 assert_eq!(mt.cols[2], expected_mt.cols[2]);
1197 assert_eq!(mt.cols[3], expected_mt.cols[3]);
1198
1199 assert!(mat4_approx_eq(m.transpose().transpose(), m));
1201 }
1202
1203 #[test]
1204 fn test_mul_mat4() {
1205 let t = Mat4::from_translation(Vec3::new(1.0, 0.0, 0.0));
1206 let r = Mat4::from_rotation_z(PI / 2.0);
1207
1208 let tr = r * t;
1210 let p = Vec4::new(1.0, 0.0, 0.0, 1.0); let expected_tr = Vec4::new(0.0, 2.0, 0.0, 1.0);
1214 assert!(vec4_approx_eq(tr * p, expected_tr));
1215
1216 let rt = t * r;
1218 let expected_rt = Vec4::new(1.0, 1.0, 0.0, 1.0);
1221 assert!(vec4_approx_eq(rt * p, expected_rt));
1222 }
1223
1224 #[test]
1225 fn test_inverse() {
1226 let m = Mat4::from_translation(Vec3::new(1., 2., 3.))
1227 * Mat4::from_rotation_y(PI / 4.0)
1228 * Mat4::from_scale(Vec3::new(1., 2., 1.));
1229
1230 let inv_m = m.inverse().expect("Matrix should be invertible");
1231 let identity = m * inv_m;
1232
1233 assert!(
1235 mat4_approx_eq(identity, Mat4::IDENTITY),
1236 "M * inv(M) should be Identity"
1237 );
1238
1239 let singular = Mat4::from_scale(Vec3::new(1.0, 0.0, 1.0));
1241 assert!(
1242 singular.inverse().is_none(),
1243 "Singular matrix inverse should be None"
1244 );
1245 }
1246
1247 #[test]
1248 fn test_affine_inverse() {
1249 let t = Mat4::from_translation(Vec3::new(1., 2., 3.));
1250 let r = Mat4::from_rotation_y(PI / 3.0);
1251 let s = Mat4::from_scale(Vec3::new(1., 2., 0.5));
1252 let m = t * r * s; let inv_m = m.inverse().expect("Matrix should be invertible");
1255 let affine_inv_m = m
1256 .affine_inverse()
1257 .expect("Matrix should be affine invertible");
1258
1259 assert!(
1261 mat4_approx_eq(inv_m, affine_inv_m),
1262 "Affine inverse should match general inverse"
1263 );
1264
1265 let identity = m * affine_inv_m;
1267 assert!(
1268 mat4_approx_eq(identity, Mat4::IDENTITY),
1269 "M * affine_inv(M) should be Identity"
1270 );
1271
1272 let singular_s = Mat4::from_scale(Vec3::new(1.0, 0.0, 1.0));
1274 let singular_m = t * singular_s;
1275 assert!(
1276 singular_m.affine_inverse().is_none(),
1277 "Singular affine matrix inverse should be None"
1278 );
1279 }
1280
1281 #[test]
1282 fn test_perspective_rh_zo() {
1283 let fov = PI / 4.0; let aspect = 16.0 / 9.0;
1285 let near = 0.1;
1286 let far = 100.0;
1287
1288 let m = Mat4::perspective_rh_zo(fov, aspect, near, far);
1289 assert!(approx_eq(m.cols[0].x, 1.0 / (aspect * (fov / 2.0).tan())));
1290 assert!(approx_eq(m.cols[1].y, 1.0 / ((fov / 2.0).tan())));
1291 assert!(approx_eq(m.cols[2].z, -far / (far - near)));
1292 assert!(approx_eq(m.cols[3].z, -(far * near) / (far - near)));
1293 }
1294
1295 #[test]
1296 fn test_orthographic_rh_zo() {
1297 let left = -1.0;
1298 let right = 1.0;
1299 let bottom = -1.0;
1300 let top = 1.0;
1301 let near = 0.1;
1302 let far = 100.0;
1303 let m = Mat4::orthographic_rh_zo(left, right, bottom, top, near, far);
1304
1305 assert!(approx_eq(m.cols[0].x, 2.0 / (right - left)));
1307 assert!(approx_eq(m.cols[1].y, 2.0 / (top - bottom)));
1308 assert!(approx_eq(m.cols[2].z, -1.0 / (far - near)));
1309
1310 assert!(approx_eq(m.cols[3].x, -(right + left) / (right - left)));
1312 assert!(approx_eq(m.cols[3].y, -(top + bottom) / (top - bottom)));
1313 assert!(approx_eq(m.cols[3].z, -near / (far - near))); }
1315
1316 #[test]
1317 fn test_look_at_rh() {
1318 let eye = Vec3::new(0.0, 0.0, 5.0);
1319 let target = Vec3::new(0.0, 0.0, 0.0);
1320 let up = Vec3::new(0.0, 1.0, 0.0);
1321
1322 let m = Mat4::look_at_rh(eye, target, up).expect("look_at_rh should return Some(Mat4)");
1323
1324 assert!(approx_eq(m.cols[2].z, 1.0));
1326
1327 assert!(approx_eq(m.cols[3].z, -5.0));
1329 }
1330
1331 #[test]
1332 fn test_look_at_rh_invalid() {
1333 let eye = Vec3::new(0.0, 0.0, 5.0);
1334 let target = Vec3::new(0.0, 0.0, 5.0); let up = Vec3::new(0.0, 1.0, 0.0);
1336
1337 assert!(Mat4::look_at_rh(eye, target, up).is_none());
1339 }
1340
1341 fn clip_depth(view_proj: &Mat4, world: Vec3) -> f32 {
1346 let clip = *view_proj * crate::math::Vec4::new(world.x, world.y, world.z, 1.0);
1347 clip.z / clip.w
1348 }
1349
1350 #[test]
1351 fn cube_face_view_projs_returns_six_in_wgpu_order() {
1352 let m = Mat4::cube_face_view_projs(Vec3::ZERO, 100.0);
1353 assert_eq!(m.len(), 6);
1354 for i in 0..6 {
1356 for j in (i + 1)..6 {
1357 let same = (0..16).all(|k| {
1358 let row = k / 4;
1359 let col = k % 4;
1360 let a = m[i].cols[col][row];
1361 let b = m[j].cols[col][row];
1362 crate::math::approx_eq(a, b)
1363 });
1364 assert!(!same, "faces {i} and {j} produced identical matrices");
1365 }
1366 }
1367 }
1368
1369 #[test]
1370 fn cube_face_matrices_project_axis_aligned_points_into_correct_face() {
1371 let pos = Vec3::ZERO;
1375 let far = 100.0;
1376 for face in crate::math::CubeFace::ALL {
1377 let vp = Mat4::cube_face_view_proj(pos, face, far);
1378 let target = pos + face.forward() * 5.0; let clip = vp * crate::math::Vec4::new(target.x, target.y, target.z, 1.0);
1380 assert!(
1381 clip.w > 0.0,
1382 "face {face:?}: target should be in front (w>0), got w = {}",
1383 clip.w
1384 );
1385 let z_norm = clip.z / clip.w;
1386 assert!(
1387 (0.0..=1.0).contains(&z_norm),
1388 "face {face:?}: target z/w {z_norm} not in [0, 1]"
1389 );
1390 let x_norm = clip.x / clip.w;
1391 let y_norm = clip.y / clip.w;
1392 assert!(
1393 x_norm.abs() < 1e-3 && y_norm.abs() < 1e-3,
1394 "face {face:?}: axis-aligned point should be near screen centre, got ({x_norm}, {y_norm})"
1395 );
1396 }
1397 }
1398
1399 #[test]
1400 fn cube_far_plane_maps_to_one() {
1401 let pos = Vec3::ZERO;
1402 let far = 50.0;
1403 for face in crate::math::CubeFace::ALL {
1404 let vp = Mat4::cube_face_view_proj(pos, face, far);
1405 let z = clip_depth(&vp, pos + face.forward() * far);
1406 assert!(
1407 (z - 1.0).abs() < 1e-4,
1408 "face {face:?}: z at far plane should be ≈ 1.0, got {z}"
1409 );
1410 }
1411 }
1412
1413 #[test]
1414 fn cube_near_plane_maps_to_zero() {
1415 let pos = Vec3::ZERO;
1416 let far = 50.0;
1417 for face in crate::math::CubeFace::ALL {
1419 let vp = Mat4::cube_face_view_proj(pos, face, far);
1420 let z = clip_depth(&vp, pos + face.forward() * 0.1);
1421 assert!(
1422 z.abs() < 1e-4,
1423 "face {face:?}: z at near plane should be ≈ 0.0, got {z}"
1424 );
1425 }
1426 }
1427
1428 #[test]
1429 fn cube_face_view_proj_idempotent_with_explicit_lookat() {
1430 let pos = Vec3::new(1.0, 2.0, 3.0);
1433 let far = 80.0;
1434 for face in crate::math::CubeFace::ALL {
1435 let helper = Mat4::cube_face_view_proj(pos, face, far);
1436 let proj = Mat4::perspective_rh_zo(crate::math::FRAC_PI_2, 1.0, 0.1, far);
1437 let view = Mat4::look_at_rh(pos, pos + face.forward(), face.up())
1438 .expect("non-degenerate basis");
1439 let expected = proj * view;
1440 for col in 0..4 {
1441 for row in 0..4 {
1442 assert!(
1443 crate::math::approx_eq_eps(
1444 helper.cols[col][row],
1445 expected.cols[col][row],
1446 1e-5,
1447 ),
1448 "face {face:?}: mismatch at col {col} row {row}"
1449 );
1450 }
1451 }
1452 }
1453 }
1454
1455 }