Skip to main content

rsm_lib/
mat4.rs

1use num_traits::{
2    NumAssign, Float
3};
4
5use std::slice::{
6    Iter,
7    IterMut
8};
9
10use std::ops::{
11    Neg, Add, Sub, Mul, Div,
12    Index, IndexMut
13};
14
15use crate::vec4::Vec4;
16use crate::vec3::Vec3;
17
18use std::fmt;
19
20#[derive(Debug, Default, Copy, Clone, PartialEq)]
21pub struct Mat4<T> (
22    pub Vec4<T>,
23    pub Vec4<T>,
24    pub Vec4<T>,
25    pub Vec4<T>
26);
27
28impl<T> Mat4<T>
29where
30    T: NumAssign + Copy,
31{
32    /// Creates a new `Mat4` instance from four column vectors.
33    ///
34    /// # Parameters
35    /// - `col0`: The first column vector.
36    /// - `col1`: The second column vector.
37    /// - `col2`: The third column vector.
38    /// - `col3`: The fourth column vector.
39    ///
40    /// # Returns
41    /// Returns a new `Mat4` with the specified column vectors.
42    ///
43    /// # Examples
44    /// ```
45    /// let col0 = Vec4::new(1.0, 0.0, 0.0, 0.0);
46    /// let col1 = Vec4::new(0.0, 1.0, 0.0, 0.0);
47    /// let col2 = Vec4::new(0.0, 0.0, 1.0, 0.0);
48    /// let col3 = Vec4::new(0.0, 0.0, 0.0, 1.0);
49    /// let mat = Mat4::new(&col0, &col1, &col2, &col3);
50    /// ```
51    #[inline]
52    pub fn new(col0: &Vec4<T>, col1: &Vec4<T>, col2: &Vec4<T>, col3: &Vec4<T>) -> Self {
53        Self (col0.clone(), col1.clone(), col2.clone(), col3.clone())
54    }
55
56    /// Returns the zero matrix (all elements are zero).
57    ///
58    /// # Returns
59    /// Returns a new `Mat4` where all components are zero.
60    ///
61    /// # Examples
62    /// ```
63    /// let mat = Mat4::zero();
64    /// ```
65    #[inline]
66    pub fn zero() -> Self {
67        Self (
68            Vec4::zero(),
69            Vec4::zero(),
70            Vec4::zero(),
71            Vec4::zero(),
72        )
73    }
74
75    /// Returns the identity matrix (diagonal with ones).
76    ///
77    /// # Returns
78    /// Returns a new `Mat4` with ones on the diagonal and zeros elsewhere.
79    ///
80    /// # Examples
81    /// ```
82    /// let mat = Mat4::identity();
83    /// ```
84    #[inline]
85    pub fn identity() -> Self {
86        Self (
87            Vec4::new(T::one(), T::zero(), T::zero(), T::zero()),
88            Vec4::new(T::zero(), T::one(), T::zero(), T::zero()),
89            Vec4::new(T::zero(), T::zero(), T::one(), T::zero()),
90            Vec4::new(T::zero(), T::zero(), T::zero(), T::one()),
91        )
92    }
93
94    /// Computes the transpose of the matrix.
95    ///
96    /// The transpose of a matrix is obtained by swapping rows with columns.
97    ///
98    /// # Returns
99    /// Returns a new `Mat4` which is the transpose of the original matrix.
100    ///
101    /// # Examples
102    /// ```
103    /// let mat = Mat4::identity();
104    /// let transposed = mat.transpose();
105    /// assert_eq!(transposed, mat);
106    /// ```
107    #[inline]
108    pub fn transpose(&self) -> Self {
109        Self (
110            Vec4::new(self.0.x, self.1.x, self.2.x, self.3.x),
111            Vec4::new(self.0.y, self.1.y, self.2.y, self.3.y),
112            Vec4::new(self.0.z, self.1.z, self.2.z, self.3.z),
113            Vec4::new(self.0.w, self.1.w, self.2.w, self.3.w),
114        )
115    }
116
117    /// Computes the determinant of the matrix.
118    ///
119    /// The determinant is a scalar value that can be used to determine if the matrix is invertible.
120    ///
121    /// # Returns
122    /// Returns the determinant as a `T`.
123    ///
124    /// # Examples
125    /// ```
126    /// let mat = Mat4::identity();
127    /// let determinant = mat.determinant();
128    /// assert_eq!(determinant, 1.0);
129    /// ```
130    pub fn determinant(&self) -> T {
131        let a00 = self.0.x;
132        let a01 = self.0.y;
133        let a02 = self.0.z;
134        let a03 = self.0.w;
135        let a10 = self.1.x;
136        let a11 = self.1.y;
137        let a12 = self.1.z;
138        let a13 = self.1.w;
139        let a20 = self.2.x;
140        let a21 = self.2.y;
141        let a22 = self.2.z;
142        let a23 = self.2.w;
143        let a30 = self.3.x;
144        let a31 = self.3.y;
145        let a32 = self.3.z;
146        let a33 = self.3.w;
147
148        let term1 = a30 * a21 * a12 * a03;
149        let term2 = a20 * a31 * a12 * a03;
150        let term3 = a30 * a11 * a22 * a03;
151        let term4 = a10 * a31 * a22 * a03;
152        let term5 = a20 * a11 * a32 * a03;
153        let term6 = a10 * a21 * a32 * a03;
154        let term7 = a30 * a21 * a02 * a13;
155        let term8 = a20 * a31 * a02 * a13;
156        let term9 = a30 * a01 * a22 * a13;
157        let term10 = a00 * a31 * a22 * a13;
158        let term11 = a20 * a01 * a32 * a13;
159        let term12 = a00 * a21 * a32 * a13;
160        let term13 = a30 * a11 * a02 * a23;
161        let term14 = a10 * a31 * a02 * a23;
162        let term15 = a30 * a01 * a12 * a23;
163        let term16 = a00 * a31 * a12 * a23;
164        let term17 = a10 * a01 * a32 * a23;
165        let term18 = a00 * a11 * a32 * a23;
166        let term19 = a20 * a11 * a02 * a33;
167        let term20 = a10 * a21 * a02 * a33;
168        let term21 = a20 * a01 * a12 * a33;
169        let term22 = a00 * a21 * a12 * a33;
170        let term23 = a10 * a01 * a22 * a33;
171        let term24 = a00 * a11 * a22 * a33;
172
173        let determinant = term1 - term2 - term3 + term4 +
174                          term5 - term6 - term7 + term8 +
175                          term9 - term10 - term11 + term12 +
176                          term13 - term14 - term15 + term16 +
177                          term17 - term18 - term19 + term20 +
178                          term21 - term22 - term23 + term24;
179
180        determinant
181    }
182
183    /// Computes the trace of the matrix.
184    ///
185    /// The trace is the sum of the diagonal elements.
186    ///
187    /// # Returns
188    /// Returns the trace as a `T`.
189    ///
190    /// # Examples
191    /// ```
192    /// let mat = Mat4::identity();
193    /// let trace = mat.trace();
194    /// assert_eq!(trace, 4.0);
195    /// ```
196    pub fn trace(&self) -> T {
197        self.0.x + self.1.y + self.2.z + self.3.w
198    }
199
200    /// Multiplies this matrix by another matrix.
201    ///
202    /// # Parameters
203    /// - `other`: The matrix to multiply with.
204    ///
205    /// # Returns
206    /// Returns the resulting matrix after multiplication.
207    ///
208    /// # Examples
209    /// ```
210    /// let mat1 = Mat4::identity();
211    /// let mat2 = Mat4::identity();
212    /// let result = mat1.mul(&mat2);
213    /// assert_eq!(result, mat1);
214    /// ```
215    pub fn mul(&self, other: &Self) -> Self {
216        let col0 = Vec4::new(
217            self.0.dot(&Vec4::new(other.0.x, other.1.x, other.2.x, other.3.x)),
218            self.1.dot(&Vec4::new(other.0.x, other.1.x, other.2.x, other.3.x)),
219            self.2.dot(&Vec4::new(other.0.x, other.1.x, other.2.x, other.3.x)),
220            self.3.dot(&Vec4::new(other.0.x, other.1.x, other.2.x, other.3.x)),
221        );
222        let col1 = Vec4::new(
223            self.0.dot(&Vec4::new(other.0.y, other.1.y, other.2.y, other.3.y)),
224            self.1.dot(&Vec4::new(other.0.y, other.1.y, other.2.y, other.3.y)),
225            self.2.dot(&Vec4::new(other.0.y, other.1.y, other.2.y, other.3.y)),
226            self.3.dot(&Vec4::new(other.0.y, other.1.y, other.2.y, other.3.y)),
227        );
228        let col2 = Vec4::new(
229            self.0.dot(&Vec4::new(other.0.z, other.1.z, other.2.z, other.3.z)),
230            self.1.dot(&Vec4::new(other.0.z, other.1.z, other.2.z, other.3.z)),
231            self.2.dot(&Vec4::new(other.0.z, other.1.z, other.2.z, other.3.z)),
232            self.3.dot(&Vec4::new(other.0.z, other.1.z, other.2.z, other.3.z)),
233        );
234        let col3 = Vec4::new(
235            self.0.dot(&Vec4::new(other.0.w, other.1.w, other.2.w, other.3.w)),
236            self.1.dot(&Vec4::new(other.0.w, other.1.w, other.2.w, other.3.w)),
237            self.2.dot(&Vec4::new(other.0.w, other.1.w, other.2.w, other.3.w)),
238            self.3.dot(&Vec4::new(other.0.w, other.1.w, other.2.w, other.3.w)),
239        );
240        Self::new(&col0, &col1, &col2, &col3)
241    }
242
243    /// Applies a translation transformation to the matrix.
244    ///
245    /// This method modifies the matrix to include the translation vector.
246    ///
247    /// # Parameters
248    /// - `translate`: The translation vector to apply.
249    ///
250    /// # Examples
251    /// ```
252    /// let mut mat = Mat4::identity();
253    /// let translate = Vec3::new(1.0, 2.0, 3.0);
254    /// mat.translate(&translate);
255    /// ```
256    #[inline]
257    pub fn translate(&mut self, translate: &Vec3<T>) {
258        self.3.x += translate.x;
259        self.3.y += translate.y;
260        self.3.z += translate.z;
261    }
262
263    /// Applies a scale transformation to the matrix.
264    ///
265    /// This method modifies the matrix to include the scaling vector.
266    ///
267    /// # Parameters
268    /// - `scale`: The scaling vector to apply.
269    ///
270    /// # Examples
271    /// ```
272    /// let mut mat = Mat4::identity();
273    /// let scale = Vec3::new(2.0, 3.0, 4.0);
274    /// mat.scale(&scale);
275    /// ```
276    #[inline]
277    pub fn scale(&mut self, scale: &Vec3<T>) {
278        self.0.x *= scale.x;
279        self.1.y *= scale.y;
280        self.2.z *= scale.z;
281    }    
282}
283
284impl<T> Mat4<T>
285where
286    T: NumAssign + Float,
287{
288    /// Computes the inverse of the matrix.
289    ///
290    /// # Returns
291    /// Returns `Some(Self)` if the matrix is invertible, `None` otherwise. The inverse is computed using the adjugate method and determinant.
292    ///
293    /// # Examples
294    /// ```
295    /// use std::f32::consts::PI;
296    /// let mat = Mat4::identity();
297    /// let inv = mat.invert();
298    /// assert!(inv.is_some());
299    /// ```
300    pub fn invert(&self) -> Option<Self> {
301        let a00 = self.0.x;
302        let a01 = self.0.y;
303        let a02 = self.0.z;
304        let a03 = self.0.w;
305        let a10 = self.1.x;
306        let a11 = self.1.y;
307        let a12 = self.1.z;
308        let a13 = self.1.w;
309        let a20 = self.2.x;
310        let a21 = self.2.y;
311        let a22 = self.2.z;
312        let a23 = self.2.w;
313        let a30 = self.3.x;
314        let a31 = self.3.y;
315        let a32 = self.3.z;
316        let a33 = self.3.w;
317
318        let b00 = a00 * a11 - a01 * a10;
319        let b01 = a00 * a12 - a02 * a10;
320        let b02 = a00 * a13 - a03 * a10;
321        let b03 = a01 * a12 - a02 * a11;
322        let b04 = a01 * a13 - a03 * a11;
323        let b05 = a02 * a13 - a03 * a12;
324        let b06 = a20 * a31 - a21 * a30;
325        let b07 = a20 * a32 - a22 * a30;
326        let b08 = a20 * a33 - a23 * a30;
327        let b09 = a21 * a32 - a22 * a31;
328        let b10 = a21 * a33 - a23 * a31;
329        let b11 = a22 * a33 - a23 * a32;
330
331        let det = b00 * b11 - b01 * b10 + b02 * b09
332            - b03 * b08 - b04 * b07 + b05 * b06;
333
334        if det.is_zero() {
335            return None;
336        }
337
338        let inv_det = T::one() / det;
339
340        Some(Self(
341            Vec4::new(
342                (a11 * b11 - a12 * b10 + a13 * b09) * inv_det,
343                (-a01 * b11 + a02 * b10 - a03 * b09) * inv_det,
344                (a31 * b05 - a32 * b04 + a33 * b03) * inv_det,
345                (-a21 * b05 + a22 * b04 - a23 * b03) * inv_det,
346            ),
347            Vec4::new(
348                (-a10 * b11 + a12 * b08 - a13 * b07) * inv_det,
349                (a00 * b11 - a02 * b08 + a03 * b07) * inv_det,
350                (-a30 * b05 + a32 * b02 - a33 * b01) * inv_det,
351                (a20 * b05 - a22 * b02 + a23 * b01) * inv_det,
352            ),
353            Vec4::new(
354                (a10 * b10 - a11 * b08 + a13 * b06) * inv_det,
355                (-a00 * b10 + a01 * b08 - a03 * b06) * inv_det,
356                (a30 * b04 - a31 * b02 + a33 * b00) * inv_det,
357                (-a20 * b04 + a21 * b02 - a23 * b00) * inv_det,
358            ),
359            Vec4::new(
360                (-a10 * b09 + a11 * b07 - a12 * b06) * inv_det,
361                (a00 * b09 - a01 * b07 + a02 * b06) * inv_det,
362                (-a30 * b03 + a31 * b01 - a32 * b00) * inv_det,
363                (a20 * b03 - a21 * b01 + a22 * b00) * inv_det,
364            ),
365        ))
366    }
367
368    /// Applies a rotation transformation to the matrix around an arbitrary axis.
369    ///
370    /// # Parameters
371    /// - `axis`: The axis of rotation as a `Vec3<T>`. It should be normalized.
372    /// - `angle`: The angle of rotation in radians.
373    ///
374    /// # Examples
375    /// ```
376    /// use std::f32::consts::PI;
377    /// let mut mat = Mat4::identity();
378    /// let axis = Vec3::new(0.0, 1.0, 0.0);
379    /// let angle = PI / 4.0; // 45 degrees
380    /// mat.rotate(&axis, angle);
381    /// ```
382    pub fn rotate(&mut self, axis: Vec3<T>, angle: T) {
383        let len_sq = axis.length_squared();
384        let inv_len = if len_sq.is_zero() {
385            T::zero()
386        } else {
387            len_sq.sqrt().recip()
388        };
389
390        let axis = axis * inv_len;
391        let s = angle.sin();
392        let c = angle.cos();
393        let t = T::one() - c;
394
395        let m00 = axis.x * axis.x * t + c;
396        let m01 = axis.y * axis.x * t + axis.z * s;
397        let m02 = axis.z * axis.x * t - axis.y * s;
398
399        let m10 = axis.x * axis.y * t - axis.z * s;
400        let m11 = axis.y * axis.y * t + c;
401        let m12 = axis.z * axis.y * t + axis.x * s;
402
403        let m20 = axis.x * axis.z * t + axis.y * s;
404        let m21 = axis.y * axis.z * t - axis.x * s;
405        let m22 = axis.z * axis.z * t + c;
406
407        for i in 0..3 {
408            let x = self[i][0];
409            let y = self[i][1];
410            let z = self[i][2];
411            self[i][0] = x * m00 + y * m10 + z * m20;
412            self[i][1] = x * m01 + y * m11 + z * m21;
413            self[i][2] = x * m02 + y * m12 + z * m22;
414        }
415    }
416
417    /// Rotates the matrix around the X-axis by a given angle.
418    ///
419    /// # Parameters
420    /// - `angle`: The angle of rotation in radians.
421    ///
422    /// # Examples
423    /// ```
424    /// use std::f32::consts::PI;
425    /// let mut mat = Mat4::identity();
426    /// let angle = PI / 4.0; // 45 degrees
427    /// mat.rotate_x(angle);
428    /// ```
429    pub fn rotate_x(&mut self, angle: T) {
430        let c = angle.cos();
431        let s = angle.sin();
432        for i in 0..3 {
433            let y = self[i][1];
434            let z = self[i][2];
435            self[i][1] = y * c - z * s;
436            self[i][2] = y * s + z * c;
437        }
438    }
439
440    /// Rotates the matrix around the Y-axis by a given angle.
441    ///
442    /// # Parameters
443    /// - `angle`: The angle of rotation in radians.
444    ///
445    /// # Examples
446    /// ```
447    /// use std::f32::consts::PI;
448    /// let mut mat = Mat4::identity();
449    /// let angle = PI / 4.0; // 45 degrees
450    /// mat.rotate_y(angle);
451    /// ```
452    pub fn rotate_y(&mut self, angle: T) {
453        let c = angle.cos();
454        let s = angle.sin();
455        for i in 0..3 {
456            let x = self[i][0];
457            let z = self[i][2];
458            self[i][0] = x * c + z * s;
459            self[i][2] = -x * s + z * c;
460        }
461    }
462
463    /// Rotates the matrix around the Z-axis by a given angle.
464    ///
465    /// # Parameters
466    /// - `angle`: The angle of rotation in radians.
467    ///
468    /// # Examples
469    /// ```
470    /// use std::f32::consts::PI;
471    /// let mut mat = Mat4::identity();
472    /// let angle = PI / 4.0; // 45 degrees
473    /// mat.rotate_z(angle);
474    /// ```
475    pub fn rotate_z(&mut self, angle: T) {
476        let c = angle.cos();
477        let s = angle.sin();
478        for i in 0..3 {
479            let x = self[i][0];
480            let y = self[i][1];
481            self[i][0] = x * c - y * s;
482            self[i][1] = x * s + y * c;
483        }
484    }
485
486    /// Applies a rotation transformation to the matrix around the X, Y, and Z axes sequentially.
487    ///
488    /// The rotations are applied in the order X -> Y -> Z, meaning that:
489    /// - First, the matrix is rotated around the X-axis by `angles.x`.
490    /// - Then, the resulting matrix is rotated around the Y-axis by `angles.y`.
491    /// - Finally, the resulting matrix is rotated around the Z-axis by `angles.z`.
492    ///
493    /// # Parameters
494    /// - `angles`: A `Vec3<T>` containing the rotation angles (in radians) for the X, Y, and Z axes respectively.
495    ///
496    /// # Examples
497    /// ```
498    /// use std::f32::consts::PI;
499    /// let mut mat = Mat4::identity();
500    /// let angles = Vec3::new(PI / 4.0, PI / 6.0, PI / 3.0);
501    /// mat.rotate_xyz(angles);
502    /// ```
503    pub fn rotate_xyz(&mut self, angles: Vec3<T>) {
504        let cx = angles.x.cos();
505        let sx = angles.x.sin();
506        for i in 0..3 {
507            let y = self[i][1];
508            let z = self[i][2];
509            self[i][1] = y * cx - z * sx;
510            self[i][2] = y * sx + z * cx;
511        }
512        let cy = angles.y.cos();
513        let sy = angles.y.sin();
514        for i in 0..3 {
515            let x = self[i][0];
516            let z = self[i][2];
517            self[i][0] = x * cy + z * sy;
518            self[i][2] = -x * sy + z * cy;
519        }
520        let cz = angles.z.cos();
521        let sz = angles.z.sin();
522        for i in 0..3 {
523            let x = self[i][0];
524            let y = self[i][1];
525            self[i][0] = x * cz - y * sz;
526            self[i][1] = x * sz + y * cz;
527        }
528    }    
529
530    /// Applies a rotation transformation to the matrix around the Z, Y, and X axes sequentially.
531    ///
532    /// The rotations are applied in the order Z -> Y -> X, meaning that:
533    /// - First, the matrix is rotated around the Z-axis by `angles.z`.
534    /// - Then, the resulting matrix is rotated around the Y-axis by `angles.y`.
535    /// - Finally, the resulting matrix is rotated around the X-axis by `angles.x`.
536    ///
537    /// # Parameters
538    /// - `angles`: A `Vec3<T>` containing the rotation angles (in radians) for the Z, Y, and X axes respectively.
539    ///
540    /// # Examples
541    /// ```
542    /// use std::f32::consts::PI;
543    /// let mut mat = Mat4::identity();
544    /// let angles = Vec3::new(PI / 3.0, PI / 6.0, PI / 4.0);
545    /// mat.rotate_zyx(angles);
546    /// ```
547    pub fn rotate_zyx(&mut self, angles: Vec3<T>) {
548        let cz = angles.z.cos();
549        let sz = angles.z.sin();
550        for i in 0..3 {
551            let x = self[i][0];
552            let y = self[i][1];
553            self[i][0] = x * cz - y * sz;
554            self[i][1] = x * sz + y * cz;
555        }
556        let cy = angles.y.cos();
557        let sy = angles.y.sin();
558        for i in 0..3 {
559            let x = self[i][0];
560            let z = self[i][2];
561            self[i][0] = x * cy + z * sy;
562            self[i][2] = -x * sy + z * cy;
563        }
564        let cx = angles.x.cos();
565        let sx = angles.x.sin();
566        for i in 0..3 {
567            let y = self[i][1];
568            let z = self[i][2];
569            self[i][1] = y * cx - z * sx;
570            self[i][2] = y * sx + z * cx;
571        }
572    }
573
574    /// Creates a frustum matrix for a perspective projection.
575    ///
576    /// The frustum matrix transforms coordinates from the view frustum to normalized device coordinates.
577    ///
578    /// # Parameters
579    /// - `left`, `right`, `bottom`, `top`: Coordinates of the clipping planes of the view frustum.
580    /// - `near_plane`: The distance to the near clipping plane.
581    /// - `far_plane`: The distance to the far clipping plane.
582    ///
583    /// # Returns
584    /// A `Mat4<T>` representing the frustum projection matrix.
585    ///
586    /// # Examples
587    /// ```
588    /// let frustum = Mat4::frustum(-1.0, 1.0, -1.0, 1.0, 0.1, 100.0);
589    /// ```
590    pub fn frustum(left: T, right: T, bottom: T, top: T, near_plane: T, far_plane: T) -> Self {
591        let two = T::from(2.0).unwrap();
592
593        let rl = right - left;
594        let tb = top - bottom;
595        let fn_ = far_plane - near_plane;
596
597        Self (
598            Vec4::new(
599                (near_plane * two) / rl,
600                T::zero(),
601                T::zero(),
602                T::zero(),
603            ),
604            Vec4::new(
605                T::zero(),
606                (near_plane * two) / tb,
607                T::zero(),
608                T::zero(),
609            ),
610            Vec4::new(
611                (right + left) / rl,
612                (top + bottom) / tb,
613                -((far_plane + near_plane) / fn_),
614                -T::one(),
615            ),
616            Vec4::new(
617                T::zero(),
618                T::zero(),
619                -((far_plane * near_plane * two) / fn_),
620                T::zero(),
621            )
622        )
623    }
624
625    /// Creates a perspective projection matrix.
626    ///
627    /// This matrix is used to transform coordinates from a perspective projection view to normalized device coordinates.
628    ///
629    /// # Parameters
630    /// - `fov_y`: Field of view in the Y direction (in radians).
631    /// - `aspect`: Aspect ratio of the viewport (width / height).
632    /// - `near_plane`: The distance to the near clipping plane.
633    /// - `far_plane`: The distance to the far clipping plane.
634    ///
635    /// # Returns
636    /// A `Mat4<T>` representing the perspective projection matrix.
637    ///
638    /// # Examples
639    /// ```
640    /// let perspective = Mat4::perspective(std::f32::consts::PI / 4.0, 16.0 / 9.0, 0.1, 100.0);
641    /// ```
642    pub fn perspective(fov_y: T, aspect: T, near_plane: T, far_plane: T) -> Self {
643        let two = T::from(2.0).unwrap();
644
645        let top = near_plane * (fov_y * T::from(0.5).unwrap()).tan();
646        let bottom = -top;
647        let right = top * aspect;
648        let left = -right;
649
650        let rl = right - left;
651        let tb = top - bottom;
652        let fn_ = far_plane - near_plane;
653
654        Self (
655            Vec4::new(
656                (near_plane * two) / rl,
657                T::zero(),
658                T::zero(),
659                T::zero(),
660            ),
661            Vec4::new(
662                T::zero(),
663                (near_plane * two) / tb,
664                T::zero(),
665                T::zero(),
666            ),
667            Vec4::new(
668                (right + left) / rl,
669                (top + bottom) / tb,
670                -((far_plane + near_plane) / fn_),
671                -T::one(),
672            ),
673            Vec4::new(
674                T::zero(),
675                T::zero(),
676                -((far_plane * near_plane * two) / fn_),
677                T::zero(),
678            )
679        )
680    }
681
682    /// Creates an orthographic projection matrix.
683    ///
684    /// This matrix maps 3D coordinates to a 2D view with no perspective distortion. It is useful for 2D games or certain types of 3D projections where depth perception is not needed.
685    ///
686    /// # Parameters
687    /// - `left`, `right`, `bottom`, `top`: Coordinates of the clipping planes of the orthographic view.
688    /// - `near_plane`: The distance to the near clipping plane.
689    /// - `far_plane`: The distance to the far clipping plane.
690    ///
691    /// # Returns
692    /// A `Mat4<T>` representing the orthographic projection matrix.
693    ///
694    /// # Examples
695    /// ```
696    /// let ortho = Mat4::orthographic(-1.0, 1.0, -1.0, 1.0, 0.1, 100.0);
697    /// ```
698    pub fn orthographic(left: T, right: T, bottom: T, top: T, near_plane: T, far_plane: T) -> Self {
699        let two = T::from(2.0).unwrap();
700
701        let rl = right - left;
702        let tb = top - bottom;
703        let fn_ = far_plane - near_plane;
704
705        Self (
706            Vec4::new(
707                two / rl,
708                T::zero(),
709                T::zero(),
710                T::zero(),
711            ),
712            Vec4::new(
713                T::zero(),
714                two / tb,
715                T::zero(),
716                T::zero(),
717            ),
718            Vec4::new(
719                T::zero(),
720                T::zero(),
721                -two / fn_,
722                T::zero(),
723            ),
724            Vec4::new(
725                -((left + right) / rl),
726                -((top + bottom) / tb),
727                -((far_plane + near_plane) / fn_),
728                T::from(1.0).unwrap(),
729            )
730        )
731    }
732
733    /// Creates a view matrix that orients the camera to look at a specific target.
734    ///
735    /// The view matrix transforms coordinates from world space to camera (or view) space.
736    ///
737    /// # Parameters
738    /// - `eye`: The position of the camera.
739    /// - `target`: The position the camera is looking at.
740    /// - `up`: The up direction of the camera.
741    ///
742    /// # Returns
743    /// A `Mat4<T>` representing the view matrix.
744    ///
745    /// # Examples
746    /// ```
747    /// let eye = Vec3::new(0.0, 0.0, 5.0);
748    /// let target = Vec3::new(0.0, 0.0, 0.0);
749    /// let up = Vec3::new(0.0, 1.0, 0.0);
750    /// let view_matrix = Mat4::look_at(eye, target, up);
751    /// ```
752    pub fn look_at(eye: Vec3<T>, target: Vec3<T>, up: Vec3<T>) -> Self {
753        let vz = Vec3 {
754            x: eye.x - target.x,
755            y: eye.y - target.y,
756            z: eye.z - target.z,
757        };
758
759        let len = (vz.x * vz.x + vz.y * vz.y + vz.z * vz.z).sqrt();
760        let vz = if len != T::zero() {
761            Vec3 {
762                x: vz.x / len,
763                y: vz.y / len,
764                z: vz.z / len,
765            }
766        } else {
767            Vec3 {
768                x: T::zero(),
769                y: T::zero(),
770                z: T::zero(),
771            }
772        };
773
774        let vx = Vec3 {
775            x: up.y * vz.z - up.z * vz.y,
776            y: up.z * vz.x - up.x * vz.z,
777            z: up.x * vz.y - up.y * vz.x,
778        };
779
780        let len = (vx.x * vx.x + vx.y * vx.y + vx.z * vx.z).sqrt();
781        let vx = if len != T::zero() {
782            Vec3 {
783                x: vx.x / len,
784                y: vx.y / len,
785                z: vx.z / len,
786            }
787        } else {
788            Vec3 {
789                x: T::zero(),
790                y: T::zero(),
791                z: T::zero(),
792            }
793        };
794
795        let vy = Vec3 {
796            x: vz.y * vx.z - vz.z * vx.y,
797            y: vz.z * vx.x - vz.x * vx.z,
798            z: vz.x * vx.y - vz.y * vx.x,
799        };
800
801        Self (
802            Vec4::new(
803                vx.x,
804                vy.x,
805                vz.x,
806                T::zero(),
807            ),
808            Vec4::new(
809                vx.y,
810                vy.y,
811                vz.y,
812                T::zero(),
813            ),
814            Vec4::new(
815                vx.z,
816                vy.z,
817                vz.z,
818                T::zero(),
819            ),
820            Vec4::new(
821                -(vx.x * eye.x + vx.y * eye.y + vx.z * eye.z),
822                -(vy.x * eye.x + vy.y * eye.y + vy.z * eye.z),
823                -(vz.x * eye.x + vz.y * eye.y + vz.z * eye.z),
824                T::one(),
825            )
826        )
827    }
828}
829
830impl<T> fmt::Display for Mat4<T>
831where
832    T: fmt::Display,
833{
834    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
835        writeln!(f, "[{}, {}, {}, {}]", self.0, self.1, self.2, self.3)
836    }
837}
838
839impl<T> Index<usize> for Mat4<T> {
840    type Output = Vec4<T>;
841
842    fn index(&self, index: usize) -> &Self::Output {
843        match index {
844            0 => &self.0,
845            1 => &self.1,
846            2 => &self.2,
847            3 => &self.3,
848            _ => panic!("Index out of bounds for Mat4"),
849        }
850    }
851}
852
853impl<T> IndexMut<usize> for Mat4<T> {
854    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
855        match index {
856            0 => &mut self.0,
857            1 => &mut self.1,
858            2 => &mut self.2,
859            3 => &mut self.3,
860            _ => panic!("Index out of bounds for Mat4"),
861        }
862    }
863}
864
865impl<'a, T> IntoIterator for &'a Mat4<T>
866where
867    T: NumAssign + Copy
868{
869    type Item = &'a Vec4<T>;
870    type IntoIter = Iter<'a, Vec4<T>>;
871
872    #[inline]
873    fn into_iter(self) -> Self::IntoIter {
874        let slice: &[Vec4<T>; 4] = unsafe { std::mem::transmute(self) };
875        slice.iter()
876    }
877}
878
879impl<'a, T> IntoIterator for &'a mut Mat4<T>
880where
881    T: NumAssign + Copy
882{
883    type Item = &'a mut Vec4<T>;
884    type IntoIter = IterMut<'a, Vec4<T>>;
885
886    #[inline]
887    fn into_iter(self) -> Self::IntoIter {
888        let slice: &mut [Vec4<T>; 4] = unsafe { std::mem::transmute(self) };
889        slice.iter_mut()
890    }
891}
892
893impl<T> Neg for Mat4<T>
894where
895    T: NumAssign + Copy + Neg<Output = T>,
896{
897    type Output = Self;
898
899    fn neg(self) -> Self::Output {
900        Self (
901            -self.0,
902            -self.1,
903            -self.2,
904            -self.3,
905        )
906    }
907}
908
909impl<T> Add<Mat4<T>> for Mat4<T>
910where
911    T: NumAssign + Copy,
912{
913    type Output = Self;
914
915    fn add(self, other: Self) -> Self {
916        Self (
917            self.0 + other.0,
918            self.1 + other.1,
919            self.2 + other.2,
920            self.3 + other.3,
921        )
922    }
923}
924
925impl<T> Sub<Mat4<T>> for Mat4<T>
926where
927    T: NumAssign + Copy,
928{
929    type Output = Self;
930
931    fn sub(self, other: Self) -> Self {
932        Self (
933            self.0 - other.0,
934            self.1 - other.1,
935            self.2 - other.2,
936            self.3 - other.3,
937        )
938    }
939}
940
941impl<T> Mul<Mat4<T>> for Mat4<T>
942where
943    T: NumAssign + Copy,
944{
945    type Output = Mat4<T>;
946
947    #[inline]
948    fn mul(self, other: Mat4<T>) -> Mat4<T> {
949        Self::mul(&self, &other)
950    }
951}
952
953impl<T> Mul<T> for Mat4<T>
954where
955    T: NumAssign + Copy,
956{
957    type Output = Self;
958
959    fn mul(self, scalar: T) -> Self {
960        Self (
961            self.0 * scalar,
962            self.1 * scalar,
963            self.2 * scalar,
964            self.3 * scalar,
965        )
966    }
967}
968
969impl<T> Div<T> for Mat4<T>
970where
971    T: NumAssign + Copy,
972{
973    type Output = Self;
974
975    fn div(self, scalar: T) -> Self {
976        Self (
977            self.0 / scalar,
978            self.1 / scalar,
979            self.2 / scalar,
980            self.3 / scalar,
981        )
982    }
983}