use glam::{Affine3A, Mat3, Mat4, vec3}; pub(crate) fn rotation(yaw: f32, pitch: f32) -> Mat3 { let (ys, yc) = yaw.sin_cos(); let (ps, pc) = pitch.sin_cos(); Mat3::from_cols_array_2d(&[[0., 0., -1.], [-1., 0., 0.], [0., 1., 0.]]) * Mat3::from_cols_array_2d(&[[pc, 0., -ps], [0., 1., 0.], [ps, 0., pc]]) * Mat3::from_cols_array_2d(&[[yc, -ys, 0.], [ys, yc, 0.], [0., 0., 1.]]) } pub(crate) fn view(yaw: f32, pitch: f32, distance: f32) -> Affine3A { Affine3A::from_mat3_translation(rotation(yaw, pitch), vec3(0., 0., distance)) } pub(crate) fn projection(fovy: f32, aspect: f32) -> Mat4 { Mat4::perspective_lh(fovy, aspect, 0.1, 10.0) } #[cfg(test)] mod tests { use super::*; use glam::Vec3; use std::f32::consts::PI; #[test] fn test_rotation() { let mut ok = true; for (yaw, pitch, x, y, z) in [ (0.0, 0.0, [0., 0., -1.], [-1., 0., 0.], [0., 1., 0.]), (0.5, 0.0, [1., 0., 0.], [0., 0., -1.], [0., 1., 0.]), (0.0, 0.5, [0., -1., 0.], [-1., 0., 0.], [0., 0., -1.]), (0.5, 0.5, [1., 0., 0.], [0., -1., 0.], [0., 0., -1.]), ] { let m = rotation(yaw * PI, pitch * PI); if !m.abs_diff_eq(Mat3::from_cols_array_2d(&[x, y, z]), 1e-3) { ok = false; println!("Wrong rotation for {}, {}", 180. * yaw, 180. * pitch); println!("X: expected: {}, actual: {}", Vec3::from_array(x), m.x_axis); println!("Y: expected: {}, actual: {}", Vec3::from_array(y), m.y_axis); println!("Z: expected: {}, actual: {}", Vec3::from_array(z), m.z_axis); } } if !ok { panic!(); } } #[test] fn test_view() { let mut ok = true; for (yaw, pitch, x1, y1, z1) in [ (0.0, 0.0, [0., 0., 2.], [-1., 0., 3.], [0., 1., 3.]), (0.5, 0.0, [1., 0., 3.], [0., 0., 2.], [0., 1., 3.]), (0.0, 0.5, [0., -1., 3.], [-1., 0., 3.], [0., 0., 2.]), (0.5, 0.5, [1., 0., 3.], [0., -1., 3.], [0., 0., 2.]), ] { let m = view(yaw * PI, pitch * PI, 3.); for (label, world, expected) in [('X', Vec3::X, x1), ('Y', Vec3::Y, y1), ('Z', Vec3::Z, z1)] { let expected = Vec3::from_array(expected); let actual = m.transform_point3(world); if !actual.abs_diff_eq(expected, 1e-3) { ok = false; println!( "Wrong transform for {}, {}: for {label}, expected: {expected}, got: {actual}", 180. * yaw, 180. * pitch ); } } } if !ok { panic!(); } } }