1use crate::{dcos, dsin, dtan, Pt3, Pt4};
25
26#[derive(Clone, Copy, Default, PartialEq)]
28pub struct Mt4 {
29 pub x: Pt4,
30 pub y: Pt4,
31 pub z: Pt4,
32 pub w: Pt4,
33}
34
35impl std::fmt::Display for Mt4 {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 writeln!(f, "| {} {} {} {} |", self.x.x, self.y.x, self.z.x, self.w.x)?;
38 writeln!(f, "| {} {} {} {} |", self.x.y, self.y.y, self.z.y, self.w.y)?;
39 writeln!(f, "| {} {} {} {} |", self.x.z, self.y.z, self.z.z, self.w.z)?;
40 writeln!(f, "| {} {} {} {} |", self.x.w, self.y.w, self.z.w, self.w.w)
41 }
42}
43
44impl Mt4 {
45 pub fn new(x: Pt4, y: Pt4, z: Pt4, w: Pt4) -> Self {
46 Self { x, y, z, w }
47 }
48
49 pub fn transposed(&self) -> Self {
50 Mt4::new(
51 Pt4::new(self.x.x, self.y.x, self.z.x, self.w.x),
52 Pt4::new(self.x.y, self.y.y, self.z.y, self.w.y),
53 Pt4::new(self.x.z, self.y.z, self.z.z, self.w.z),
54 Pt4::new(self.x.w, self.y.w, self.z.w, self.w.w),
55 )
56 }
57
58 pub fn identity() -> Self {
59 Mt4::new(
60 Pt4::new(1.0, 0.0, 0.0, 0.0),
61 Pt4::new(0.0, 1.0, 0.0, 0.0),
62 Pt4::new(0.0, 0.0, 1.0, 0.0),
63 Pt4::new(0.0, 0.0, 0.0, 1.0),
64 )
65 }
66
67 pub fn scale_matrix(x: f64, y: f64, z: f64) -> Self {
68 let mut result = Mt4::identity();
69 result.x.x = x;
70 result.y.y = y;
71 result.z.z = z;
72 result
73 }
74
75 pub fn translate_matrix(x: f64, y: f64, z: f64) -> Self {
76 let mut result = Mt4::identity();
77 result.w.x = x;
78 result.w.y = y;
79 result.w.z = z;
80 result
81 }
82
83 pub fn rot_x_matrix(degrees: f64) -> Self {
84 let c = dcos(degrees);
85 let s = dsin(degrees);
86 Mt4::new(
87 Pt4::new(1.0, 0.0, 0.0, 0.0),
88 Pt4::new(0.0, c, -s, 0.0),
89 Pt4::new(0.0, s, c, 0.0),
90 Pt4::new(0.0, 0.0, 0.0, 1.0),
91 )
92 .transposed()
93 }
94
95 pub fn rot_y_matrix(degrees: f64) -> Self {
96 let c = dcos(degrees);
97 let s = dsin(degrees);
98 Mt4::new(
99 Pt4::new(c, 0.0, s, 0.0),
100 Pt4::new(0.0, 1.0, 0.0, 0.0),
101 Pt4::new(-s, 0.0, c, 0.0),
102 Pt4::new(0.0, 0.0, 0.0, 1.0),
103 )
104 .transposed()
105 }
106
107 pub fn rot_z_matrix(degrees: f64) -> Self {
108 let c = dcos(degrees);
109 let s = dsin(degrees);
110 Mt4::new(
111 Pt4::new(c, -s, 0.0, 0.0),
112 Pt4::new(s, c, 0.0, 0.0),
113 Pt4::new(0.0, 0.0, 1.0, 0.0),
114 Pt4::new(0.0, 0.0, 0.0, 1.0),
115 )
116 .transposed()
117 }
118
119 pub fn rot_vec(x: f64, y: f64, z: f64, degrees: f64) -> Self {
120 let c = dcos(degrees);
121 let s = dsin(degrees);
122 Mt4::new(
123 Pt4::new(
124 c + x * x * (1.0 - c),
125 x * y * (1.0 - c) - z * s,
126 x * z * (1.0 - c) + y * s,
127 0.0,
128 ),
129 Pt4::new(
130 y * x * (1.0 - c) + z * s,
131 c + y * y * (1.0 - c),
132 y * z * (1.0 - c) - x * s,
133 0.0,
134 ),
135 Pt4::new(
136 z * x * (1.0 - c) - z * s,
137 z * y * (1.0 - c) + x * s,
138 c + z * z * (1.0 - c),
139 0.0,
140 ),
141 Pt4::new(0.0, 0.0, 0.0, 1.0),
142 )
143 .transposed()
144 }
145
146 pub fn perspective_matrix(fovy: f64, aspect: f64, near: f64, far: f64) -> Self {
147 let tan_half_fovy = dtan(fovy / 2.0);
148 Mt4::new(
149 Pt4::new(1.0 / (aspect * tan_half_fovy), 0.0, 0.0, 0.0),
150 Pt4::new(0.0, 1.0 / tan_half_fovy, 0.0, 0.0),
151 Pt4::new(
152 0.0,
153 0.0,
154 -(far + near) / (far - near),
155 -(2.0 * far * near) / (far - near),
156 ),
157 Pt4::new(0.0, 0.0, -1.0, 1.0),
158 )
159 .transposed()
160 }
161
162 pub fn inverse(&self) -> Option<Self> {
163 let mut out = Mt4::identity();
164
165 out[0] = self[5] * self[10] * self[15]
166 - self[5] * self[11] * self[14]
167 - self[9] * self[6] * self[15]
168 + self[9] * self[7] * self[14]
169 + self[13] * self[6] * self[11]
170 - self[13] * self[7] * self[10];
171
172 out[4] = -self[4] * self[10] * self[15]
173 + self[4] * self[11] * self[14]
174 + self[8] * self[6] * self[15]
175 - self[8] * self[7] * self[14]
176 - self[12] * self[6] * self[11]
177 + self[12] * self[7] * self[10];
178
179 out[8] = self[4] * self[9] * self[15]
180 - self[4] * self[11] * self[13]
181 - self[8] * self[5] * self[15]
182 + self[8] * self[7] * self[13]
183 + self[12] * self[5] * self[11]
184 - self[12] * self[7] * self[9];
185
186 out[12] = -self[4] * self[9] * self[14]
187 + self[4] * self[10] * self[13]
188 + self[8] * self[5] * self[14]
189 - self[8] * self[6] * self[13]
190 - self[12] * self[5] * self[10]
191 + self[12] * self[6] * self[9];
192
193 out[1] = -self[1] * self[10] * self[15]
194 + self[1] * self[11] * self[14]
195 + self[9] * self[2] * self[15]
196 - self[9] * self[3] * self[14]
197 - self[13] * self[2] * self[11]
198 + self[13] * self[3] * self[10];
199
200 out[5] = self[0] * self[10] * self[15]
201 - self[0] * self[11] * self[14]
202 - self[8] * self[2] * self[15]
203 + self[8] * self[3] * self[14]
204 + self[12] * self[2] * self[11]
205 - self[12] * self[3] * self[10];
206
207 out[9] = -self[0] * self[9] * self[15]
208 + self[0] * self[11] * self[13]
209 + self[8] * self[1] * self[15]
210 - self[8] * self[3] * self[13]
211 - self[12] * self[1] * self[11]
212 + self[12] * self[3] * self[9];
213
214 out[13] = self[0] * self[9] * self[14]
215 - self[0] * self[10] * self[13]
216 - self[8] * self[1] * self[14]
217 + self[8] * self[2] * self[13]
218 + self[12] * self[1] * self[10]
219 - self[12] * self[2] * self[9];
220
221 out[2] = self[1] * self[6] * self[15]
222 - self[1] * self[7] * self[14]
223 - self[5] * self[2] * self[15]
224 + self[5] * self[3] * self[14]
225 + self[13] * self[2] * self[7]
226 - self[13] * self[3] * self[6];
227
228 out[6] = -self[0] * self[6] * self[15]
229 + self[0] * self[7] * self[14]
230 + self[4] * self[2] * self[15]
231 - self[4] * self[3] * self[14]
232 - self[12] * self[2] * self[7]
233 + self[12] * self[3] * self[6];
234
235 out[10] = self[0] * self[5] * self[15]
236 - self[0] * self[7] * self[13]
237 - self[4] * self[1] * self[15]
238 + self[4] * self[3] * self[13]
239 + self[12] * self[1] * self[7]
240 - self[12] * self[3] * self[5];
241
242 out[14] = -self[0] * self[5] * self[14]
243 + self[0] * self[6] * self[13]
244 + self[4] * self[1] * self[14]
245 - self[4] * self[2] * self[13]
246 - self[12] * self[1] * self[6]
247 + self[12] * self[2] * self[5];
248
249 out[3] = -self[1] * self[6] * self[11]
250 + self[1] * self[7] * self[10]
251 + self[5] * self[2] * self[11]
252 - self[5] * self[3] * self[10]
253 - self[9] * self[2] * self[7]
254 + self[9] * self[3] * self[6];
255
256 out[7] = self[0] * self[6] * self[11]
257 - self[0] * self[7] * self[10]
258 - self[4] * self[2] * self[11]
259 + self[4] * self[3] * self[10]
260 + self[8] * self[2] * self[7]
261 - self[8] * self[3] * self[6];
262
263 out[11] = -self[0] * self[5] * self[11]
264 + self[0] * self[7] * self[9]
265 + self[4] * self[1] * self[11]
266 - self[4] * self[3] * self[9]
267 - self[8] * self[1] * self[7]
268 + self[8] * self[3] * self[5];
269
270 out[15] = self[0] * self[5] * self[10]
271 - self[0] * self[6] * self[9]
272 - self[4] * self[1] * self[10]
273 + self[4] * self[2] * self[9]
274 + self[8] * self[1] * self[6]
275 - self[8] * self[2] * self[5];
276
277 let mut det = self[0] * out[0] + self[1] * out[4] + self[2] * out[8] + self[3] * out[12];
278
279 if det == 0.0 {
280 None
281 } else {
282 det = 1.0 / det;
283 for i in 0..16 {
284 out[i] = out[i] * det;
285 }
286 Some(out)
287 }
288 }
289
290 pub fn look_at_matrix_rh(eye: Pt3, center: Pt3, up: Pt3) -> Self {
291 let mut f = center - eye;
292 f = f.normalized();
293 let mut s = f.cross(up);
294 s = s.normalized();
295 let u = s.cross(f);
296 Mt4::new(
297 Pt4::new(s.x, s.y, s.z, -s.dot(eye)),
298 Pt4::new(u.x, u.y, u.z, -u.dot(eye)),
299 Pt4::new(-f.x, -f.y, -f.z, f.dot(eye)),
300 Pt4::new(0.0, 0.0, 0.0, 1.0),
301 )
302 }
303
304 pub fn look_at_matrix_lh(eye: Pt3, center: Pt3, up: Pt3) -> Self {
305 let mut f = center - eye;
306 f = f.normalized();
307 let mut s = up.cross(f);
308 if s == Pt3::new(0.0, 0.0, 0.0) {
310 if up.dot(f) < 0.0 {
312 return Mt4::rot_x_matrix(180.0);
313 }
314 return Mt4::identity();
315 }
316 s = s.normalized();
317 let u = f.cross(s);
318 Mt4::new(
319 Pt4::new(s.x, s.y, s.z, -s.dot(eye)),
320 Pt4::new(u.x, u.y, u.z, -u.dot(eye)),
321 Pt4::new(f.x, f.y, f.z, -f.dot(eye)),
322 Pt4::new(0.0, 0.0, 0.0, 1.0),
323 )
324 }
325
326 pub fn rotation_from_direction(direction: Pt3, up: Pt3) -> Self {
327 let x_axis: Pt3 = up.cross(direction).normalized();
328 let z_axis: Pt3 = direction.cross(x_axis).normalized();
329 let mut result = Mt4::identity();
330 result.x.x = x_axis.x;
331 result.x.y = direction.x;
332 result.x.z = z_axis.x;
333
334 result.y.x = x_axis.y;
335 result.y.y = direction.y;
336 result.y.z = z_axis.y;
337
338 result.z.x = x_axis.z;
339 result.z.y = direction.z;
340 result.z.z = z_axis.z;
341
342 result
343 }
344}
345
346impl std::ops::Mul<Pt4> for Mt4 {
347 type Output = Pt4;
348
349 fn mul(self, rhs: Pt4) -> Self::Output {
350 let t = self.transposed();
351 Pt4::new(t.x.dot(rhs), t.y.dot(rhs), t.z.dot(rhs), t.w.dot(rhs))
352 }
353}
354
355impl std::ops::Mul<Pt3> for Mt4 {
356 type Output = Pt3;
357
358 fn mul(self, rhs: Pt3) -> Self::Output {
359 let t = self.transposed();
360 Pt3::new(
361 t.x.as_pt3().dot(rhs),
362 t.y.as_pt3().dot(rhs),
363 t.z.as_pt3().dot(rhs),
364 )
365 }
366}
367
368impl std::ops::Mul<Mt4> for Mt4 {
369 type Output = Mt4;
370
371 fn mul(self, rhs: Mt4) -> Self::Output {
372 let t = self.transposed();
373 Mt4::new(
374 Pt4::new(
375 t.x.dot(rhs.x),
376 t.y.dot(rhs.x),
377 t.z.dot(rhs.x),
378 t.w.dot(rhs.x),
379 ),
380 Pt4::new(
381 t.x.dot(rhs.y),
382 t.y.dot(rhs.y),
383 t.z.dot(rhs.y),
384 t.w.dot(rhs.y),
385 ),
386 Pt4::new(
387 t.x.dot(rhs.z),
388 t.y.dot(rhs.z),
389 t.z.dot(rhs.z),
390 t.w.dot(rhs.z),
391 ),
392 Pt4::new(
393 t.x.dot(rhs.w),
394 t.y.dot(rhs.w),
395 t.z.dot(rhs.w),
396 t.w.dot(rhs.w),
397 ),
398 )
399 }
400}
401
402impl std::ops::Index<usize> for Mt4 {
403 type Output = f64;
404
405 fn index(&self, index: usize) -> &Self::Output {
406 match index {
407 0 => &self.x.x,
408 1 => &self.x.y,
409 2 => &self.x.z,
410 3 => &self.x.w,
411 4 => &self.y.x,
412 5 => &self.y.y,
413 6 => &self.y.z,
414 7 => &self.y.w,
415 8 => &self.z.x,
416 9 => &self.z.y,
417 10 => &self.z.z,
418 11 => &self.z.w,
419 12 => &self.w.x,
420 13 => &self.w.y,
421 14 => &self.w.z,
422 15 => &self.w.w,
423 _ => panic!("Index out of bounds"),
424 }
425 }
426}
427
428impl std::ops::IndexMut<usize> for Mt4 {
429 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
430 match index {
431 0 => &mut self.x.x,
432 1 => &mut self.x.y,
433 2 => &mut self.x.z,
434 3 => &mut self.x.w,
435 4 => &mut self.y.x,
436 5 => &mut self.y.y,
437 6 => &mut self.y.z,
438 7 => &mut self.y.w,
439 8 => &mut self.z.x,
440 9 => &mut self.z.y,
441 10 => &mut self.z.z,
442 11 => &mut self.z.w,
443 12 => &mut self.w.x,
444 13 => &mut self.w.y,
445 14 => &mut self.w.z,
446 15 => &mut self.w.w,
447 _ => panic!("Index out of bounds"),
448 }
449 }
450}