diff options
author | eug-vs <eugene@eug-vs.xyz> | 2021-10-30 14:56:51 +0300 |
---|---|---|
committer | eug-vs <eugene@eug-vs.xyz> | 2021-10-30 14:56:51 +0300 |
commit | ec90992002d5370348c5a72d3faf45303f29c400 (patch) | |
tree | 1a0771204d06d2c208a031744441dc41a5240a12 | |
parent | 61c757f2499d79efe4066ca2d8c1ff7827604543 (diff) | |
download | pistol-ec90992002d5370348c5a72d3faf45303f29c400.tar.gz |
feat: dynamically create SDF from vec of Objects
-rw-r--r-- | src/main.rs | 45 | ||||
-rw-r--r-- | src/renderer.rs | 37 | ||||
-rw-r--r-- | src/sdf.rs | 117 |
3 files changed, 129 insertions, 70 deletions
diff --git a/src/main.rs b/src/main.rs index 694a843..5873996 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,31 +6,42 @@ mod renderer; mod sdf; use std::{f32::consts::PI, time::Instant}; -use cgmath::{Angle, Matrix3, Rad, Vector3, Zero}; +use cgmath::{Angle, Array, Matrix3, Rad, Vector3, Zero}; use ncurses::*; use camera::Camera; use buffer::Buffer; use renderer::Renderer; -use sdf::sd_gear; - +use sdf::{Sphere, Gear, Object, SDBox}; fn main() { - let mut renderer = Renderer { - buffer:Buffer::from_height(50.0, 3.0), - camera: Camera::new( + // This vector will later be built + // by parsing a JSON scene + let mut renderer = Renderer::new( + Box::new(Buffer::from_height(50.0, 3.0)), + Box::new(Camera::new( Vector3::new(-4.0, 0.0, 0.0), Vector3::zero(), PI / 2.0, 1.0 - ) - }; - - // This closure will later be built - // by parsing a JSON scene - let sdf_global = |point: Vector3<f32>, time: f32| -> f32 { - sd_gear(point, time, Vector3::zero(), 2.0, 0.4, 30.0) - }; + )), + vec![ + Box::new(Sphere { + center: Vector3::zero(), + radius: 0.4, + }), + Box::new(Gear { + center: Vector3::zero(), + radius: 2.0, + thickness: 0.4, + turn_rate: 30.0, + }), + Box::new(SDBox { + center: Vector3::new(2.0, 2.0, 0.0), + size: Vector3::from_value(1.0), + }), + ] + ); initscr(); @@ -42,13 +53,9 @@ fn main() { time += 1.0; - let sdf = |point: Vector3<f32>| -> f32 { - sdf_global(point, time) - }; - // Render let timestamp = Instant::now(); - renderer.render(&sdf); + renderer.render(time); addstr(&format!("\nRendered in {:?} ({:.1} FPS)\n", timestamp.elapsed(), 1.0 / timestamp.elapsed().as_secs_f64())); addstr(&format!("Camera: {:?}\n", renderer.camera.position)); addstr(&format!("Facing: {:?}, Up: {:?}\n", renderer.camera.direction, renderer.camera.up)); diff --git a/src/renderer.rs b/src/renderer.rs index 468f4ff..0c6c8d2 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -5,18 +5,41 @@ use std::f32; use crate::Buffer; use crate::Camera; +use crate::Object; type Vector = Vector3<f32>; -pub struct Renderer { - pub camera: Camera, - pub buffer: Buffer, +pub struct Renderer<'a> { + pub camera: Box<Camera>, + pub buffer: Box<Buffer>, + pub sdf: Box<dyn Fn(Vector, f32) -> f32 + 'a>, } -impl Renderer { - pub fn render(&self, sdf: &dyn Fn(Vector) -> f32) { +impl Renderer<'_> { + // TODO: figure out how the fuck it actually works + pub fn new(buffer: Box<Buffer>, camera: Box<Camera>, objects: Vec<Box<dyn Object>>) -> Self { + let sdf = move |point: Vector3<f32>, time: f32| -> f32 { + let mut dist: f32 = 100000.0; + for object in objects.iter() { + dist = dist.min(object.sdf(point, time)); + } + dist + }; + + Self { + buffer, + camera, + sdf: Box::new(sdf) + } + } + + pub fn render(&self, time: f32) { let (mut ray_dir, mut step_h, mut step_v) = self.camera.get_screen_iterator(); + let sdf = |point: Vector3<f32>| -> f32 { + (self.sdf)(point, time) + }; + step_v /= self.buffer.height; step_h /= self.buffer.width; @@ -26,10 +49,10 @@ impl Renderer { for _j in 0..self.buffer.width as usize { ray_dir += step_h; - let collision = Self::ray_march(self.camera.position, ray_dir, sdf); + let collision = Self::ray_march(self.camera.position, ray_dir, &sdf); let brightness = match collision { - Some(point) => Self::light_point(point, sdf), + Some(point) => Self::light_point(point, &sdf), None => 0.0 }; @@ -4,57 +4,86 @@ use cgmath::prelude::*; type Vector = Vector3<f32>; -pub fn sd_sphere(point: Vector, center: Vector, radius: f32) -> f32 { - (point - center).magnitude() - radius +pub trait Object { + fn sdf(&self, point: Vector, time: f32) -> f32; } -pub 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) +pub struct Sphere { + pub center: Vector, + pub radius: f32, } -pub fn sd_gear(point: Vector, time: f32, center: Vector, radius: f32, thickness: f32, turn_rate: f32) -> f32 { - let mut dist: f32; +impl Object for Sphere { + fn sdf(&self, point: Vector, _time: f32) -> f32 { + (point - self.center).magnitude() - self.radius + } +} - let thickness_over_2 = thickness / 2.0; - let thickness_over_4 = thickness / 4.0; +pub struct SDBox { + pub center: Vector, + pub size: Vector, +} - // 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 - } - // Teeth - { - let sector_angle: f32 = 2.0 * PI / 12.0; - - // Account for rotation with time - let angle = sector_angle * 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); +impl Object for SDBox { + fn sdf(&self, point: Vector, _time: f32) -> f32 { + let diff = self.center - point; + let q = diff.map(|n| n.abs()) - self.size / 2.0; + return q.map(|n| n.max(0.0)).magnitude() + (q.y.max(q.z).max(q.x)).min(0.0) } +} + +pub struct Gear { + pub center: Vector, + pub radius: f32, + pub thickness: f32, + pub turn_rate: f32, +} + +impl Object for Gear { + fn sdf(&self, point: Vector, time: f32) -> f32 { + let mut dist: f32; - // Take a slice - dist = dist.max(point.x.abs() - thickness_over_2); + let thickness_over_2 = self.thickness / 2.0; + let thickness_over_4 = self.thickness / 4.0; - return dist; + // Ring + { + let cylinder_dist = (Vector::new(0.0, point.y, point.z) - self.center).magnitude() - (self.radius - thickness_over_4); + dist = cylinder_dist.abs() - thickness_over_2; // Make cylinder hollow + } + // Teeth + { + let sector_angle: f32 = 2.0 * PI / 12.0; + + // Account for rotation with time + let angle = sector_angle * time / self.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() + ); + + // Make teeth smooth by subtracting some amount + dist = dist.min(SDBox { + center: Vector { x: 0.0, y: self.radius + thickness_over_2, z: 0.0 }, + size: Vector::new(self.thickness, self.thickness * 2.0, self.thickness), + }.sdf(mapped_point, time) - thickness_over_4); + } + + // Take a slice + dist = dist.max(point.x.abs() - thickness_over_2); + + return dist; + } } + |