atanor 0.1.0

Motor 3D ray-traced que vive solo y exclusivamente en la terminal.
Documentation
use crate::math::Ray;
use glam::Vec3;

pub struct Camera {
    pub position: Vec3,
    pub yaw: f32,   // radianes, rotación sobre Y
    pub pitch: f32, // radianes, rotación sobre X
    pub fov_y: f32, // radianes
}

impl Camera {
    pub fn new(position: Vec3, fov_y_deg: f32) -> Self {
        Self {
            position,
            yaw: 0.0,
            pitch: 0.0,
            fov_y: fov_y_deg.to_radians(),
        }
    }

    pub fn forward(&self) -> Vec3 {
        let cp = self.pitch.cos();
        Vec3::new(
            self.yaw.sin() * cp,
            self.pitch.sin(),
            -self.yaw.cos() * cp,
        )
        .normalize()
    }

    pub fn right(&self) -> Vec3 {
        Vec3::new(self.yaw.cos(), 0.0, self.yaw.sin()).normalize()
    }

    pub fn up(&self) -> Vec3 {
        self.right().cross(self.forward()).normalize()
    }

    /// Genera un rayo primario para un píxel (x, y) de un framebuffer de tamaño (w, h).
    /// El píxel (0,0) está arriba-izquierda.
    /// `jitter` es un offset sub-píxel en [0,1)² — para SSAA pasa una muestra
    /// Halton; para 1 sample/pixel pasa `(0.5, 0.5)` (centro del píxel).
    pub fn ray(&self, x: u16, y: u16, w: u16, h: u16, jitter: glam::Vec2) -> Ray {
        let aspect = w as f32 / h.max(1) as f32;
        let pixel_aspect = 1.0; // half-blocks ya dejan el píxel cuadrado-ish
        let half_h = (self.fov_y * 0.5).tan();
        let half_w = half_h * aspect * pixel_aspect;

        let u = ((x as f32 + jitter.x) / w as f32) * 2.0 - 1.0;
        let v = 1.0 - ((y as f32 + jitter.y) / h as f32) * 2.0;

        let dir = self.forward() + self.right() * (u * half_w) + self.up() * (v * half_h);
        Ray::new(self.position, dir)
    }

    pub fn translate_local(&mut self, delta: Vec3) {
        let f = self.forward();
        let r = self.right();
        let u = Vec3::Y;
        self.position += r * delta.x + u * delta.y + f * delta.z;
    }

    pub fn rotate(&mut self, dyaw: f32, dpitch: f32) {
        self.yaw += dyaw;
        self.pitch = (self.pitch + dpitch).clamp(-1.5, 1.5);
    }
}