#![allow(dead_code)] use mem_arena::MemArena; use crate::{ lerp::lerp_slice, math::{Matrix4x4, Point, Vector}, ray::Ray, sampling::square_to_circle, }; #[derive(Copy, Clone, Debug)] pub struct Camera<'a> { transforms: &'a [Matrix4x4], fovs: &'a [f32], tfovs: &'a [f32], aperture_radii: &'a [f32], focus_distances: &'a [f32], } impl<'a> Camera<'a> { pub fn new( arena: &'a MemArena, transforms: &[Matrix4x4], fovs: &[f32], mut aperture_radii: &[f32], mut focus_distances: &[f32], ) -> Camera<'a> { assert!(!transforms.is_empty(), "Camera has no transform(s)!"); assert!(!fovs.is_empty(), "Camera has no fov(s)!"); // Aperture needs focus distance and vice-versa. if aperture_radii.is_empty() || focus_distances.is_empty() { aperture_radii = &[0.0]; focus_distances = &[1.0]; if aperture_radii.is_empty() && !focus_distances.is_empty() { println!( "WARNING: camera has aperture radius but no focus distance. Disabling \ focal blur." ); } else if !aperture_radii.is_empty() && focus_distances.is_empty() { println!( "WARNING: camera has focus distance but no aperture radius. Disabling \ focal blur." ); } } // Can't have focus distance of zero. if focus_distances.iter().any(|d| *d == 0.0) { if aperture_radii.iter().any(|a| *a > 0.0) { println!("WARNING: camera focal distance is zero or less. Disabling focal blur."); } aperture_radii = &[0.0]; focus_distances = &[1.0]; } // Convert angle fov into linear fov. let tfovs: Vec = fovs .iter() .map(|n| (n / 2.0).sin() / (n / 2.0).cos()) .collect(); Camera { transforms: arena.copy_slice(&transforms), fovs: arena.copy_slice(&fovs), tfovs: arena.copy_slice(&tfovs), aperture_radii: arena.copy_slice(&aperture_radii), focus_distances: arena.copy_slice(&focus_distances), } } pub fn generate_ray(&self, x: f32, y: f32, time: f32, wavelength: f32, u: f32, v: f32) -> Ray { // Get time-interpolated camera settings let transform = lerp_slice(self.transforms, time); let tfov = lerp_slice(self.tfovs, time); let aperture_radius = lerp_slice(self.aperture_radii, time); let focus_distance = lerp_slice(self.focus_distances, time); // Ray origin let orig = { let (u, v) = square_to_circle((u * 2.0) - 1.0, (v * 2.0) - 1.0); Point::new(aperture_radius * u, aperture_radius * v, 0.0) }; // Ray direction let dir = Vector::new( (x * tfov) - (orig.x() / focus_distance), (y * tfov) - (orig.y() / focus_distance), 1.0, ) .normalized(); Ray::new(orig * transform, dir * transform, time, wavelength, false) } }