From 759b6a4211a0344787fb82cbaa48c435baf76cfe Mon Sep 17 00:00:00 2001 From: numzero Date: Tue, 4 Aug 2026 23:21:11 +0300 Subject: [PATCH] add camera calculations --- src/camera.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ 2 files changed, 80 insertions(+) create mode 100644 src/camera.rs diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..f54059b --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,78 @@ +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!(); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 294edfd..33f8d16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ use std::error::Error; use glam::{UVec2, Vec4, vec4}; +mod camera; + const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth16Unorm; const OUTPUT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb;