diff options
Diffstat (limited to 'engine/src/projection.rs')
| -rw-r--r-- | engine/src/projection.rs | 201 |
1 files changed, 177 insertions, 24 deletions
diff --git a/engine/src/projection.rs b/engine/src/projection.rs index aa84a9f..b3bfa00 100644 --- a/engine/src/projection.rs +++ b/engine/src/projection.rs @@ -1,57 +1,210 @@ -use crate::matrix::Matrix; +use crate::builder; +use crate::data_types::dimens::Dimens; +use crate::matrix::{CellPos as MatrixCellPos, Matrix}; +use crate::reflection::Reflection; +use crate::vector::Vec2; +use crate::windowing::dpi::PhysicalSize; -#[derive(Debug)] +#[derive(Debug, Clone, Reflection)] #[non_exhaustive] pub enum Projection { Perspective(Perspective), + Orthographic(Orthographic), +} + +impl Projection +{ + pub fn to_matrix_rh( + &self, + window_size: PhysicalSize<u32>, + clip_volume: ClipVolume, + ) -> Matrix<f32, 4, 4> + { + match self { + Projection::Perspective(perspective_proj) => perspective_proj.to_matrix_rh( + window_size.width as f32 / window_size.height as f32, + clip_volume, + ), + Projection::Orthographic(orthographic_proj) => { + orthographic_proj.to_matrix_rh(window_size, clip_volume) + } + } + } } /// Perspective projection parameters. -#[derive(Debug)] +#[derive(Debug, Clone, Reflection)] pub struct Perspective { - pub fov_radians: f32, + /// Field-of-view in degrees + pub fov_degs: 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<f32, 4, 4> + { + let mut out = Matrix::new(); + + let fov_rads = self.fov_degs.to_radians(); + + match clip_volume { + ClipVolume::NegOneToOne => { + out[MatrixCellPos { row: 0, col: 0 }] = + (1.0 / (fov_rads / 2.0).tan()) / aspect; + out[MatrixCellPos { row: 1, col: 1 }] = 1.0 / (fov_rads / 2.0).tan(); + out[MatrixCellPos { row: 2, col: 2 }] = + (self.near + self.far) / (self.near - self.far); + out[MatrixCellPos { row: 2, col: 3 }] = + (2.0 * self.near * self.far) / (self.near - self.far); + out[MatrixCellPos { row: 3, col: 2 }] = -1.0; + } + } + + out + } +} + impl Default for Perspective { fn default() -> Self { Self { - fov_radians: 80.0f32.to_radians(), + fov_degs: 80.0, far: 100.0, near: 0.1, } } } -pub(crate) fn new_perspective_matrix( - perspective: &Perspective, - aspect: f32, -) -> Matrix<f32, 4, 4> +#[derive(Debug, Default, Clone, Reflection)] +pub enum OrthographicSize +{ + FixedSize(Dimens<f32>), + + #[default] + WindowSize, +} + +builder! { +#[builder(name = OrthographicBuilder, derives=(Debug, Clone))] +#[derive(Debug, Clone, Reflection)] +#[non_exhaustive] +pub struct Orthographic +{ + pub near: f32, + pub far: f32, + pub viewport_origin: Vec2<f32>, + pub size: OrthographicSize, +} +} + +impl Orthographic +{ + pub fn builder() -> OrthographicBuilder + { + OrthographicBuilder::default() + } + + /// Creates a orthographic projection matrix using right-handed coordinates. + pub fn to_matrix_rh( + &self, + window_size: PhysicalSize<u32>, + clip_volume: ClipVolume, + ) -> Matrix<f32, 4, 4> + { + let size = match self.size { + OrthographicSize::FixedSize(fixed_size) => fixed_size, + OrthographicSize::WindowSize => Dimens { + width: window_size.width as f32, + height: window_size.height as f32, + }, + }; + + let origin_x = size.width * self.viewport_origin.x; + let origin_y = size.height * self.viewport_origin.y; + + let left = -origin_x; + let right = size.width - origin_x; + + let bottom = -origin_y; + let top = size.height - origin_y; + + let near = self.near; + let far = self.far; + + orthographic_rh( + OrthographicParams { left, right, bottom, top, near, far }, + clip_volume, + ) + } +} + +impl Default for Orthographic { - let mut out = Matrix::new(); + fn default() -> Self + { + Self::builder().build() + } +} - out.set_cell(0, 0, (1.0 / (perspective.fov_radians / 2.0).tan()) / aspect); +impl Default for OrthographicBuilder +{ + fn default() -> Self + { + Self { + near: 0.0, + far: 1000.0, + viewport_origin: Vec2 { x: 0.5, y: 0.5 }, + size: OrthographicSize::WindowSize, + } + } +} - out.set_cell(1, 1, 1.0 / (perspective.fov_radians / 2.0).tan()); +#[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, +} - out.set_cell( - 2, - 2, - (perspective.near + perspective.far) / (perspective.near - perspective.far), - ); +#[derive(Debug, Clone)] +pub struct OrthographicParams +{ + pub left: f32, + pub right: f32, + pub bottom: f32, + pub top: f32, + pub near: f32, + pub far: f32, +} - out.set_cell( - 2, - 3, - (2.0 * perspective.near * perspective.far) / (perspective.near - perspective.far), - ); +/// Creates a orthographic projection matrix using right-handed coordinates. +pub fn orthographic_rh( + OrthographicParams { left, right, bottom, top, near, far }: OrthographicParams, + clip_volume: ClipVolume, +) -> Matrix<f32, 4, 4> +{ + let mut result = Matrix::<f32, 4, 4>::new(); - out.set_cell(3, 2, -1.0); + match clip_volume { + ClipVolume::NegOneToOne => { + result[MatrixCellPos { row: 0, col: 0 }] = 2.0 / (right - left); + result[MatrixCellPos { row: 1, col: 1 }] = 2.0 / (top - bottom); + result[MatrixCellPos { row: 2, col: 2 }] = -2.0 / (far - near); + result[MatrixCellPos { row: 0, col: 3 }] = -(right + left) / (right - left); + result[MatrixCellPos { row: 1, col: 3 }] = -(top + bottom) / (top - bottom); + result[MatrixCellPos { row: 2, col: 3 }] = -(far + near) / (far - near); + result[MatrixCellPos { row: 3, col: 3 }] = 1.0; + } + } - out + result } |
