use cgmath::{Matrix4, Vector3}; #[derive(Clone, Copy, Debug, PartialEq)] pub struct Camera { position: (f32, f32), zoom: f32, rotation: f32, inverse_aspect_ratio: f32, } pub(crate) type CameraUniform = [[f32; 4]; 4]; #[allow(clippy::cast_precision_loss)] fn inverse_aspect_ratio(width: u32, height: u32) -> f32 { (height as f32) / (width as f32) } impl Camera { /// Create a new camera, with a position of (0, 0), and a zoom of 1.0 pub(crate) fn from_size(width: u32, height: u32) -> Self { Self { position: (0.0, 0.0), zoom: 1.0, rotation: 0.0, inverse_aspect_ratio: inverse_aspect_ratio(width, height), } } /// Get the camera's current x position #[must_use] pub const fn x(&self) -> f32 { self.position.0 } /// Get the camera's current y position #[must_use] pub const fn y(&self) -> f32 { self.position.1 } /// Get the camera's current zoom #[must_use] pub const fn zoom(&self) -> f32 { self.zoom } /// Set the position of the camera pub fn set_position(&mut self, x: f32, y: f32) { self.position = (x, y); } /// Set the aspect ratio of the camera pub fn set_zoom(&mut self, zoom: f32) { self.zoom = zoom; } /// Set the aspect ratio of the camera pub(crate) fn set_size(&mut self, width: u32, height: u32) { self.inverse_aspect_ratio = inverse_aspect_ratio(width, height); } #[allow(clippy::wrong_self_convention)] pub(crate) fn to_matrix(&mut self) -> [[f32; 4]; 4] { let cos_theta = self.rotation.cos(); let sin_theta = self.rotation.sin(); let x_axis = Vector3::new(cos_theta, -sin_theta, 0.0); let y_axis = Vector3::new(sin_theta, cos_theta, 0.0); let z_axis = Vector3::new(0.0, 0.0, 1.0); let eye = Vector3::new(self.position.0, self.position.1, 0.0); let x_dot = -cgmath::dot(x_axis, eye); let y_dot = -cgmath::dot(y_axis, eye); let z_dot = -cgmath::dot(z_axis, eye); #[rustfmt::skip] let view_matrix = Matrix4::new( x_axis.x, y_axis.x, z_axis.x, 0.0, x_axis.y, y_axis.y, z_axis.y, 0.0, x_axis.x, y_axis.y, z_axis.z, 0.0, x_dot, y_dot, z_dot, 1.0 ); #[rustfmt::skip] // TODO implement more scaling coordinate systems let projection_matrix = Matrix4::new( self.inverse_aspect_ratio, 0.0, 0.0, 0.0, 0.0, self.zoom, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 ); (projection_matrix * view_matrix).into() } }