diff options
author | eug-vs <eugene@eug-vs.xyz> | 2021-10-28 15:47:56 +0300 |
---|---|---|
committer | eug-vs <eugene@eug-vs.xyz> | 2021-10-29 01:37:29 +0300 |
commit | 974536dfc3fef1ea28bd9d71930ac2894e0c6d1c (patch) | |
tree | 14a5920390dc4f0f72425dd1abfdd797c685ba8f | |
parent | e4b516d9fe63a2fa5da9dfe2125798f15453e83f (diff) | |
download | pistol-974536dfc3fef1ea28bd9d71930ac2894e0c6d1c.tar.gz |
feat: optimize the fuck out of everything
-rw-r--r-- | src/camera.rs | 124 | ||||
-rw-r--r-- | src/main.rs | 9 |
2 files changed, 61 insertions, 72 deletions
diff --git a/src/camera.rs b/src/camera.rs index ed8117e..8768ace 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,30 +1,18 @@ use cgmath::Matrix3; -use cgmath::Rad; use cgmath::Vector3; use cgmath::prelude::*; -use std::fmt; +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 = 30; +pub const HEIGHT: i32 = 60; pub const WIDTH: i32 = HEIGHT * 3; #[derive(Debug)] -pub struct Buffer (pub [[char; WIDTH as usize]; HEIGHT as usize]); - -impl fmt::Display for Buffer { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for i in 0..HEIGHT as usize { - for j in 0..WIDTH as usize { - write!(f, "{}", self.0[i][j])?; - } - writeln!(f)?; - } - write!(f, "") - } -} - -#[derive(Debug)] pub struct Camera { pub time: f32, pub position: Vector, @@ -35,9 +23,11 @@ pub struct Camera { pub distance: f32, pub brightness: f32, pub aspect_ratio: f32, - pub buffer: Buffer, 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 { @@ -56,39 +46,49 @@ fn sd_box(point: Vector, center: Vector, size: Vector) -> f32 { return q.map(|n| n.max(0.0)).magnitude() + (q.y.max(q.z).max(q.x)).min(0.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 / 4.0); - dist = cylinder_dist.abs() - thickness / 2.0; // Make cylinder hollow + 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 } // Teeth { - let sector_angle = Rad::full_turn() / 12.0; - - let center = Vector { x: 0.0, y: radius + thickness / 2.0, z: 0.0 }; - let size = Vector::new(thickness, thickness * 2.0, thickness); + let sector_angle: f32 = 2.0 * PI / 12.0; // Account for rotation with time - let rotated_point = Matrix3::from_angle_x(sector_angle * self.time / turn_rate) * point; + 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 sector = (point_angle / sector_angle.0).round(); - let mut mapped_point = Matrix3::from_angle_x(-sector_angle * sector) * rotated_point; - mapped_point.y = mapped_point.y.abs(); + 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 / 4.0); + dist = dist.min(sd_box(mapped_point, center, size) - thickness_over_4); } // Take a slice - dist = dist.max(point.x.abs() - thickness / 2.0); + dist = dist.max(point.x.abs() - thickness_over_2); return dist; } @@ -96,34 +96,25 @@ impl Camera { self.sd_gear(point, Vector::zero(), 3.0, 0.6, 10.0) } - pub fn screen(&self) -> (f32, f32) { - let width = self.distance * 2.0 * (self.angle / 2.0).tan(); - let height = width * self.aspect_ratio; - // println!("Screen {}x{} units", width, height); - (width, height) - } - pub fn render(& mut self) { - let palette = "$@B%8&WM#oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. ".to_string(); - let (screen_width, screen_height) = self.screen(); - - let cross = self.up.cross(self.direction); - // 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, - cross * screen_width, - self.up * screen_height, + self.direction.cross(self.up) * self.width, + -self.up * self.height, ); - for i in 0..HEIGHT as usize { - let ix = i as f32 / HEIGHT as f32; - for j in 0..WIDTH as usize { - let jx = j as f32 / WIDTH as f32; - // Apply transform to unit square centered at (1, 0, 0) - let ray_dir = operator * Vector { x: 1.0, y: 0.5 - jx, z: 0.5 - ix }; + 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); @@ -132,11 +123,10 @@ impl Camera { None => 0.0 }; - self.buffer.0[i][j] = palette - .chars() - .nth(((1.0 - brightness) * (palette.len() - 1) as f32) as usize) - .unwrap(); + row.push(self.palette[((1.0 - brightness) * (self.palette.len() - 1) as f32) as usize]); } + ray_dir -= step_h * WIDTH as f32; + addstr(&row); } } @@ -147,11 +137,13 @@ impl Camera { let dy = Vector::unit_y() * d; let dz = Vector::unit_z() * d; + let sdf = self.sdf(point); + return (Vector { - x: (self.sdf(point + dx) - self.sdf(point - dx)), - y: (self.sdf(point + dy) - self.sdf(point - dy)), - z: (self.sdf(point + dz) - self.sdf(point - dz)), - } / (2.0 * d)).normalize() + 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> { @@ -162,7 +154,7 @@ impl Camera { let mut dist = 0.0; let mut count = 0; - while dist < 10.0 && count < 30 { + while dist < 8.0 && count < 10 { count += 1; dist = self.sdf(point); if dist.abs() < threshold { @@ -181,20 +173,16 @@ impl Camera { pub fn diffuse_lighting(&self, point: Vector) -> f32 { let mut res: f32 = 1.0; - let mut ph = 1e20; - let mut t = 0.001; + let mut t = 0.1; let k = 4.0; - while t < 7.0 { + while t < 1.0 { let h = self.sdf(point - self.light * t); if h < 0.001 { return 0.00 } - let y = h * h / (2.0 * ph); - let d = (h * h - y * y).sqrt(); - res = res.min(k * d / (t - y).max(0.0)); + res = res.min(k * h / t); t += h; - ph = h; } return res diff --git a/src/main.rs b/src/main.rs index c139888..aa5a456 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,10 +3,10 @@ extern crate ncurses; mod camera; mod canvas; use std::{f32::consts::PI, time::Instant}; -use cgmath::{Angle, InnerSpace, Matrix3, Rad, Vector3, Zero}; +use cgmath::{Angle, InnerSpace, Matrix3, Rad, Vector3}; use ncurses::*; -use crate::camera::{Buffer, Camera, WIDTH, HEIGHT}; +use crate::camera::{Camera, WIDTH, HEIGHT}; fn main() { let mut cam = Camera { @@ -18,10 +18,12 @@ fn main() { distance: 1.0, aspect_ratio: 2.0 * HEIGHT as f32 / WIDTH as f32, brightness: 5.0, - buffer: Buffer([[' '; WIDTH as usize]; HEIGHT as usize]), time: 0.0, speed: 0.5, turn_rate: 30.0, + width: 2.0, + height: 4.0 * HEIGHT as f32 / WIDTH as f32, + palette: "$@B%8&WM#oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. ".chars().collect(), }; initscr(); @@ -34,7 +36,6 @@ fn main() { cam.time += 1.0; let timestamp = Instant::now(); cam.render(); - addstr(&cam.buffer.to_string()); addstr(&format!("\nRendered in {:?} ({:.0} FPS)\n", timestamp.elapsed(), 1.0 / timestamp.elapsed().as_secs_f64())); addstr(&format!("\nTime: {:?}\n", cam.time)); addstr(&format!("Camera: {:?}\n", cam.position)); |