Skip to main content

cvmath/mat/
mat4.rs

1/*!
2Mat4 transformation matrix.
3*/
4
5use super::*;
6
7/// 4D transformation matrix.
8///
9/// Each field _a_<sub>i</sub><sub>j</sub> represents the _i_-th row and _j_-th column of the matrix.
10///
11/// Row-major storage with column-major semantics.
12///
13/// Stored in row-major order (fields appear in reading order),
14/// but interpreted as column-major: each column is a transformed basis vector,
15/// and matrices are applied to column vectors via `mat * vec`.
16#[derive(Copy, Clone, Default, PartialEq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18#[repr(C)]
19pub struct Mat4<T> {
20	pub a11: T, pub a12: T, pub a13: T, pub a14: T,
21	pub a21: T, pub a22: T, pub a23: T, pub a24: T,
22	pub a31: T, pub a32: T, pub a33: T, pub a34: T,
23	pub a41: T, pub a42: T, pub a43: T, pub a44: T,
24}
25
26/// Mat4 constructor.
27#[allow(non_snake_case)]
28#[inline]
29pub const fn Mat4<T>(
30	a11: T, a12: T, a13: T, a14: T,
31	a21: T, a22: T, a23: T, a24: T,
32	a31: T, a32: T, a33: T, a34: T,
33	a41: T, a42: T, a43: T, a44: T,
34) -> Mat4<T> {
35	Mat4 { a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, a42, a43, a44 }
36}
37
38#[cfg(feature = "dataview")]
39unsafe impl<T: dataview::Pod> dataview::Pod for Mat4<T> {}
40
41//----------------------------------------------------------------
42// Constructors
43
44impl<T> Mat4<T> {
45	/// Constructs a new matrix from components.
46	#[inline]
47	pub const fn new(
48		a11: T, a12: T, a13: T, a14: T,
49		a21: T, a22: T, a23: T, a24: T,
50		a31: T, a32: T, a33: T, a34: T,
51		a41: T, a42: T, a43: T, a44: T,
52	) -> Mat4<T> {
53		Mat4 {
54			a11, a12, a13, a14,
55			a21, a22, a23, a24,
56			a31, a32, a33, a34,
57			a41, a42, a43, a44,
58		}
59	}
60}
61
62impl<T: Zero> Mat4<T> {
63	/// Zero matrix.
64	pub const ZERO: Mat4<T> = Mat4 {
65		a11: T::ZERO, a12: T::ZERO, a13: T::ZERO, a14: T::ZERO,
66		a21: T::ZERO, a22: T::ZERO, a23: T::ZERO, a24: T::ZERO,
67		a31: T::ZERO, a32: T::ZERO, a33: T::ZERO, a34: T::ZERO,
68		a41: T::ZERO, a42: T::ZERO, a43: T::ZERO, a44: T::ZERO,
69	};
70}
71
72impl<T: Zero + One> Mat4<T> {
73	/// Identity matrix.
74	pub const IDENTITY: Mat4<T> = Mat4 {
75		a11: T::ONE,  a12: T::ZERO, a13: T::ZERO, a14: T::ZERO,
76		a21: T::ZERO, a22: T::ONE,  a23: T::ZERO, a24: T::ZERO,
77		a31: T::ZERO, a32: T::ZERO, a33: T::ONE,  a34: T::ZERO,
78		a41: T::ZERO, a42: T::ZERO, a43: T::ZERO, a44: T::ONE,
79	};
80}
81
82impl<T: Zero + One> From<Mat3<T>> for Mat4<T> {
83	#[inline]
84	fn from(mat: Mat3<T>) -> Mat4<T> {
85		Mat4 {
86			a11: mat.a11, a12: mat.a12, a13: mat.a13,
87			a21: mat.a21, a22: mat.a22, a23: mat.a23,
88			a31: mat.a31, a32: mat.a32, a33: mat.a33,
89			..Mat4::IDENTITY
90		}
91	}
92}
93
94impl<T: Zero + One> From<Transform3<T>> for Mat4<T> {
95	#[inline]
96	fn from(mat: Transform3<T>) -> Mat4<T> {
97		Mat4 {
98			a11: mat.a11, a12: mat.a12, a13: mat.a13, a14: mat.a14,
99			a21: mat.a21, a22: mat.a22, a23: mat.a23, a24: mat.a24,
100			a31: mat.a31, a32: mat.a32, a33: mat.a33, a34: mat.a34,
101			..Mat4::IDENTITY
102		}
103	}
104}
105
106// https://miz-ar.info/glm-notes/gtc/matrix-transform.html
107
108impl<T: Float> Mat4<T> {
109	/// Look-at matrix.
110	#[inline]
111	pub fn look_at(position: Vec3<T>, target: Vec3<T>, ref_up: Vec3<T>, hand: Hand) -> Mat4<T> {
112		Transform3::look_at(position, target, ref_up, hand).into()
113	}
114
115	/// Orthographic projection matrix.
116	///
117	/// Clip and hand parameters only affect the Z coordinate.
118	#[inline]
119	pub fn ortho(left: T, right: T, bottom: T, top: T, near: T, far: T, (hand, clip): (Hand, Clip)) -> Mat4<T> {
120		let mins = Vec3(left, bottom, near);
121		let maxs = Vec3(right, top, far);
122		Transform3::ortho(Bounds3 { mins, maxs }, (hand, clip)).into()
123	}
124
125	/// Orthographic projection matrix matching the framing of a perspective camera.
126	///
127	/// Produces an orthographic projection that preserves the screen-space framing
128	/// of a perspective projection at a given `focus_depth`.
129	///
130	/// Useful for matching zoom level or composition when switching from
131	/// perspective to orthographic rendering modes.
132	///
133	/// # Parameters
134	/// - `focus_depth`: Distance from the camera to the subject being framed.
135	/// - `fov_y`: Vertical field of view in radians (for the perspective camera).
136	/// - `aspect_ratio`: Width over height of the viewport.
137	/// - `near`, `far`: Depth clipping planes.
138	/// - `flags`: Projection handedness and clip space settings.
139	#[inline]
140	pub fn ortho_perspective(focus_depth: T, fov_y: Angle<T>, aspect_ratio: T, near: T, far: T, flags: (Hand, Clip)) -> Mat4<T> {
141		debug_assert!(fov_y > Angle::ZERO && fov_y < Angle::HALF, "fov_y must be in (0, 180)");
142		debug_assert!(aspect_ratio > T::ZERO, "aspect_ratio must be strictly positive");
143		debug_assert!(T::ZERO < near && near < far);
144
145		let half_height = (fov_y / T::TWO).tan() * focus_depth;
146		let half_width = half_height * aspect_ratio;
147		Mat4::ortho(-half_width, half_width, -half_height, half_height, near, far, flags)
148	}
149
150	/// Frustum matrix.
151	#[inline]
152	pub fn frustum(left: T, right: T, bottom: T, top: T, near: T, far: T, (hand, clip): (Hand, Clip)) -> Mat4<T> {
153		debug_assert!(T::ZERO < near && near < far);
154
155		let nf = far - near;
156		let np = near + near;
157
158		let a11 = np / (right - left);
159		let a13 = (right + left) / (right - left);
160		let a22 = np / (top - bottom);
161		let a23 = (top + bottom) / (top - bottom);
162
163		let a33 = match clip {
164			Clip::ZO => far / nf,
165			Clip::NO => (far + near) / nf,
166		};
167		let a33 = match hand {
168			Hand::LH => a33,
169			Hand::RH => -a33,
170		};
171
172		let a34 = -far * near / nf;
173		let a34 = match clip {
174			Clip::ZO => a34,
175			Clip::NO => a34 + a34,
176		};
177
178		let a43 = match hand {
179			Hand::LH => T::ONE,
180			Hand::RH => -T::ONE,
181		};
182
183		Mat4 { a11, a13, a22, a23, a33, a34, a43, ..Mat4::ZERO }
184	}
185
186	/// Perspective projection matrix.
187	///
188	/// # Parameters
189	/// - `fov_y`: Vertical field of view in radians (for the perspective camera).
190	/// - `aspect_ratio`: Width over height of the viewport.
191	/// - `near`, `far`: Depth clipping planes.
192	/// - `flags`: Projection handedness and clip space settings.
193	#[inline]
194	pub fn perspective(fov_y: Angle<T>, aspect_ratio: T, near: T, far: T, flags: (Hand, Clip)) -> Mat4<T> {
195		debug_assert!(fov_y > Angle::ZERO && fov_y < Angle::HALF, "fov_y must be in (0, 180)");
196		debug_assert!(aspect_ratio > T::ZERO, "aspect_ratio must be strictly positive");
197		debug_assert!(T::ZERO < near && near < far);
198
199		let half_fov_y = fov_y / T::TWO;
200		let half_height = near * half_fov_y.tan();
201		let half_width = aspect_ratio * half_height;
202
203		let left = -half_width;
204		let right = half_width;
205		let bottom = -half_height;
206		let top = half_height;
207
208		Mat4::frustum(left, right, bottom, top, near, far, flags)
209	}
210
211	/// Projection matrix blending orthographic and perspective.
212	///
213	/// # Parameters
214	/// * `blend` controls the mix between ortho (0.0) and perspective (1.0).
215	/// * `focus_depth` is the distance to the subject kept stable in the transition.
216	/// - `fov_y`: Vertical field of view in radians (for the perspective camera).
217	/// - `aspect_ratio`: Width over height of the viewport.
218	/// - `near`, `far`: Depth clipping planes.
219	/// - `flags`: Projection handedness and clip space settings.
220	pub fn blend_ortho_perspective(blend: T, focus_depth: T, fov_y: Angle<T>, aspect_ratio: T, near: T, far: T, flags: (Hand, Clip)) -> Mat4<T> {
221		debug_assert!(fov_y > Angle::ZERO && fov_y < Angle::HALF, "fov_y must be in (0, 180)");
222		debug_assert!(aspect_ratio > T::ZERO, "aspect_ratio must be strictly positive");
223		debug_assert!(T::ZERO < near && near < far);
224		debug_assert!(blend >= T::ZERO && blend <= T::ONE, "fraction must be in [0, 1]");
225
226		let blend = blend.clamp(T::ZERO, T::ONE);
227		let blend = blend * blend;
228		let blend = blend * blend;
229
230		let half_height = (fov_y / T::TWO).tan() * focus_depth;
231		if blend <= T::EPSILON {
232			let half_width = half_height * aspect_ratio;
233			return Mat4::ortho(-half_width, half_width, -half_height, half_height, near, far, flags);
234		}
235
236		let dz = (T::ONE - blend) / blend;
237		let adjusted_fov_y = Angle::atan(half_height / (focus_depth + dz)) * T::TWO;
238
239		let projection = Mat4::perspective(adjusted_fov_y, aspect_ratio, near + dz, far + dz, flags);
240		let trans = Vec3(T::ZERO, T::ZERO, match flags.0 { Hand::LH => dz, Hand::RH => -dz });
241		let view_shift = Transform3::translation(trans);
242
243		projection * view_shift
244	}
245}
246
247//----------------------------------------------------------------
248// Conversions
249
250impl<T> Mat4<T> {
251	/// Converts to a Mat3 matrix.
252	#[inline]
253	pub fn mat3(self) -> Mat3<T> {
254		Mat3 {
255			a11: self.a11, a12: self.a12, a13: self.a13,
256			a21: self.a21, a22: self.a22, a23: self.a23,
257			a31: self.a31, a32: self.a32, a33: self.a33,
258		}
259	}
260}
261
262impl<T> Mat4<T> {
263	#[inline]
264	fn as_array(&self) -> &[T; 16] {
265		unsafe { mem::transmute(self)}
266	}
267	/// Imports the matrix from a row-major layout.
268	#[inline]
269	pub fn from_row_major(mat: [[T; 4]; 4]) -> Mat4<T> {
270		let [[a11, a12, a13, a14], [a21, a22, a23, a24], [a31, a32, a33, a34], [a41, a42, a43, a44]] = mat;
271		Mat4 {
272			a11, a12, a13, a14,
273			a21, a22, a23, a24,
274			a31, a32, a33, a34,
275			a41, a42, a43, a44,
276		}
277	}
278	/// Imports the matrix from a column-major layout.
279	#[inline]
280	pub fn from_column_major(mat: [[T; 4]; 4]) -> Mat4<T> {
281		let [[a11, a21, a31, a41], [a12, a22, a32, a42], [a13, a23, a33, a43], [a14, a24, a34, a44]] = mat;
282		Mat4 {
283			a11, a12, a13, a14,
284			a21, a22, a23, a24,
285			a31, a32, a33, a34,
286			a41, a42, a43, a44,
287		}
288	}
289	/// Exports the matrix as a row-major array.
290	#[inline]
291	pub fn into_row_major(self) -> [[T; 4]; 4] {
292		[
293			[self.a11, self.a12, self.a13, self.a14],
294			[self.a21, self.a22, self.a23, self.a24],
295			[self.a31, self.a32, self.a33, self.a34],
296			[self.a41, self.a42, self.a43, self.a44],
297		]
298	}
299	/// Exports the matrix as a column-major array.
300	#[inline]
301	pub fn into_column_major(self) -> [[T; 4]; 4] {
302		[
303			[self.a11, self.a21, self.a31, self.a41],
304			[self.a12, self.a22, self.a32, self.a42],
305			[self.a13, self.a23, self.a33, self.a43],
306			[self.a14, self.a24, self.a34, self.a44],
307		]
308	}
309}
310
311//----------------------------------------------------------------
312// Decomposition
313
314impl<T> Mat4<T> {
315	/// Composes the matrix from basis vectors.
316	#[inline]
317	pub fn compose(x: Vec4<T>, y: Vec4<T>, z: Vec4<T>, w: Vec4<T>) -> Mat4<T> {
318		Mat4 {
319			a11: x.x, a12: y.x, a13: z.x, a14: w.x,
320			a21: x.y, a22: y.y, a23: z.y, a24: w.y,
321			a31: x.z, a32: y.z, a33: z.z, a34: w.z,
322			a41: x.w, a42: y.w, a43: z.w, a44: w.w,
323		}
324	}
325	/// Gets the transformed X basis vector.
326	#[inline]
327	pub fn x(self) -> Vec4<T> {
328		Vec4 { x: self.a11, y: self.a21, z: self.a31, w: self.a41 }
329	}
330	/// Gets the transformed Y basis vector.
331	#[inline]
332	pub fn y(self) -> Vec4<T> {
333		Vec4 { x: self.a12, y: self.a22, z: self.a32, w: self.a42 }
334	}
335	/// Gets the transformed Z basis vector.
336	#[inline]
337	pub fn z(self) -> Vec4<T> {
338		Vec4 { x: self.a13, y: self.a23, z: self.a33, w: self.a43 }
339	}
340	/// Gets the transformed W basis vector.
341	#[inline]
342	pub fn w(self) -> Vec4<T> {
343		Vec4 { x: self.a14, y: self.a24, z: self.a34, w: self.a44 }
344	}
345}
346
347//----------------------------------------------------------------
348// Operations
349
350impl<T: Scalar> Mat4<T> {
351	/// Computes the determinant.
352	#[inline]
353	pub fn det(self) -> T {
354		// 2×2 subfactors
355		let s0 = self.a33 * self.a44 - self.a34 * self.a43;
356		let s1 = self.a32 * self.a44 - self.a34 * self.a42;
357		let s2 = self.a32 * self.a43 - self.a33 * self.a42;
358		let s3 = self.a31 * self.a44 - self.a34 * self.a41;
359		let s4 = self.a31 * self.a43 - self.a33 * self.a41;
360		let s5 = self.a31 * self.a42 - self.a32 * self.a41;
361
362		// Cofactors for first row
363		let c0 = self.a22 * s0 - self.a23 * s1 + self.a24 * s2;
364		let c1 = self.a21 * s0 - self.a23 * s3 + self.a24 * s4;
365		let c2 = self.a21 * s1 - self.a22 * s3 + self.a24 * s5;
366		let c3 = self.a21 * s2 - self.a22 * s4 + self.a23 * s5;
367
368		// Final determinant using expansion by first row
369		self.a11 * c0 - self.a12 * c1 + self.a13 * c2 - self.a14 * c3
370	}
371	/// Computes the trace.
372	#[inline]
373	pub fn trace(self) -> T {
374		self.a11 + self.a22 + self.a33 + self.a44
375	}
376	/// Computes the squared Frobenius norm (sum of squares of all matrix elements).
377	///
378	/// This measure is useful for quickly checking matrix magnitude or comparing matrices without the cost of a square root operation.
379	///
380	/// To check if a matrix is effectively zero, test if `flat_norm_sqr()` is below a small epsilon threshold.
381	#[inline]
382	pub fn flat_norm_sqr(self) -> T {
383		self.a11 * self.a11 + self.a12 * self.a12 + self.a13 * self.a13 + self.a14 * self.a14 +
384		self.a21 * self.a21 + self.a22 * self.a22 + self.a23 * self.a23 + self.a24 * self.a24 +
385		self.a31 * self.a31 + self.a32 * self.a32 + self.a33 * self.a33 + self.a34 * self.a34 +
386		self.a41 * self.a41 + self.a42 * self.a42 + self.a43 * self.a43 + self.a44 * self.a44
387	}
388	#[inline]
389	pub fn try_invert(self) -> Option<Mat4<T>> where T: Float {
390		glu_invert(&self)
391	}
392	/// Computes the inverse matrix.
393	///
394	/// Returns the zero matrix if the determinant is exactly zero.
395	#[inline]
396	pub fn inverse(self) -> Mat4<T> where T: Float {
397		glu_invert(&self).unwrap_or(Mat4::ZERO)
398	}
399	/// Returns the transposed matrix.
400	#[inline]
401	pub fn transpose(self) -> Mat4<T> {
402		Mat4 {
403			a11: self.a11, a12: self.a21, a13: self.a31, a14: self.a41,
404			a21: self.a12, a22: self.a22, a23: self.a32, a24: self.a42,
405			a31: self.a13, a32: self.a23, a33: self.a33, a34: self.a43,
406			a41: self.a14, a42: self.a24, a43: self.a34, a44: self.a44,
407		}
408	}
409	/// Linear interpolation between the matrix elements.
410	#[inline]
411	pub fn lerp(self, rhs: Mat4<T>, t: T) -> Mat4<T> where T: Float {
412		Mat4 {
413			a11: self.a11 + (rhs.a11 - self.a11) * t,
414			a12: self.a12 + (rhs.a12 - self.a12) * t,
415			a13: self.a13 + (rhs.a13 - self.a13) * t,
416			a14: self.a14 + (rhs.a14 - self.a14) * t,
417
418			a21: self.a21 + (rhs.a21 - self.a21) * t,
419			a22: self.a22 + (rhs.a22 - self.a22) * t,
420			a23: self.a23 + (rhs.a23 - self.a23) * t,
421			a24: self.a24 + (rhs.a24 - self.a24) * t,
422
423			a31: self.a31 + (rhs.a31 - self.a31) * t,
424			a32: self.a32 + (rhs.a32 - self.a32) * t,
425			a33: self.a33 + (rhs.a33 - self.a33) * t,
426			a34: self.a34 + (rhs.a34 - self.a34) * t,
427
428			a41: self.a41 + (rhs.a41 - self.a41) * t,
429			a42: self.a42 + (rhs.a42 - self.a42) * t,
430			a43: self.a43 + (rhs.a43 - self.a43) * t,
431			a44: self.a44 + (rhs.a44 - self.a44) * t,
432		}
433	}
434}
435
436//----------------------------------------------------------------
437// Operators
438
439impl<T: ops::Neg<Output = T>> ops::Neg for Mat4<T> {
440	type Output = Mat4<T>;
441	#[inline]
442	fn neg(self) -> Mat4<T> {
443		Mat4 {
444			a11: -self.a11, a12: -self.a12, a13: -self.a13, a14: -self.a14,
445			a21: -self.a21, a22: -self.a22, a23: -self.a23, a24: -self.a24,
446			a31: -self.a31, a32: -self.a32, a33: -self.a33, a34: -self.a34,
447			a41: -self.a41, a42: -self.a42, a43: -self.a43, a44: -self.a44,
448		}
449	}
450}
451
452impl<T: Copy + ops::Add<Output = T>> ops::Add<Mat4<T>> for Mat4<T> {
453	type Output = Mat4<T>;
454	#[inline]
455	fn add(self, rhs: Mat4<T>) -> Mat4<T> {
456		Mat4 {
457			a11: self.a11 + rhs.a11, a12: self.a12 + rhs.a12, a13: self.a13 + rhs.a13, a14: self.a14 + rhs.a14,
458			a21: self.a21 + rhs.a21, a22: self.a22 + rhs.a22, a23: self.a23 + rhs.a23, a24: self.a24 + rhs.a24,
459			a31: self.a31 + rhs.a31, a32: self.a32 + rhs.a32, a33: self.a33 + rhs.a33, a34: self.a34 + rhs.a34,
460			a41: self.a41 + rhs.a41, a42: self.a42 + rhs.a42, a43: self.a43 + rhs.a43, a44: self.a44 + rhs.a44,
461		}
462	}
463}
464impl<T: Copy + ops::AddAssign> ops::AddAssign<Mat4<T>> for Mat4<T> {
465	#[inline]
466	fn add_assign(&mut self, rhs: Mat4<T>) {
467		self.a11 += rhs.a11; self.a12 += rhs.a12; self.a13 += rhs.a13; self.a14 += rhs.a14;
468		self.a21 += rhs.a21; self.a22 += rhs.a22; self.a23 += rhs.a23; self.a24 += rhs.a24;
469		self.a31 += rhs.a31; self.a32 += rhs.a32; self.a33 += rhs.a33; self.a34 += rhs.a34;
470		self.a41 += rhs.a41; self.a42 += rhs.a42; self.a43 += rhs.a43; self.a44 += rhs.a44;
471	}
472}
473impl<T: Copy + ops::Sub<Output = T>> ops::Sub<Mat4<T>> for Mat4<T> {
474	type Output = Mat4<T>;
475	#[inline]
476	fn sub(self, rhs: Mat4<T>) -> Mat4<T> {
477		Mat4 {
478			a11: self.a11 - rhs.a11, a12: self.a12 - rhs.a12, a13: self.a13 - rhs.a13, a14: self.a14 - rhs.a14,
479			a21: self.a21 - rhs.a21, a22: self.a22 - rhs.a22, a23: self.a23 - rhs.a23, a24: self.a24 - rhs.a24,
480			a31: self.a31 - rhs.a31, a32: self.a32 - rhs.a32, a33: self.a33 - rhs.a33, a34: self.a34 - rhs.a34,
481			a41: self.a41 - rhs.a41, a42: self.a42 - rhs.a42, a43: self.a43 - rhs.a43, a44: self.a44 - rhs.a44,
482		}
483	}
484}
485impl<T: Copy + ops::SubAssign> ops::SubAssign<Mat4<T>> for Mat4<T> {
486	#[inline]
487	fn sub_assign(&mut self, rhs: Mat4<T>) {
488		self.a11 -= rhs.a11; self.a12 -= rhs.a12; self.a13 -= rhs.a13; self.a14 -= rhs.a14;
489		self.a21 -= rhs.a21; self.a22 -= rhs.a22; self.a23 -= rhs.a23; self.a24 -= rhs.a24;
490		self.a31 -= rhs.a31; self.a32 -= rhs.a32; self.a33 -= rhs.a33; self.a34 -= rhs.a34;
491		self.a41 -= rhs.a41; self.a42 -= rhs.a42; self.a43 -= rhs.a43; self.a44 -= rhs.a44;
492	}
493}
494
495impl<T: Copy + ops::Mul<Output = T>> ops::Mul<T> for Mat4<T> {
496	type Output = Mat4<T>;
497	#[inline]
498	fn mul(self, rhs: T) -> Mat4<T> {
499		Mat4 {
500			a11: self.a11 * rhs, a12: self.a12 * rhs, a13: self.a13 * rhs, a14: self.a14 * rhs,
501			a21: self.a21 * rhs, a22: self.a22 * rhs, a23: self.a23 * rhs, a24: self.a24 * rhs,
502			a31: self.a31 * rhs, a32: self.a32 * rhs, a33: self.a33 * rhs, a34: self.a34 * rhs,
503			a41: self.a41 * rhs, a42: self.a42 * rhs, a43: self.a43 * rhs, a44: self.a44 * rhs,
504		}
505	}
506}
507impl<T: Copy + ops::MulAssign> ops::MulAssign<T> for Mat4<T> {
508	#[inline]
509	fn mul_assign(&mut self, rhs: T) {
510		self.a11 *= rhs; self.a12 *= rhs; self.a13 *= rhs; self.a14 *= rhs;
511		self.a21 *= rhs; self.a22 *= rhs; self.a23 *= rhs; self.a24 *= rhs;
512		self.a31 *= rhs; self.a32 *= rhs; self.a33 *= rhs; self.a34 *= rhs;
513		self.a41 *= rhs; self.a42 *= rhs; self.a43 *= rhs; self.a44 *= rhs;
514	}
515}
516
517impl<T: Copy + ops::Add<Output = T> + ops::Mul<Output = T>> ops::Mul<Vec4<T>> for Mat4<T> {
518	type Output = Vec4<T>;
519	#[inline]
520	fn mul(self, rhs: Vec4<T>) -> Vec4<T> {
521		Vec4 {
522			x: self.a11 * rhs.x + self.a12 * rhs.y + self.a13 * rhs.z + self.a14 * rhs.w,
523			y: self.a21 * rhs.x + self.a22 * rhs.y + self.a23 * rhs.z + self.a24 * rhs.w,
524			z: self.a31 * rhs.x + self.a32 * rhs.y + self.a33 * rhs.z + self.a34 * rhs.w,
525			w: self.a41 * rhs.x + self.a42 * rhs.y + self.a43 * rhs.z + self.a44 * rhs.w,
526		}
527	}
528}
529
530impl<T: Copy + ops::Add<Output = T> + ops::Mul<Output = T>> ops::Mul<Mat4<T>> for Mat4<T> {
531	type Output = Mat4<T>;
532	#[inline]
533	fn mul(self, rhs: Mat4<T>) -> Mat4<T> {
534		Mat4 {
535			a11: self.a11 * rhs.a11 + self.a12 * rhs.a21 + self.a13 * rhs.a31 + self.a14 * rhs.a41,
536			a12: self.a11 * rhs.a12 + self.a12 * rhs.a22 + self.a13 * rhs.a32 + self.a14 * rhs.a42,
537			a13: self.a11 * rhs.a13 + self.a12 * rhs.a23 + self.a13 * rhs.a33 + self.a14 * rhs.a43,
538			a14: self.a11 * rhs.a14 + self.a12 * rhs.a24 + self.a13 * rhs.a34 + self.a14 * rhs.a44,
539
540			a21: self.a21 * rhs.a11 + self.a22 * rhs.a21 + self.a23 * rhs.a31 + self.a24 * rhs.a41,
541			a22: self.a21 * rhs.a12 + self.a22 * rhs.a22 + self.a23 * rhs.a32 + self.a24 * rhs.a42,
542			a23: self.a21 * rhs.a13 + self.a22 * rhs.a23 + self.a23 * rhs.a33 + self.a24 * rhs.a43,
543			a24: self.a21 * rhs.a14 + self.a22 * rhs.a24 + self.a23 * rhs.a34 + self.a24 * rhs.a44,
544
545			a31: self.a31 * rhs.a11 + self.a32 * rhs.a21 + self.a33 * rhs.a31 + self.a34 * rhs.a41,
546			a32: self.a31 * rhs.a12 + self.a32 * rhs.a22 + self.a33 * rhs.a32 + self.a34 * rhs.a42,
547			a33: self.a31 * rhs.a13 + self.a32 * rhs.a23 + self.a33 * rhs.a33 + self.a34 * rhs.a43,
548			a34: self.a31 * rhs.a14 + self.a32 * rhs.a24 + self.a33 * rhs.a34 + self.a34 * rhs.a44,
549
550			a41: self.a41 * rhs.a11 + self.a42 * rhs.a21 + self.a43 * rhs.a31 + self.a44 * rhs.a41,
551			a42: self.a41 * rhs.a12 + self.a42 * rhs.a22 + self.a43 * rhs.a32 + self.a44 * rhs.a42,
552			a43: self.a41 * rhs.a13 + self.a42 * rhs.a23 + self.a43 * rhs.a33 + self.a44 * rhs.a43,
553			a44: self.a41 * rhs.a14 + self.a42 * rhs.a24 + self.a43 * rhs.a34 + self.a44 * rhs.a44,
554		}
555	}
556}
557impl<T: Copy + ops::Add<Output = T> + ops::Mul<Output = T>> ops::MulAssign<Mat4<T>> for Mat4<T> {
558	#[inline]
559	fn mul_assign(&mut self, rhs: Mat4<T>) {
560		*self = *self * rhs;
561	}
562}
563
564impl<T: Copy + ops::Add<Output = T> + ops::Mul<Output = T>> ops::Mul<Transform3<T>> for Mat4<T> {
565	type Output = Mat4<T>;
566	#[inline]
567	fn mul(self, rhs: Transform3<T>) -> Mat4<T> {
568		Mat4 {
569			a11: self.a11 * rhs.a11 + self.a12 * rhs.a21 + self.a13 * rhs.a31,
570			a12: self.a11 * rhs.a12 + self.a12 * rhs.a22 + self.a13 * rhs.a32,
571			a13: self.a11 * rhs.a13 + self.a12 * rhs.a23 + self.a13 * rhs.a33,
572			a14: self.a11 * rhs.a14 + self.a12 * rhs.a24 + self.a13 * rhs.a34 + self.a14,
573
574			a21: self.a21 * rhs.a11 + self.a22 * rhs.a21 + self.a23 * rhs.a31,
575			a22: self.a21 * rhs.a12 + self.a22 * rhs.a22 + self.a23 * rhs.a32,
576			a23: self.a21 * rhs.a13 + self.a22 * rhs.a23 + self.a23 * rhs.a33,
577			a24: self.a21 * rhs.a14 + self.a22 * rhs.a24 + self.a23 * rhs.a34 + self.a24,
578
579			a31: self.a31 * rhs.a11 + self.a32 * rhs.a21 + self.a33 * rhs.a31,
580			a32: self.a31 * rhs.a12 + self.a32 * rhs.a22 + self.a33 * rhs.a32,
581			a33: self.a31 * rhs.a13 + self.a32 * rhs.a23 + self.a33 * rhs.a33,
582			a34: self.a31 * rhs.a14 + self.a32 * rhs.a24 + self.a33 * rhs.a34 + self.a34,
583
584			a41: self.a41 * rhs.a11 + self.a42 * rhs.a21 + self.a43 * rhs.a31,
585			a42: self.a41 * rhs.a12 + self.a42 * rhs.a22 + self.a43 * rhs.a32,
586			a43: self.a41 * rhs.a13 + self.a42 * rhs.a23 + self.a43 * rhs.a33,
587			a44: self.a41 * rhs.a14 + self.a42 * rhs.a24 + self.a43 * rhs.a34 + self.a44,
588		}
589	}
590}
591impl<T: Copy + ops::Add<Output = T> + ops::Mul<Output = T>> ops::MulAssign<Transform3<T>> for Mat4<T> {
592	#[inline]
593	fn mul_assign(&mut self, rhs: Transform3<T>) {
594		*self = *self * rhs;
595	}
596}
597
598/// Inverts the matrix using the gluInvertMatrix algorithm.
599/// Returns Some(inverse) or None if non-invertible.
600#[inline(always)]
601fn glu_invert<T: Float>(this: &Mat4<T>) -> Option<Mat4<T>> {
602	let m = [
603		this.a11, this.a12, this.a13, this.a14,
604		this.a21, this.a22, this.a23, this.a24,
605		this.a31, this.a32, this.a33, this.a34,
606		this.a41, this.a42, this.a43, this.a44,
607	];
608
609	let mut inv = [T::ZERO; 16];
610
611	inv[0] =  m[5]  * m[10] * m[15] - m[5]  * m[11] * m[14] - m[9]  * m[6]  * m[15]
612	        + m[9]  * m[7]  * m[14] + m[13] * m[6]  * m[11] - m[13] * m[7]  * m[10];
613
614	inv[4] = -m[4]  * m[10] * m[15] + m[4]  * m[11] * m[14] + m[8]  * m[6]  * m[15]
615	        - m[8]  * m[7]  * m[14] - m[12] * m[6]  * m[11] + m[12] * m[7]  * m[10];
616
617	inv[8] =  m[4]  * m[9]  * m[15] - m[4]  * m[11] * m[13] - m[8]  * m[5]  * m[15]
618	        + m[8]  * m[7]  * m[13] + m[12] * m[5]  * m[11] - m[12] * m[7]  * m[9];
619
620	inv[12] = -m[4]  * m[9]  * m[14] + m[4]  * m[10] * m[13] + m[8]  * m[5]  * m[14]
621	         - m[8]  * m[6]  * m[13] - m[12] * m[5]  * m[10] + m[12] * m[6]  * m[9];
622
623	inv[1] = -m[1]  * m[10] * m[15] + m[1]  * m[11] * m[14] + m[9]  * m[2]  * m[15]
624	        - m[9]  * m[3]  * m[14] - m[13] * m[2]  * m[11] + m[13] * m[3]  * m[10];
625
626	inv[5] =  m[0]  * m[10] * m[15] - m[0]  * m[11] * m[14] - m[8]  * m[2]  * m[15]
627	        + m[8]  * m[3]  * m[14] + m[12] * m[2]  * m[11] - m[12] * m[3]  * m[10];
628
629	inv[9] = -m[0]  * m[9]  * m[15] + m[0]  * m[11] * m[13] + m[8]  * m[1]  * m[15]
630	        - m[8]  * m[3]  * m[13] - m[12] * m[1]  * m[11] + m[12] * m[3]  * m[9];
631
632	inv[13] = m[0]  * m[9]  * m[14] - m[0]  * m[10] * m[13] - m[8]  * m[1]  * m[14]
633	        + m[8]  * m[2]  * m[13] + m[12] * m[1]  * m[10] - m[12] * m[2]  * m[9];
634
635	inv[2] =  m[1]  * m[6]  * m[15] - m[1]  * m[7]  * m[14] - m[5]  * m[2]  * m[15]
636	        + m[5]  * m[3]  * m[14] + m[13] * m[2]  * m[7]  - m[13] * m[3]  * m[6];
637
638	inv[6] = -m[0]  * m[6]  * m[15] + m[0]  * m[7]  * m[14] + m[4]  * m[2]  * m[15]
639	        - m[4]  * m[3]  * m[14] - m[12] * m[2]  * m[7]  + m[12] * m[3]  * m[6];
640
641	inv[10] = m[0]  * m[5]  * m[15] - m[0]  * m[7]  * m[13] - m[4]  * m[1]  * m[15]
642	        + m[4]  * m[3]  * m[13] + m[12] * m[1]  * m[7]  - m[12] * m[3]  * m[5];
643
644	inv[14] = -m[0]  * m[5]  * m[14] + m[0]  * m[6]  * m[13] + m[4]  * m[1]  * m[14]
645	         - m[4]  * m[2]  * m[13] - m[12] * m[1]  * m[6]  + m[12] * m[2]  * m[5];
646
647	inv[3] = -m[1]  * m[6]  * m[11] + m[1]  * m[7]  * m[10] + m[5]  * m[2]  * m[11]
648	        - m[5]  * m[3]  * m[10] - m[9]  * m[2]  * m[7]  + m[9]  * m[3]  * m[6];
649
650	inv[7] =  m[0]  * m[6]  * m[11] - m[0]  * m[7]  * m[10] - m[4]  * m[2]  * m[11]
651	        + m[4]  * m[3]  * m[10] + m[8]  * m[2]  * m[7]  - m[8]  * m[3]  * m[6];
652
653	inv[11] = -m[0]  * m[5]  * m[11] + m[0]  * m[7]  * m[9]  + m[4]  * m[1]  * m[11]
654	         - m[4]  * m[3]  * m[9]  - m[8]  * m[1]  * m[7]  + m[8]  * m[3]  * m[5];
655
656	inv[15] =  m[0]  * m[5]  * m[10] - m[0]  * m[6]  * m[9]  - m[4]  * m[1]  * m[10]
657	         + m[4]  * m[2]  * m[9]  + m[8]  * m[1]  * m[6]  - m[8]  * m[2]  * m[5];
658
659	let det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
660	if det == T::ZERO {
661		return None; // Not invertible
662	}
663
664	let inv_det = T::ONE / det;
665
666	Some(Mat4 {
667		a11: inv[0] * inv_det, a12: inv[1] * inv_det, a13: inv[2] * inv_det, a14: inv[3] * inv_det,
668		a21: inv[4] * inv_det, a22: inv[5] * inv_det, a23: inv[6] * inv_det, a24: inv[7] * inv_det,
669		a31: inv[8] * inv_det, a32: inv[9] * inv_det, a33: inv[10] * inv_det, a34: inv[11] * inv_det,
670		a41: inv[12] * inv_det, a42: inv[13] * inv_det, a43: inv[14] * inv_det, a44: inv[15] * inv_det,
671	})
672}
673
674impl_mat_neg!(Mat4);
675impl_mat_add_mat!(Mat4);
676impl_mat_sub_mat!(Mat4);
677impl_mat_mul_scalar!(Mat4);
678impl_mat_mul_vec!(Mat4, Vec4);
679impl_mat_mul_mat!(Mat4);
680
681//----------------------------------------------------------------
682// Formatting
683
684impl<T: fmt::Display> fmt::Display for Mat4<T> {
685	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
686		f.write_str("Mat4(")?;
687		print::print(&move |i| &self.as_array()[i], 0x44, f)?;
688		f.write_str(")")
689	}
690}
691impl<T: fmt::Debug> fmt::Debug for Mat4<T> {
692	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
693		f.write_str("Mat4(")?;
694		print::print(&move |i| print::Debug(&self.as_array()[i]), 0x44, f)?;
695		f.write_str(")")
696	}
697}
698
699//----------------------------------------------------------------
700// Tests
701
702#[test]
703fn test_inverse() {
704	let mut rng = urandom::seeded(42);
705
706	for _ in 0..1000 {
707		let fov_y = Angle::deg(rng.range(1.0..179.0));
708
709		let aspect = rng.range(0.5..4.0);
710
711		let near = rng.range(0.1..10.0);
712		let far = near + rng.range(10.0..100.0);
713
714		let hand = if rng.coin_flip() { Hand::RH } else { Hand::LH };
715		let clip = if rng.coin_flip() { Clip::NO } else { Clip::ZO };
716
717		let mat = Mat4::perspective(fov_y, aspect, near, far, (hand, clip));
718		let inv = mat.inverse();
719
720		let p = Vec4(
721			rng.range(-10.0..10.0),
722			rng.range(-10.0..10.0),
723			rng.range(near..far),
724			rng.range(0.1..10.0),
725		);
726
727		let projected = mat * p;
728		let unprojected = inv * projected;
729		let _identity = mat * inv;
730
731		let error = (unprojected - p).len();
732		assert!(error < 1e-6, "Failed for fov_y: {fov_y}, aspect: {aspect}, near: {near}, far: {far}, hand: {hand:?}, clip: {clip:?}, p: {p:?}, error: {error}");
733	}
734}
735
736#[test]
737fn test_ortho_inverse() {
738	let near = 10.0;
739	let far = 2000.0;
740	let half_width = 400.0;
741	let half_height = 300.0;
742	let projection = Mat4f::ortho(
743		-half_width, half_width,
744		-half_height, half_height,
745		near, far,
746		(Hand::LH, Clip::NO)
747	);
748
749	dbg!(projection.det());
750
751	let inv_proj = projection.inverse();
752	let identity = projection * inv_proj;
753
754	dbg!(identity);
755
756	let error = (identity.flat_norm_sqr() - 4.0).abs();
757	assert!(error.abs() < 1e-6, "Inverse projection matrix does not yield identity: error = {error}");
758}
759
760#[test]
761fn test_add() {
762	let lhs = Mat4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
763	let rhs = Mat4(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160);
764	let expected = Mat4(11, 22, 33, 44, 55, 66, 77, 88, 99, 110, 121, 132, 143, 154, 165, 176);
765
766	assert_eq!(lhs + rhs, expected);
767	assert_eq!(lhs + &rhs, expected);
768	assert_eq!(&lhs + rhs, expected);
769	assert_eq!(&lhs + &rhs, expected);
770
771	let mut value = lhs;
772	value += rhs;
773	assert_eq!(value, expected);
774
775	let mut value = lhs;
776	value += &rhs;
777	assert_eq!(value, expected);
778}
779
780#[test]
781fn test_sub() {
782	let lhs = Mat4(11, 22, 33, 44, 55, 66, 77, 88, 99, 110, 121, 132, 143, 154, 165, 176);
783	let rhs = Mat4(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160);
784	let expected = Mat4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
785
786	assert_eq!(lhs - rhs, expected);
787	assert_eq!(lhs - &rhs, expected);
788	assert_eq!(&lhs - rhs, expected);
789	assert_eq!(&lhs - &rhs, expected);
790
791	let mut value = lhs;
792	value -= rhs;
793	assert_eq!(value, expected);
794
795	let mut value = lhs;
796	value -= &rhs;
797	assert_eq!(value, expected);
798}
799
800#[test]
801fn test_neg() {
802	let value = Mat4(1, -2, 3, -4, 5, -6, 7, -8, 9, -10, 11, -12, 13, -14, 15, -16);
803	let expected = Mat4(-1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15, 16);
804	assert_eq!(-value, expected);
805	assert_eq!(-&value, expected);
806}