glium_types/matrices/
dmat4.rs

1use glium::uniforms::AsUniformValue;
2
3use crate::{matrices::DMat3, quaternions::DQuat, vectors::{dvec3, DVec4, DVec3}};
4
5use super::DMat2;
6
7#[derive(Clone, Copy, PartialEq, Debug)]
8/// a matrix often used for transformations in glium.
9pub struct DMat4 {
10    matrix: [[f64; 4]; 4]
11}
12impl DMat4{
13    pub const IDENTITY: Self = DMat4::from_values(
14        1.0, 0.0, 0.0, 0.0,
15        0.0, 1.0, 0.0, 0.0,
16        0.0, 0.0, 1.0, 0.0,
17        0.0, 0.0, 0.0, 1.0
18    );
19    pub const fn from_scale(scale: DVec3) -> Self{
20        let (x, y, z) = (scale.x, scale.y, scale.z);
21        DMat4::from_values(
22            x, 0.0, 0.0, 0.0,
23            0.0, y, 0.0, 0.0,
24            0.0, 0.0, z, 0.0,
25            0.0, 0.0, 0.0, 1.0
26        )
27    }
28    pub const fn from_column_major_array(array: [[f64; 4]; 4]) -> Self { Self { matrix: array } }
29    pub const fn into_column_major_array(self) -> [[f64; 4]; 4] { self.matrix }
30    pub const fn from_row_major_array(array: [[f64; 4]; 4]) -> Self { Self { matrix: array }.transpose() }
31    pub const fn into_row_major_array(self) -> [[f64; 4]; 4] { self.transpose().matrix }
32    /// transform from position, scale and rotation. much quicker than multiplying
33    ///  position * rot * scale matrices while having the same result.
34    pub fn from_transform(pos: DVec3, scale: DVec3, rot: DQuat) -> Self {
35        let DQuat { r, i, j, k } = rot;
36        let (sx, sy, sz) = (scale.x * 2.0, scale.y * 2.0, scale.z * 2.0);
37        DMat4::from_values(
38            sx*(0.5 - (j*j + k*k)), sy*(i*j - k*r), sz*(i*k + j*r), pos.x,
39            sx*(i*j + k*r), sy*(0.5 - (i*i + k*k)), sz*(j*k - i*r), pos.y,
40            sx*(i*k - j*r), sy*(j*k + i*r), sz*(0.5 - (i*i + j*j)), pos.z,   
41            0.0, 0.0, 0.0, 1.0
42        )
43    }
44    /// makes the inverse of `from_transform`, useful for making camera transform.
45    pub fn from_inverse_transform(pos: DVec3, scale: DVec3, rot: DQuat) -> Self {
46        let DQuat { r, i, j, k } = rot;
47        let DVec3 { x, y, z } = DVec3::ONE / scale;
48
49        let scalar = r*r + i*i + j*j + k*k;
50        let s = 2.0/(scalar*scalar);
51        let sx = s*x;
52        let sy = s*y;
53        let sz = s*z;
54        let a = x - sx*(j*j + k*k);
55        let b = sx*(i*j + k*r);
56        let c = sx*(i*k - j*r);
57        let d = sy*(i*j - k*r);
58        let e = y - sy*(i*i + k*k);
59        let f = sy*(j*k + i*r);
60        let g = sz*(i*k + j*r);
61        let h = sz*(j*k - i*r);
62        let i = z - sz*(i*i + j*j);
63        DMat4::from_values(
64            a, b, c, -a*pos.x - b*pos.y - c*pos.z,
65            d, e, f, -d*pos.x - e*pos.y - f*pos.z,
66            g, h, i, -g*pos.x - h*pos.y - i*pos.z,
67            0.0, 0.0, 0.0, 1.0
68        )
69    }
70    pub fn from_pos_and_rot(pos: DVec3, rot: DQuat) -> Self {
71        let (r, i, j, k) = (rot.r, rot.i, rot.j, rot.k);
72        DMat4::from_values(
73            1.0 - 2.0*(j*j + k*k), 2.0*(i*j - k*r), 2.0*(i*k + j*r), pos.x,
74            2.0*(i*j + k*r), 1.0 - 2.0*(i*i + k*k), 2.0*(j*k - i*r), pos.y,
75            2.0*(i*k - j*r), 2.0*(j*k + i*r), 1.0 - 2.0*(i*i + j*j), pos.z,   
76            0.0, 0.0, 0.0, 1.0
77        )
78    }
79    pub const fn from_pos_and_scale(pos: DVec3, scale: DVec3) -> Self {
80        DMat4::from_values(
81            scale.x, 0.0, 0.0, pos.x,
82            0.0, scale.y, 0.0, pos.y,
83            0.0, 0.0, scale.z, pos.z,
84            0.0, 0.0, 0.0, 1.0
85        )
86    }
87    pub fn from_scale_and_rot(scale: DVec3, rot: DQuat) -> Self {
88        let (r, i, j, k) = (rot.r, rot.i, rot.j, rot.k);
89        let (sx, sy, sz) = (scale.x * 2.0, scale.y * 2.0, scale.z * 2.0);
90        DMat4::from_values(
91            sx*(0.5 - (j*j + k*k)), sy*(i*j - k*r), sz*(i*k + j*r), 0.0,
92            sx*(i*j + k*r), sy*(0.5 - (i*i + k*k)), sz*(j*k - i*r), 0.0,
93            sx*(i*k - j*r), sy*(j*k + i*r), sz*(0.5 - (i*i + j*j)), 0.0,   
94            0.0, 0.0, 0.0, 1.0
95        )
96    }
97    pub const fn from_pos(pos: DVec3) -> Self {
98        let (x, y, z) = (pos.x, pos.y, pos.z);
99        DMat4::from_values(
100            1.0, 0.0, 0.0, x,
101            0.0, 1.0, 0.0, y,
102            0.0, 0.0, 1.0, z,
103            0.0, 0.0, 0.0, 1.0
104        )
105    }
106    pub fn from_rot(rot: DQuat) -> Self {
107        rot.into()
108    }
109    /// rotates the matrixs components
110    pub const fn transpose(self) -> Self {
111        let DMat4 { matrix: [
112            [a, e, i, m],
113            [b, f, j, n],
114            [c, g, k, o],
115            [d, h, l, p]
116        ] } = self;
117        DMat4::from_values(
118            a, e, i, m,
119            b, f, j, n,
120            c, g, k, o,
121            d, h, l, p
122        )
123    }
124    /// creates a 3d perspective matrix. known as `perspective` in the supplied vertex shader
125    pub fn perspective_3d(window_dimesnsions: (u32, u32), fov: f64, zfar: f64, znear: f64) -> Self {
126        let (width, height) = window_dimesnsions;
127        let aspect_ratio = height as f64 / width as f64;
128        let f = 1.0 / (fov / 2.0).tan();
129        Self{
130            matrix: [
131                [f * aspect_ratio, 0.0,              0.0              , 0.0],
132                [         0.0    ,  f ,              0.0              , 0.0],
133                [         0.0    , 0.0,  (zfar+znear)/(zfar-znear)    , 1.0],
134                [         0.0    , 0.0, -(2.0*zfar*znear)/(zfar-znear), 0.0],
135            ]
136        }
137    }
138    /// creates a 2d perspective matrix. known as `perspective` in the supplied vertex shader
139    pub fn perspective_2d(window_dimesnsions: (u32, u32)) -> Self {
140        let (width, height) = window_dimesnsions;
141        let aspect_ratio = height as f64 / width as f64;
142        DMat4::from_scale(dvec3(aspect_ratio, 1.0, 1.0))
143    }
144    /// creates a matrix with the following values
145    /// ```
146    /// use glium_types::matrices::DMat4;
147    /// let new_matrix = DMat4::from_values(
148    ///     1.0, 0.0, 0.0, 0.0,
149    ///     0.0, 1.0, 0.0, 0.0,
150    ///     0.0, 0.0, 1.0, 0.0,
151    ///     0.0, 0.0, 0.0, 1.0
152    /// );
153    /// assert!(new_matrix == DMat4::IDENTITY);
154    /// ```
155    #[allow(clippy::too_many_arguments)]
156    pub const fn from_values(
157        a: f64, b: f64, c: f64, d: f64,
158        e: f64, f: f64, g: f64, h: f64,
159        i: f64, j: f64, k: f64, l: f64,
160        m: f64, n: f64, o: f64, p: f64
161    ) -> Self {
162        Self{
163            // opengl uses a diffent matrix format to the input. this is why the order is shifted.
164            matrix: [
165                [a, e, i, m],
166                [b, f, j, n],
167                [c, g, k, o],
168                [d, h, l, p]
169            ]
170        }
171    }
172    pub const fn column(&self, pos: usize) -> [f64; 4] {
173        self.matrix[pos]
174    }
175    pub const fn row(&self, pos: usize) -> [f64; 4] {
176        let matrix = self.matrix;
177        [matrix[0][pos], matrix[1][pos], matrix[2][pos], matrix[3][pos]]
178    }
179    pub fn position(&self) -> DVec3 {
180        dvec3(self[3][0], self[3][1], self[3][2])
181    }
182    pub fn determinant(self) -> f64 {
183        let a = self;
184        let s0 = a[0][0] * a[1][1] - a[1][0] * a[0][1];
185        let s1 = a[0][0] * a[1][2] - a[1][0] * a[0][2];
186        let s2 = a[0][0] * a[1][3] - a[1][0] * a[0][3];
187        let s3 = a[0][1] * a[1][2] - a[1][1] * a[0][2];
188        let s4 = a[0][1] * a[1][3] - a[1][1] * a[0][3];
189        let s5 = a[0][2] * a[1][3] - a[1][2] * a[0][3];
190
191        let c5 = a[2][2] * a[3][3] - a[3][2] * a[2][3];
192        let c4 = a[2][1] * a[3][3] - a[3][1] * a[2][3];
193        let c3 = a[2][1] * a[3][2] - a[3][1] * a[2][2];
194        let c2 = a[2][0] * a[3][3] - a[3][0] * a[2][3];
195        let c1 = a[2][0] * a[3][2] - a[3][0] * a[2][2];
196        let c0 = a[2][0] * a[3][1] - a[3][0] * a[2][1];
197
198        s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0
199    }
200    /// get inverse of matrix. useful for camera.
201    pub fn inverse(self) -> Self {
202        let a = self;
203        let s0 = a[0][0] * a[1][1] - a[1][0] * a[0][1];
204        let s1 = a[0][0] * a[1][2] - a[1][0] * a[0][2];
205        let s2 = a[0][0] * a[1][3] - a[1][0] * a[0][3];
206        let s3 = a[0][1] * a[1][2] - a[1][1] * a[0][2];
207        let s4 = a[0][1] * a[1][3] - a[1][1] * a[0][3];
208        let s5 = a[0][2] * a[1][3] - a[1][2] * a[0][3];
209
210        let c5 = a[2][2] * a[3][3] - a[3][2] * a[2][3];
211        let c4 = a[2][1] * a[3][3] - a[3][1] * a[2][3];
212        let c3 = a[2][1] * a[3][2] - a[3][1] * a[2][2];
213        let c2 = a[2][0] * a[3][3] - a[3][0] * a[2][3];
214        let c1 = a[2][0] * a[3][2] - a[3][0] * a[2][2];
215        let c0 = a[2][0] * a[3][1] - a[3][0] * a[2][1];
216
217        let invdet = 1.0 / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0);
218
219        let mut b = DMat4::IDENTITY;
220        
221        b[0][0] = ( a[1][1] * c5 - a[1][2] * c4 + a[1][3] * c3) * invdet;
222        b[0][1] = (-a[0][1] * c5 + a[0][2] * c4 - a[0][3] * c3) * invdet;
223        b[0][2] = ( a[3][1] * s5 - a[3][2] * s4 + a[3][3] * s3) * invdet;
224        b[0][3] = (-a[2][1] * s5 + a[2][2] * s4 - a[2][3] * s3) * invdet;
225
226        b[1][0] = (-a[1][0] * c5 + a[1][2] * c2 - a[1][3] * c1) * invdet;
227        b[1][1] = ( a[0][0] * c5 - a[0][2] * c2 + a[0][3] * c1) * invdet;
228        b[1][2] = (-a[3][0] * s5 + a[3][2] * s2 - a[3][3] * s1) * invdet;
229        b[1][3] = ( a[2][0] * s5 - a[2][2] * s2 + a[2][3] * s1) * invdet;
230
231        b[2][0] = ( a[1][0] * c4 - a[1][1] * c2 + a[1][3] * c0) * invdet;
232        b[2][1] = (-a[0][0] * c4 + a[0][1] * c2 - a[0][3] * c0) * invdet;
233        b[2][2] = ( a[3][0] * s4 - a[3][1] * s2 + a[3][3] * s0) * invdet;
234        b[2][3] = (-a[2][0] * s4 + a[2][1] * s2 - a[2][3] * s0) * invdet;
235
236        b[3][0] = (-a[1][0] * c3 + a[1][1] * c1 - a[1][2] * c0) * invdet;
237        b[3][1] = ( a[0][0] * c3 - a[0][1] * c1 + a[0][2] * c0) * invdet;
238        b[3][2] = (-a[3][0] * s3 + a[3][1] * s1 - a[3][2] * s0) * invdet;
239        b[3][3] = ( a[2][0] * s3 - a[2][1] * s1 + a[2][2] * s0) * invdet;
240
241        b
242    }
243    pub fn scale(self, scalar: f64) -> DMat4 {
244        let DMat4 { matrix: [
245            [a, e, i, m],
246            [b, f, j, n],
247            [c, g, k, o],
248            [d, h, l, p]
249        ] } = self;
250        DMat4::from_values(
251            a*scalar, b*scalar, c*scalar, d*scalar,
252            e*scalar, f*scalar, g*scalar, h*scalar,
253            i*scalar, j*scalar, k*scalar, l*scalar,
254            m*scalar, n*scalar, o*scalar, p*scalar
255        )
256    }
257}
258impl Default for DMat4{
259    fn default() -> Self {
260        Self { matrix: [
261            [1.0, 0.0, 0.0, 0.0],
262            [0.0, 1.0, 0.0, 0.0],
263            [0.0, 0.0, 1.0, 0.0],
264            [0.0, 0.0, 0.0, 1.0],
265        ] }
266    }
267}
268impl std::ops::Mul<DVec4> for DMat4 {
269    fn mul(self, rhs: DVec4) -> Self::Output { rhs.transform(self) }
270    type Output = DVec4;
271}
272impl std::ops::Mul<DMat4> for f64 {
273    fn mul(self, rhs: DMat4) -> Self::Output { rhs * self }
274    type Output = DMat4;
275}
276impl std::ops::Mul<f64> for DMat4 {
277    fn mul(self, rhs: f64) -> Self::Output { self.scale(rhs) }
278    type Output = Self;
279}
280impl std::ops::MulAssign<f64> for DMat4 {
281    fn mul_assign(&mut self, rhs: f64) { *self = *self * rhs }
282}
283impl std::ops::Div<DMat4> for f64 {
284    fn div(self, rhs: DMat4) -> Self::Output {
285        let DMat4 { matrix: [
286            [a, e, i, m],
287            [b, f, j, n],
288            [c, g, k, o],
289            [d, h, l, p]
290        ] } = rhs;
291        DMat4::from_values(
292            self/a, self/b, self/c, self/d,
293            self/e, self/f, self/g, self/h,
294            self/i, self/j, self/k, self/l,
295            self/m, self/n, self/o, self/p
296        )
297    }
298    type Output = DMat4;
299}
300impl std::ops::Div<f64> for DMat4 {
301    fn div(self, rhs: f64) -> Self::Output { self.scale(1.0/rhs) }
302    type Output = Self;
303}
304impl std::ops::DivAssign<f64> for DMat4 {
305    fn div_assign(&mut self, rhs: f64) { *self = *self / rhs }
306}
307impl std::ops::Rem<DMat4> for f64 {
308    fn rem(self, rhs: DMat4) -> Self::Output {
309        let DMat4 { matrix: [
310            [a, e, i, m],
311            [b, f, j, n],
312            [c, g, k, o],
313            [d, h, l, p]
314        ] } = rhs;
315        DMat4::from_values(
316            self%a, self%b, self%c, self%d,
317            self%e, self%f, self%g, self%h,
318            self%i, self%j, self%k, self%l,
319            self%m, self%n, self%o, self%p
320        )
321    }
322    type Output = DMat4;
323}
324impl std::ops::Rem<f64> for DMat4 {
325    fn rem(self, rhs: f64) -> Self::Output {
326        let DMat4 { matrix: [
327            [a, e, i, m],
328            [b, f, j, n],
329            [c, g, k, o],
330            [d, h, l, p]
331        ] } = self;
332        DMat4::from_values(
333            a%rhs, b%rhs, c%rhs, d%rhs,
334            e%rhs, f%rhs, g%rhs, h%rhs,
335            i%rhs, j%rhs, k%rhs, l%rhs,
336            m%rhs, n%rhs, o%rhs, p%rhs
337        )
338    }
339    type Output = Self;
340}
341impl std::ops::RemAssign<f64> for DMat4 {
342    fn rem_assign(&mut self, rhs: f64) { *self = *self % rhs }
343}
344#[allow(clippy::needless_range_loop)] // in this case i think it looks nicer :)
345impl std::ops::Mul for DMat4{
346    fn mul(self, rhs: Self) -> Self::Output {
347        let mut matrix = [[0.0; 4];  4];
348        for x in 0..4{
349            for y in 0..4{
350                matrix[x][y] = DVec4::dot(self.row(y).into(), rhs.column(x).into());
351            }
352        }
353        Self {
354            matrix
355        }
356    }
357    type Output = Self;
358}
359impl std::ops::MulAssign for DMat4 {
360    fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs }
361}
362#[allow(clippy::suspicious_arithmetic_impl)]
363impl std::ops::Div for DMat4 {
364    fn div(self, rhs: Self) -> Self::Output { self * rhs.inverse() }
365    type Output = Self;
366}
367impl std::ops::DivAssign for DMat4 {
368    fn div_assign(&mut self, rhs: Self) { *self = *self / rhs }
369}
370impl AsUniformValue for DMat4 {
371    fn as_uniform_value(&self) -> glium::uniforms::UniformValue<'_> {
372        glium::uniforms::UniformValue::DoubleMat4(self.matrix)
373    }
374}
375impl std::ops::Add for DMat4 {
376    fn add(self, rhs: Self) -> Self::Output {
377        let a = self.matrix;
378        let b = rhs.matrix;
379        Self{
380            matrix:[
381                [a[0][0] + b[0][0], a[0][1] + b[0][1], a[0][2] + b[0][2], a[0][3] + b[0][3]],
382                [a[1][0] + b[1][0], a[1][1] + b[1][1], a[1][2] + b[1][2], a[1][3] + b[1][3]],
383                [a[2][0] + b[2][0], a[2][1] + b[2][1], a[2][2] + b[2][2], a[2][3] + b[2][3]],
384                [a[3][0] + b[3][0], a[3][1] + b[3][1], a[3][2] + b[3][2], a[3][3] + b[3][3]],
385            ]
386        }
387    }
388    type Output = Self;
389}
390impl std::ops::AddAssign for DMat4 {
391    fn add_assign(&mut self, rhs: Self) {
392        *self = *self + rhs
393    }
394}
395impl std::ops::Sub for DMat4 {
396    fn sub(self, rhs: Self) -> Self::Output {
397        let a = self.matrix;
398        let b = rhs.matrix;
399        Self{
400            matrix:[
401                [a[0][0] - b[0][0], a[0][1] - b[0][1], a[0][2] - b[0][2], a[0][3] - b[0][3]],
402                [a[1][0] - b[1][0], a[1][1] - b[1][1], a[1][2] - b[1][2], a[1][3] - b[1][3]],
403                [a[2][0] - b[2][0], a[2][1] - b[2][1], a[2][2] - b[2][2], a[2][3] - b[2][3]],
404                [a[3][0] - b[3][0], a[3][1] - b[3][1], a[3][2] - b[3][2], a[3][3] - b[3][3]],
405            ]
406        }
407    }
408    type Output = Self;
409}
410impl std::ops::SubAssign for DMat4 {
411    fn sub_assign(&mut self, rhs: Self) {
412        *self = *self - rhs
413    }
414}
415impl From<DMat3> for DMat4 {
416    fn from(value: DMat3) -> Self {
417        Self::from_values(
418            value[0][0], value[0][1], value[0][2], 0.0,
419            value[1][0], value[1][1], value[1][2], 0.0,
420            value[2][0], value[2][1], value[2][2], 0.0,
421            0.0,         0.0,         0.0,         1.0
422        )
423    }
424}
425impl From<DMat2> for DMat4 {
426    fn from(value: DMat2) -> Self {
427        Self::from_values(
428            value[0][0], value[0][1], 0.0, 0.0,
429            value[1][0], value[1][1], 0.0, 0.0,
430            0.0,         0.0,         1.0, 0.0,
431            0.0,         0.0,         0.0, 1.0
432        )
433    }
434}
435impl From<DQuat> for DMat4 {
436    fn from(value: DQuat) -> DMat4 {
437        let DQuat { r, i, j, k } = value;
438         DMat4::from_values(
439            1.0 - 2.0*(j*j + k*k), 2.0*(i*j - k*r), 2.0*(i*k + j*r), 0.0,
440            2.0*(i*j + k*r), 1.0 - 2.0*(i*i + k*k), 2.0*(j*k - i*r), 0.0,
441            2.0*(i*k - j*r), 2.0*(j*k + i*r), 1.0 - 2.0*(i*i + j*j), 0.0,   
442            0.0, 0.0, 0.0, 1.0
443        )
444    }
445}
446impl std::ops::Index<usize> for DMat4 {
447    fn index(&self, index: usize) -> &Self::Output {
448        &self.matrix[index]
449    }
450    
451    type Output = [f64; 4];
452}
453impl std::ops::IndexMut<usize> for DMat4 {
454    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
455        &mut self.matrix[index]
456    }
457}
458#[test]
459fn test_from_inverse_transform() {
460    let rot = DQuat::from_x_rot(1.3);
461    let pos = dvec3(1.0, 2.0, 0.3);
462    let scale = dvec3(1.1, 2.0, 3.9);
463    let transform = DMat4::from_transform(pos, scale, rot);
464    let inv_transform = DMat4::from_inverse_transform(pos, scale, rot);
465    let result = transform * inv_transform;
466    assert!(eq_mats(DMat4::IDENTITY, result));
467}
468#[test]
469fn mat4_inverse() {
470    let rot = DQuat::from_x_rot(1.3);
471    let pos = dvec3(1.0, 2.0, 0.3);
472    let scale = dvec3(1.1, 2.0, 3.9);
473    let a = DMat4::from_transform(pos, scale, rot);
474    assert!(eq_mats(DMat4::IDENTITY, a.inverse() * a));
475}
476#[test]
477fn test_transform(){
478    let rot = DQuat::from_x_rot(1.3);
479    let pos = dvec3(1.0, 2.0, 0.3);
480    let scale = dvec3(1.1, 2.0, 3.9);
481    let transform = DMat4::from_transform(pos, scale, rot);
482    let result = DMat4::from_pos(pos) * DMat4::from_rot(rot) * DMat4::from_scale(scale);
483    assert!(eq_mats(transform, result))
484}
485#[allow(dead_code)]
486fn eq_mats(a: DMat4, b: DMat4) -> bool {
487    for x in 0..4 {
488        for y in 0..4 {
489            if f64::abs(a[x][y] - b[x][y]) > f64::EPSILON {
490                return false;
491            }
492        }
493    }
494    true
495}