diff options
Diffstat (limited to 'src/camera.rs')
-rw-r--r-- | src/camera.rs | 200 |
1 files changed, 31 insertions, 169 deletions
diff --git a/src/camera.rs b/src/camera.rs index 8409ea5..84d8446 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,196 +1,58 @@ -use cgmath::Matrix3; -use cgmath::Vector3; +use cgmath::{Vector3, Matrix3}; use cgmath::prelude::*; -use ncurses::addch; -use ncurses::addstr; -use std::f32; -use std::f32::consts::PI; -use std::time::Instant; - type Vector = Vector3<f32>; -pub const HEIGHT: i32 = 50; -pub const WIDTH: i32 = HEIGHT * 3; +// The physical screen that camera projects onto +#[derive(Debug, Copy, Clone)] +pub struct Screen { + pub width: f32, + pub height: f32 +} -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub struct Camera { - pub time: f32, pub position: Vector, pub direction: Vector, pub up: Vector, - pub light: Vector, - pub angle: f32, pub distance: f32, - pub brightness: f32, - pub aspect_ratio: f32, pub speed: f32, pub turn_rate: f32, - pub width: f32, - pub height: f32, - pub palette: Vec<char>, -} - -fn softmin(left: f32, right: f32, k: f32) -> f32 { - // return left.min(right); - let h = (k-(left-right).abs()).max(0.0) / k; - return left.min(right) - h*h*k*(1.0/4.0); + pub screen: Screen, } -fn sd_sphere(point: Vector, center: Vector, radius: f32) -> f32 { - (point - center).magnitude() - radius -} - -fn sd_box(point: Vector, center: Vector, size: Vector) -> f32 { - let diff = center - point; - let q = diff.map(|n| n.abs()) - size / 2.0; - return q.map(|n| n.max(0.0)).magnitude() + (q.y.max(q.z).max(q.x)).min(0.0) -} +const ASPECT_RATIO: f32 = 2.0 / 3.0; impl Camera { - pub fn sd_gear(&self, point: Vector, center: Vector, radius: f32, thickness: f32, turn_rate: f32) -> f32 { - let mut dist: f32; - - let thickness_over_2 = thickness / 2.0; - let thickness_over_4 = thickness / 4.0; - - // Ring - { - let cylinder_dist = (Vector::new(0.0, point.y, point.z) - center).magnitude() - (radius - thickness_over_4); - dist = cylinder_dist.abs() - thickness_over_2; // Make cylinder hollow + pub fn new(position: Vector, look_at: Vector, angle: f32, distance: f32) -> Self { + let width = distance * 2.0 * (angle / 2.0).tan(); + let height = width * ASPECT_RATIO; + + Self { + position, + direction: (look_at - position).normalize(), + up: Vector::unit_z(), + distance, + screen: Screen { width, height }, + speed: 0.5, + turn_rate: 60.0 } - - // Teeth - { - let sector_angle: f32 = 2.0 * PI / 12.0; - - // Account for rotation with time - let angle = sector_angle * self.time / turn_rate; - let rotated_point = Vector::new( - point.x, - point.y * angle.cos() - point.z * angle.sin(), - point.y * angle.sin() + point.z * angle.cos() - ); - - // Map all space to the first sector - let point_angle = (rotated_point.z / rotated_point.y).atan(); - let angle2 = -sector_angle * (point_angle / sector_angle).round(); - - let mapped_point = Vector::new( - rotated_point.x, - (rotated_point.y * angle2.cos() - rotated_point.z * angle2.sin()).abs(), - rotated_point.y * angle2.sin() + rotated_point.z * angle2.cos() - ); - - let center = Vector { x: 0.0, y: radius + thickness_over_2, z: 0.0 }; - let size = Vector::new(thickness, thickness * 2.0, thickness); - // Make teeth smooth by subtracting some amount - dist = dist.min(sd_box(mapped_point, center, size) - thickness_over_4); - } - - // Take a slice - dist = dist.max(point.x.abs() - thickness_over_2); - - return dist; - } - pub fn sdf(&self, point: Vector) -> f32 { - self.sd_gear(point, Vector::zero(), 3.0, 0.6, 10.0) } - - pub fn render(& mut self) { + pub fn get_screen_iterator(self) -> (Vector, Vector, Vector) { // Linear transormation operator for calculating screen position // Assumes "initial" screen is perpendicular to OX // and it's bottom edge is parallel to OY let operator = Matrix3::from_cols( self.direction * self.distance, - self.direction.cross(self.up) * self.width, - -self.up * self.height, + self.direction.cross(self.up) * self.screen.width, + -self.up * self.screen.height, ); - let mut ray_dir = operator * Vector::new(1.0, -0.5, -0.5); // Corner - let step_v = operator * Vector3::unit_z() / HEIGHT as f32; - let step_h = operator * Vector3::unit_y() / WIDTH as f32; - - for _i in 0..HEIGHT as usize { - ray_dir += step_v; - let mut row = "\n".to_string(); - for _j in 0..WIDTH as usize { - ray_dir += step_h; - - let collision = self.ray_marching(self.position, ray_dir); + let corner_dir = operator * Vector::new(1.0, -0.5, -0.5); // Corner + let step_v = operator * Vector::unit_z(); + let step_h = operator * Vector::unit_y(); - let brightness = match collision { - Some(point) => self.light_point(point), - None => 0.0 - }; - - row.push(self.palette[((1.0 - brightness) * (self.palette.len() - 1) as f32) as usize]); - } - ray_dir -= step_h * WIDTH as f32; - addstr(&row); - } - } - - pub fn normal(&self, point: Vector) -> Vector { - let d = 0.001; - - let dx = Vector::unit_x() * d; - let dy = Vector::unit_y() * d; - let dz = Vector::unit_z() * d; - - let sdf = self.sdf(point); - - return (Vector { - x: (self.sdf(point + dx) - sdf), - y: (self.sdf(point + dy) - sdf), - z: (self.sdf(point + dz) - sdf), - } / d).normalize() - } - - pub fn ray_marching(&self, origin: Vector, direction: Vector) -> Option<Vector> { - let threshold = 0.1; - - let ray = direction.normalize(); - let mut point = origin; - let mut dist = 0.0; - let mut count = 0; - - while dist < self.brightness && count < 10 { - count += 1; - dist = self.sdf(point); - if dist.abs() < threshold { - return Some(point); - } - point += ray * dist; - } - - return None - } - - pub fn light_point(&self, point: Vector) -> f32 { - let ambient = 0.1; - return ambient + (1.0 - ambient) * (self.diffuse_lighting(point) * 0.7 + self.specular_lighting(point) * 0.3) - } - - pub fn diffuse_lighting(&self, point: Vector) -> f32 { - let mut res: f32 = 1.0; - let mut t = 0.1; - let k = 4.0; - - while t < 1.0 { - let h = self.sdf(point - self.light * t); - if h < 0.001 { - return 0.00 - } - res = res.min(k * h / t); - t += h; - } - - return res - } - - pub fn specular_lighting(&self, point: Vector) -> f32 { - let normal = self.normal(point); - let dot = -(normal.dot(self.light)); - return dot.min(1.0).max(0.0) + // TODO: return an actual iterator + return (corner_dir, step_h, step_v) } } + |