summaryrefslogtreecommitdiff
path: root/src/camera.rs
blob: 2eb1730912ee711d9370ac5d5508430562f11fc6 (plain)
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()
	}
}