roast2d_internal 0.4.0

Roast2D internal crate
Documentation
use glam::{Mat4, Vec3};

/// Camera projection mode
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Projection {
    /// Perspective projection with field of view
    Perspective {
        /// Vertical field of view in radians
        fov_y_radians: f32,
    },
    /// Orthographic projection
    Orthographic {
        /// Viewport height in world units (width is calculated from aspect ratio)
        height: f32,
    },
}

impl Default for Projection {
    fn default() -> Self {
        Projection::Perspective {
            fov_y_radians: std::f32::consts::FRAC_PI_4,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Camera3D {
    pub eye: Vec3,
    pub target: Vec3,
    pub up: Vec3,
    pub projection: Projection,
    pub znear: f32,
    pub zfar: f32,
}

impl Default for Camera3D {
    fn default() -> Self {
        Self {
            eye: Vec3::new(0.0, -5.0, 3.0),
            target: Vec3::ZERO,
            up: Vec3::Y,
            projection: Projection::default(),
            znear: 0.1,
            zfar: 100.0,
        }
    }
}

impl Camera3D {
    /// Create a perspective camera.
    ///
    /// # Arguments
    /// * `eye` - Camera position
    /// * `target` - Point the camera looks at
    /// * `fov_y_radians` - Vertical field of view in radians
    pub fn perspective(eye: Vec3, target: Vec3, fov_y_radians: f32) -> Self {
        Self {
            eye,
            target,
            up: Vec3::Y,
            projection: Projection::Perspective { fov_y_radians },
            znear: 0.1,
            zfar: 100.0,
        }
    }

    /// Create an orthographic camera.
    ///
    /// # Arguments
    /// * `eye` - Camera position
    /// * `target` - Point the camera looks at
    /// * `height` - Viewport height in world units
    pub fn orthographic(eye: Vec3, target: Vec3, height: f32) -> Self {
        Self {
            eye,
            target,
            up: Vec3::Y,
            projection: Projection::Orthographic { height },
            znear: 0.1,
            zfar: 100.0,
        }
    }

    /// Get the vertical field of view in radians (only valid for perspective projection).
    /// Returns default FOV for orthographic cameras.
    pub fn fov_y_radians(&self) -> f32 {
        match self.projection {
            Projection::Perspective { fov_y_radians } => fov_y_radians,
            Projection::Orthographic { .. } => std::f32::consts::FRAC_PI_4,
        }
    }

    /// Set the vertical field of view (converts to perspective if needed).
    pub fn set_fov_y_radians(&mut self, fov: f32) {
        self.projection = Projection::Perspective { fov_y_radians: fov };
    }

    pub fn view_matrix(&self) -> Mat4 {
        Mat4::look_at_rh(self.eye, self.target, self.up)
    }

    pub fn projection_matrix(&self, aspect: f32) -> Mat4 {
        match self.projection {
            Projection::Perspective { fov_y_radians } => {
                // Using perspective_rh (wgpu [0,1] depth range) with inverted znear/zfar
                // for reverse-Z depth buffer (bigger Z is closer), matching the 2D camera convention.
                Mat4::perspective_rh(fov_y_radians, aspect, self.zfar, self.znear)
            }
            Projection::Orthographic { height } => {
                let half_height = height / 2.0;
                let half_width = half_height * aspect;
                // Using orthographic_rh with inverted znear/zfar for reverse-Z
                Mat4::orthographic_rh(
                    -half_width,
                    half_width,
                    -half_height,
                    half_height,
                    self.zfar,
                    self.znear,
                )
            }
        }
    }

    pub fn view_proj(&self, aspect: f32) -> Mat4 {
        self.projection_matrix(aspect) * self.view_matrix()
    }
}