aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreug-vs <eugene@eug-vs.xyz>2021-10-30 14:56:51 +0300
committereug-vs <eugene@eug-vs.xyz>2021-10-30 14:56:51 +0300
commitec90992002d5370348c5a72d3faf45303f29c400 (patch)
tree1a0771204d06d2c208a031744441dc41a5240a12
parent61c757f2499d79efe4066ca2d8c1ff7827604543 (diff)
downloadpistol-ec90992002d5370348c5a72d3faf45303f29c400.tar.gz
feat: dynamically create SDF from vec of Objects
-rw-r--r--src/main.rs45
-rw-r--r--src/renderer.rs37
-rw-r--r--src/sdf.rs117
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
};
diff --git a/src/sdf.rs b/src/sdf.rs
index 00a4a11..c46deab 100644
--- a/src/sdf.rs
+++ b/src/sdf.rs
@@ -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;
+ }
}
+