use crate::data_types::dimens::Dimens3; use crate::matrix::Matrix; use crate::util::builder; use crate::vector::Vec3; #[derive(Debug)] #[non_exhaustive] pub enum Projection { Perspective(Perspective), Orthographic(Orthographic), } /// Perspective projection parameters. #[derive(Debug)] pub struct Perspective { pub fov_radians: f32, pub far: f32, pub near: f32, } impl Perspective { /// Creates a perspective projection matrix using right-handed coordinates. #[inline] pub fn to_matrix_rh(&self, aspect: f32, clip_volume: ClipVolume) -> Matrix { let mut out = Matrix::new(); match clip_volume { ClipVolume::NegOneToOne => { out.set_cell(0, 0, (1.0 / (self.fov_radians / 2.0).tan()) / aspect); out.set_cell(1, 1, 1.0 / (self.fov_radians / 2.0).tan()); out.set_cell(2, 2, (self.near + self.far) / (self.near - self.far)); out.set_cell(2, 3, (2.0 * self.near * self.far) / (self.near - self.far)); out.set_cell(3, 2, -1.0); } } out } } impl Default for Perspective { fn default() -> Self { Self { fov_radians: 80.0f32.to_radians(), far: 100.0, near: 0.1, } } } builder! { #[builder(name = OrthographicBuilder, derives=(Debug, Clone))] #[derive(Debug, Clone, PartialEq, PartialOrd)] #[non_exhaustive] pub struct Orthographic { pub size: Dimens3, } } impl Orthographic { pub fn builder() -> OrthographicBuilder { OrthographicBuilder::default() } /// Creates a orthographic projection matrix using right-handed coordinates. pub fn to_matrix_rh( &self, center_pos: &Vec3, clip_volume: ClipVolume, ) -> Matrix { let mut result = Matrix::::new(); let left = center_pos.x - (self.size.width / 2.0); let right = center_pos.x + (self.size.width / 2.0); let bottom = center_pos.y - (self.size.height / 2.0); let top = center_pos.y + (self.size.height / 2.0); let near = center_pos.z - (self.size.depth / 2.0); let far = center_pos.z + (self.size.depth / 2.0); match clip_volume { ClipVolume::NegOneToOne => { result.set_cell(0, 0, 2.0 / (right - left)); result.set_cell(1, 1, 2.0 / (top - bottom)); result.set_cell(2, 2, -2.0 / (far - near)); result.set_cell(0, 3, -(right + left) / (right - left)); result.set_cell(1, 3, -(top + bottom) / (top - bottom)); result.set_cell(2, 3, -(far + near) / (far - near)); result.set_cell(3, 3, 1.0); } } result } } impl Default for Orthographic { fn default() -> Self { Self { size: Dimens3 { width: 10.0, height: 7.0, depth: 10.0, }, } } } impl Default for OrthographicBuilder { fn default() -> Self { let orthographic = Orthographic::default(); OrthographicBuilder { size: orthographic.size } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[non_exhaustive] pub enum ClipVolume { /// -1 to +1. This is the OpenGL clip volume definition. NegOneToOne, }