1use crate::matrix::*;
53use crate::quaternion::*;
54use crate::scalar::*;
55use crate::vector::*;
56use num_traits::{Zero, One};
57
58pub fn translate<T: Scalar>(trans: Vector3<T>) -> Matrix4<T> {
71 Matrix4::new(
72 <T as One>::one(),
73 <T as Zero>::zero(),
74 <T as Zero>::zero(),
75 <T as Zero>::zero(),
76 <T as Zero>::zero(),
77 <T as One>::one(),
78 <T as Zero>::zero(),
79 <T as Zero>::zero(),
80 <T as Zero>::zero(),
81 <T as Zero>::zero(),
82 <T as One>::one(),
83 <T as Zero>::zero(),
84 trans.x,
85 trans.y,
86 trans.z,
87 <T as One>::one(),
88 )
89}
90
91pub fn scale<T: Scalar>(scale: Vector3<T>) -> Matrix4<T> {
104 Matrix4::new(
105 scale.x,
106 <T as Zero>::zero(),
107 <T as Zero>::zero(),
108 <T as Zero>::zero(),
109 <T as Zero>::zero(),
110 scale.y,
111 <T as Zero>::zero(),
112 <T as Zero>::zero(),
113 <T as Zero>::zero(),
114 <T as Zero>::zero(),
115 scale.z,
116 <T as Zero>::zero(),
117 <T as Zero>::zero(),
118 <T as Zero>::zero(),
119 <T as Zero>::zero(),
120 <T as One>::one(),
121 )
122}
123
124pub fn rotation_from_quat<T: FloatScalar>(q: &Quat<T>) -> Matrix4<T> {
128 Quat::mat4(q)
129}
130
131pub fn rotation_from_axis_angle<T: FloatScalar>(
142 axis: &Vector3<T>,
143 angle: T,
144 epsilon: T,
145) -> Option<Matrix4<T>> {
146 Quat::of_axis_angle(axis, angle, epsilon).map(|q| q.mat4())
147}
148
149pub fn transform_vec3<T: FloatScalar>(m: &Matrix4<T>, v: &Vector3<T>) -> Vector3<T> {
153 let v4 = Vector4::new(v.x, v.y, v.z, <T as One>::one());
154 let vout = *m * v4;
155 Vector3::new(vout.x / vout.w, vout.y / vout.w, vout.z / vout.w)
156}
157
158pub fn project3<T: FloatScalar>(
170 world: &Matrix4<T>,
171 persp: &Matrix4<T>,
172 lb: &Vector2<T>,
173 rt: &Vector2<T>,
174 pt: &Vector3<T>,
175) -> Vector3<T> {
176 let inp = Vector4::new(pt.x, pt.y, pt.z, <T as One>::one());
177 let pw = *persp * *world;
178 let mut out = pw * inp;
179
180 out.x /= out.w;
181 out.y /= out.w;
182 out.z /= out.w;
183
184 let out_x = lb.x + ((rt.x - lb.x) * (out.x + <T as One>::one()) * T::half());
185 let out_y = lb.y + ((rt.y - lb.y) * (out.y + <T as One>::one()) * T::half());
186 let out_z = (out.z + <T as One>::one()) * T::half();
187 Vector3::new(out_x, out_y, out_z)
188}
189
190pub fn unproject3<T: FloatScalar>(
202 world: &Matrix4<T>,
203 persp: &Matrix4<T>,
204 lb: &Vector2<T>,
205 rt: &Vector2<T>,
206 pt: &Vector3<T>,
207) -> Vector3<T> {
208 let pw = *persp * *world;
209 let inv = if pw.is_affine(T::epsilon()) {
210 pw.inverse_affine()
211 } else {
212 pw.inverse()
213 };
214 let in_x = (T::two() * (pt.x - lb.x) / (rt.x - lb.x)) - <T as One>::one();
215 let in_y = (T::two() * (pt.y - lb.y) / (rt.y - lb.y)) - <T as One>::one();
216 let in_z = (T::two() * pt.z) - <T as One>::one();
217 let in_w = <T as One>::one();
218 let inp = Vector4::new(in_x, in_y, in_z, in_w);
219 let out = inv * inp;
220 let out4 = out / out.w;
221 Vector3::new(out4.x, out4.y, out4.z)
222}
223
224pub fn frustum<T: FloatScalar>(lbn: &Vector3<T>, rtf: &Vector3<T>) -> Matrix4<T> {
232 let width = rtf.x - lbn.x;
233 let height = rtf.y - lbn.y;
234 let depth = rtf.z - lbn.z;
235 let a = (rtf.x + lbn.x) / width;
236 let b = (rtf.y + lbn.y) / height;
237 let c = -(rtf.z + lbn.z) / depth;
238 let d = -(T::two() * rtf.z * lbn.z) / depth;
239
240 Matrix4::new(
241 T::two() * lbn.z / width,
242 <T as Zero>::zero(),
243 <T as Zero>::zero(),
244 <T as Zero>::zero(),
245 <T as Zero>::zero(),
246 T::two() * lbn.z / height,
247 <T as Zero>::zero(),
248 <T as Zero>::zero(),
249 a,
250 b,
251 c,
252 -<T as One>::one(),
253 <T as Zero>::zero(),
254 <T as Zero>::zero(),
255 d,
256 <T as Zero>::zero(),
257 )
258}
259
260pub fn ortho4<T: FloatScalar>(
269 left: T,
270 right: T,
271 bottom: T,
272 top: T,
273 near: T,
274 far: T,
275) -> Matrix4<T> {
276 let width = right - left;
277 let height = top - bottom;
278 let depth = far - near;
279 let r00 = T::two() / width;
280 let r11 = T::two() / height;
281 let r22 = -T::two() / depth;
282 let r03 = -(right + left) / width;
283 let r13 = -(top + bottom) / height;
284 let r23 = -(far + near) / depth;
285 Matrix4::new(
286 r00,
287 <T as Zero>::zero(),
288 <T as Zero>::zero(),
289 <T as Zero>::zero(),
290 <T as Zero>::zero(),
291 r11,
292 <T as Zero>::zero(),
293 <T as Zero>::zero(),
294 <T as Zero>::zero(),
295 <T as Zero>::zero(),
296 r22,
297 <T as Zero>::zero(),
298 r03,
299 r13,
300 r23,
301 <T as One>::one(),
302 )
303}
304
305pub fn perspective<T: FloatScalar>(fovy: T, aspect: T, near: T, far: T) -> Matrix4<T> {
315 let f = <T as One>::one() / T::ttan(fovy * T::half());
316 let denom = near - far;
317 let a = (far + near) / denom;
318 let b = (T::two() * far * near) / denom;
319
320 Matrix4::new(
321 f / aspect,
322 <T as Zero>::zero(),
323 <T as Zero>::zero(),
324 <T as Zero>::zero(),
325 <T as Zero>::zero(),
326 f,
327 <T as Zero>::zero(),
328 <T as Zero>::zero(),
329 <T as Zero>::zero(),
330 <T as Zero>::zero(),
331 a,
332 -<T as One>::one(),
333 <T as Zero>::zero(),
334 <T as Zero>::zero(),
335 b,
336 <T as Zero>::zero(),
337 )
338}
339
340pub fn lookat<T: FloatScalar>(eye: &Vector3<T>, dest: &Vector3<T>, up: &Vector3<T>) -> Matrix4<T> {
349 let f = Vector3::normalize(&(*dest - *eye));
350 let s = Vector3::normalize(&Vector3::cross(&f, up));
351 let u = Vector3::normalize(&Vector3::cross(&s, &f));
352
353 let trans = translate(-*eye);
354
355 let m = Matrix4::new(
356 s.x,
357 u.x,
358 -f.x,
359 <T as Zero>::zero(),
360 s.y,
361 u.y,
362 -f.y,
363 <T as Zero>::zero(),
364 s.z,
365 u.z,
366 -f.z,
367 <T as Zero>::zero(),
368 <T as Zero>::zero(),
369 <T as Zero>::zero(),
370 <T as Zero>::zero(),
371 <T as One>::one(),
372 );
373 m * trans
374}
375
376pub fn decompose<T: FloatScalar>(m: &Matrix4<T>) -> Option<(Vector3<T>, Quat<T>, Vector3<T>)> {
385 let mut col0 = Vector3::new(m.col[0].x, m.col[0].y, m.col[0].z);
386 let mut col1 = Vector3::new(m.col[1].x, m.col[1].y, m.col[1].z);
387 let mut col2 = Vector3::new(m.col[2].x, m.col[2].y, m.col[2].z);
388 let det = m.determinant();
389
390 let mut scale = Vector3::new(
392 Vector3::length(&col0),
393 Vector3::length(&col1),
394 Vector3::length(&col2),
395 );
396 let trans = Vector3::new(m.col[3].x, m.col[3].y, m.col[3].z);
397
398 if det < <T as Zero>::zero() {
399 scale = -scale;
400 }
401
402 if scale.x != <T as Zero>::zero() {
403 col0 = col0 / scale.x;
404 } else {
405 return Option::None;
406 }
407
408 if scale.y != <T as Zero>::zero() {
409 col1 = col1 / scale.y;
410 } else {
411 return Option::None;
412 }
413
414 if scale.z != <T as Zero>::zero() {
415 col2 = col2 / scale.z;
416 } else {
417 return Option::None;
418 }
419
420 let rot_matrix = Matrix3::new(
421 col0.x, col0.y, col0.z, col1.x, col1.y, col1.z, col2.x, col2.y, col2.z,
422 );
423
424 let rot = Quat::of_matrix3(&rot_matrix);
425
426 Some((scale, rot, trans))
427}
428
429#[cfg(test)]
430mod tests {
431 use super::*;
432 #[test]
433 pub fn test_decompose() {
434 let ms = scale(Vector3::<f32>::new(4.0, 5.0, 6.0));
435 let mt = translate(Vector3::<f32>::new(1.0, 2.0, 3.0));
436 let q = Quat::<f32>::of_axis_angle(&Vector3::new(1.0, 1.0, 1.0), 1.0, EPS_F32)
437 .expect("axis length too small");
438 let mr = rotation_from_quat(&q);
439
440 let m = mt * mr * ms;
441
442 let v = decompose(&m);
443 match v {
444 None => assert_eq!(1, 2),
445 Some((s, r, t)) => {
446 assert_eq!((s.x - 4.0) < f32::epsilon(), true);
447 assert_eq!((s.y - 5.0) < f32::epsilon(), true);
448 assert_eq!((s.z - 6.0) < f32::epsilon(), true);
449
450 assert_eq!((q.x - r.x) < f32::epsilon(), true);
451 assert_eq!((q.y - r.y) < f32::epsilon(), true);
452 assert_eq!((q.z - r.z) < f32::epsilon(), true);
453 assert_eq!((q.w - r.w) < f32::epsilon(), true);
454
455 assert_eq!((t.x - 1.0) < f32::epsilon(), true);
456 assert_eq!((t.y - 2.0) < f32::epsilon(), true);
457 assert_eq!((t.z - 3.0) < f32::epsilon(), true);
458 }
459 }
460 }
461
462 #[test]
463 fn test_rotation_from_axis_angle_zero_axis() {
464 let axis = Vector3::<f32>::new(0.0, 0.0, 0.0);
465 assert!(rotation_from_axis_angle(&axis, 1.0, EPS_F32).is_none());
466 }
467
468 #[test]
469 fn test_project_unproject_roundtrip() {
470 let world = Matrix4::<f32>::identity();
471 let proj = ortho4(-1.0, 1.0, -1.0, 1.0, 0.1, 10.0);
472 let lb = Vector2::new(-1.0, -1.0);
473 let rt = Vector2::new(1.0, 1.0);
474 let pt = Vector3::new(0.2, -0.4, 1.0);
475
476 let screen = project3(&world, &proj, &lb, &rt, &pt);
477 let out = unproject3(&world, &proj, &lb, &rt, &screen);
478
479 assert!((out.x - pt.x).abs() < 0.001);
480 assert!((out.y - pt.y).abs() < 0.001);
481 assert!((out.z - pt.z).abs() < 0.001);
482 }
483
484 #[test]
485 fn test_lookat_identity() {
486 let eye = Vector3::new(0.0f32, 0.0, 0.0);
487 let dest = Vector3::new(0.0f32, 0.0, -1.0);
488 let up = Vector3::new(0.0f32, 1.0, 0.0);
489 let view = lookat(&eye, &dest, &up);
490 let v = Vector4::new(1.0f32, 2.0, 3.0, 1.0);
491 let out = view * v;
492
493 assert!((out.x - v.x).abs() < 0.001);
494 assert!((out.y - v.y).abs() < 0.001);
495 assert!((out.z - v.z).abs() < 0.001);
496 assert!((out.w - v.w).abs() < 0.001);
497 }
498}