From 814a65859a24300cbbfd28a25012007c2815f8e1 Mon Sep 17 00:00:00 2001 From: Robert Garrett Date: Sat, 24 Jun 2023 11:36:23 -0500 Subject: [PATCH] Hittables as enum instead of trait objects I hit an issue implementing the threading where the use of trait objects got real angry about lifetimes. The variants of geometry could be described as a runtime variable (thus requiring dynamic dispatch), but I'm not gonna do that. Instead: Enums! --- src/hittable.rs | 95 ++++++++++++++++++++++++++++++------------------- src/main.rs | 78 ++++++++++++++++++++-------------------- src/sphere.rs | 59 ------------------------------ 3 files changed, 99 insertions(+), 133 deletions(-) delete mode 100644 src/sphere.rs diff --git a/src/hittable.rs b/src/hittable.rs index d4f7950..9ef5ea7 100644 --- a/src/hittable.rs +++ b/src/hittable.rs @@ -18,49 +18,72 @@ impl HitRecord{ } } -pub trait Hittable { - fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option; +pub enum Hittable { + Sphere { center: Vec3, radius: f32, material: Option }, + HittableList { hittables: Vec } } -pub struct HittableList{ - hittables: Vec>, -} +impl Hittable { + pub fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { + match self { + Hittable::HittableList { hittables } => { + let mut might_return = HitRecord { + p: Vec3::zero(), + normal: Vec3::zero(), + material: None, + t: t_max, + front_face: false, + }; + let mut hit_anything = false; -impl HittableList{ - pub fn new() -> HittableList { - HittableList { - hittables: Vec::>::new() - } - } - pub fn add(&mut self, hittable: Box ) -> () { - self.hittables.push(hittable); - } -// pub fn clear(&mut self) -> () { -// self.hittables.clear(); -// } -} + for item in hittables { + if let Some(record) = item.hit(r, t_min, might_return.t){ + hit_anything = true; + might_return = record; + } + } + if hit_anything{ + return Some(might_return); + } else { return None; } + } -impl Hittable for HittableList{ - fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option{ - let mut might_return = HitRecord { - p: Vec3::zero(), - normal: Vec3::zero(), - material: None, - t: t_max, - front_face: false, - }; - let mut hit_anything = false; + Hittable::Sphere { center, radius, material } => { + let oc = r.orig - *center; + let a = r.dir.length_squared(); + let half_b = Vec3::dot(oc, r.dir); + let c = oc.length_squared() - radius * radius; + let discriminant = half_b*half_b - a*c; - for item in &self.hittables { - if let Some(record) = item.hit(r, t_min, might_return.t){ - hit_anything = true; - might_return = record; + if discriminant < 0.0 { + return None; + } + let sqrtd = discriminant.sqrt(); + + // nearest root that lies within tolerance + let mut root = (-half_b - sqrtd) / a; + if root < t_min || root > t_max { + root = (-half_b + sqrtd) / a; + if root < t_min || root > t_max { + return None; + } + } + let mut record = HitRecord{ + p: r.at(root), + normal: (r.at(root) - *center) / *radius, + material: *material, + t: root, + front_face: false, + }; + let outward_normal = (record.p - *center) / *radius; + record.set_face_normal(r, outward_normal); + Some(record) } } - if hit_anything{ - return Some(might_return); - } else { return None; } + } + pub fn push(&mut self, item: Hittable) { + if let Hittable::HittableList { hittables } = self { + hittables.push(item); + } } } - diff --git a/src/main.rs b/src/main.rs index d8eed8c..fe59706 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,15 +5,11 @@ mod ray; mod camera; mod material; mod hittable; -mod sphere; use crate::vec3::Vec3; use crate::ray::Ray; -use crate::sphere::Sphere; -use crate::hittable::{ - Hittable, - HittableList, -}; +use crate::hittable::Hittable; +use crate::hittable::Hittable::{HittableList, Sphere}; use crate::material::Material; use crate::camera::Camera; @@ -68,7 +64,7 @@ fn main() { eprintln!("Done!"); } -fn render_line(y: i32, image: (i32, i32), samples_per_pixel: u32, world: &dyn Hittable, max_depth: u32, small_rng: &mut SmallRng, cam: &camera::Camera ) -> Vec { +fn render_line(y: i32, image: (i32, i32), samples_per_pixel: u32, world: &Hittable, max_depth: u32, small_rng: &mut SmallRng, cam: &camera::Camera ) -> Vec { let distrib_zero_one = Uniform::new(0.0, 1.0); let distrib_plusminus_one = Uniform::new(-1.0, 1.0); let mut line = Vec::::new(); @@ -85,7 +81,7 @@ fn render_line(y: i32, image: (i32, i32), samples_per_pixel: u32, world: &dyn Hi return line; } -fn ray_color(r: Ray, world: &dyn Hittable, depth: u32, srng: &mut SmallRng, distrib: Uniform ) -> Vec3 { +fn ray_color(r: Ray, world: &Hittable, depth: u32, srng: &mut SmallRng, distrib: Uniform ) -> Vec3 { // recursion depth guard if depth == 0 { return Vec3::zero(); @@ -112,11 +108,11 @@ fn ray_color(r: Ray, world: &dyn Hittable, depth: u32, srng: &mut SmallRng, dist return Vec3::ones() * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t } -fn random_scene(srng: &mut SmallRng) -> HittableList { +fn random_scene(srng: &mut SmallRng) -> Hittable { let mat_ground = Material::Lambertian { albedo: Vec3::new(0.5, 0.5, 0.5) }; - let mut world = HittableList::new(); + let mut world = Hittable::HittableList { hittables : Vec::::new() }; - world.add( Box::new( Sphere::new(0.0, -1000.0, 0.0, 1000.0, Some(mat_ground) ))); + world.push( Hittable::Sphere { center: Vec3::new(0.0, -1000.0, 0.0), radius: 1000.0, material: Some(mat_ground) }); let distrib_zero_one = Uniform::new(0.0, 1.0); for a in -11..11 { @@ -133,14 +129,12 @@ fn random_scene(srng: &mut SmallRng) -> HittableList { // diffuse let albedo = Vec3::rand(srng, distrib_zero_one) * Vec3::rand(srng, distrib_zero_one); let sphere_material = Material::Lambertian { albedo }; - world.add( - Box::new( - Sphere { - center, - radius: 0.2, - material: Some(sphere_material), - } - ) + world.push( + Hittable::Sphere { + center, + radius: 0.2, + material: Some(sphere_material), + } ); } else if choose_mat < 0.95 { // metal @@ -150,26 +144,22 @@ fn random_scene(srng: &mut SmallRng) -> HittableList { let albedo = Vec3::rand(srng, distr_albedo); let fuzz = srng.sample(distr_fuzz); let material = Material::Metal { albedo, fuzz }; - world.add( - Box::new( - Sphere { - center, - radius: 0.2, - material: Some(material), - } - ) + world.push( + Hittable::Sphere { + center, + radius: 0.2, + material: Some(material), + } ); } else { // glass let material = Material::Dielectric { index_refraction: 1.5 }; - world.add( - Box::new( - Sphere{ - center, - radius: 0.2, - material: Some(material), - } - ) + world.push( + Hittable::Sphere{ + center, + radius: 0.2, + material: Some(material), + } ); }; @@ -178,13 +168,25 @@ fn random_scene(srng: &mut SmallRng) -> HittableList { } let material1 = Material::Dielectric { index_refraction: 1.5 }; - world.add(Box::new( Sphere::new(0.0, 1.0, 0.0, 1.0, Some(material1)) )); + world.push( Hittable::Sphere{ + center: Vec3::new(0.0, 1.0, 0.0), + radius: 1.0, + material: Some(material1) + }); let material2 = Material::Lambertian { albedo: Vec3::new(0.4, 0.2, 0.1) }; - world.add(Box::new( Sphere::new(-4.0, 1.0, 0.0, 1.0, Some(material2)) )); + world.push( Hittable::Sphere { + center: Vec3::new(-4.0, 1.0, 0.0), + radius: 1.0, + material: Some(material2) + }); let material3 = Material::Metal { albedo: Vec3::new(0.7, 0.6, 0.5), fuzz: 0.0 }; - world.add(Box::new( Sphere::new(4.0, 1.0, 0.0, 1.0, Some(material3)) )); + world.push( Hittable::Sphere { + center: Vec3::new(4.0, 1.0, 0.0), + radius: 1.0, + material: Some(material3) + }); return world; } diff --git a/src/sphere.rs b/src/sphere.rs deleted file mode 100644 index d9d0874..0000000 --- a/src/sphere.rs +++ /dev/null @@ -1,59 +0,0 @@ - -use crate::vec3::Vec3; -use crate::hittable::{ - Hittable, - HitRecord, -}; -use crate::material::Material; -use crate::ray::Ray; - -pub struct Sphere{ - pub center: Vec3, - pub radius: f32, - pub material: Option, -} - -impl Sphere { - pub fn new(x: f32, y: f32, z: f32, r: f32, mat: Option) -> Sphere { - Sphere { - center: Vec3::new(x, y, z), - radius: r, - material: mat, - } - } -} - -impl Hittable for Sphere { - fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option{ - let oc = r.orig - self.center; - let a = r.dir.length_squared(); - let half_b = Vec3::dot(oc, r.dir); - let c = oc.length_squared() - self.radius * self.radius; - let discriminant = half_b*half_b - a*c; - - if discriminant < 0.0 { - return None; - } - let sqrtd = discriminant.sqrt(); - - // nearest root that lies within tolerance - let mut root = (-half_b - sqrtd) / a; - if root < t_min || root > t_max { - root = (-half_b + sqrtd) / a; - if root < t_min || root > t_max { - return None; - } - } - let mut record = HitRecord{ - p: r.at(root), - normal: (r.at(root) - self.center) / self.radius, - material: self.material, - t: root, - front_face: false, - }; - let outward_normal = (record.p - self.center) / self.radius; - record.set_face_normal(r, outward_normal); - Some(record) - } -} -