aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreug-vs <eugene@eug-vs.xyz>2021-10-28 15:47:56 +0300
committereug-vs <eugene@eug-vs.xyz>2021-10-29 01:37:29 +0300
commit974536dfc3fef1ea28bd9d71930ac2894e0c6d1c (patch)
tree14a5920390dc4f0f72425dd1abfdd797c685ba8f
parente4b516d9fe63a2fa5da9dfe2125798f15453e83f (diff)
downloadpistol-974536dfc3fef1ea28bd9d71930ac2894e0c6d1c.tar.gz
feat: optimize the fuck out of everything
-rw-r--r--src/camera.rs124
-rw-r--r--src/main.rs9
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));