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!
This commit is contained in:
2023-06-24 11:36:23 -05:00
parent d97b4ced83
commit 814a65859a
3 changed files with 99 additions and 133 deletions

View File

@@ -18,49 +18,72 @@ impl HitRecord{
} }
} }
pub trait Hittable { pub enum Hittable {
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord>; Sphere { center: Vec3, radius: f32, material: Option<Material> },
HittableList { hittables: Vec<Hittable> }
} }
pub struct HittableList{ impl Hittable {
hittables: Vec<Box<dyn Hittable>>, pub fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
} 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{ for item in hittables {
pub fn new() -> HittableList { if let Some(record) = item.hit(r, t_min, might_return.t){
HittableList { hit_anything = true;
hittables: Vec::<Box<dyn Hittable>>::new() might_return = record;
} }
} }
pub fn add(&mut self, hittable: Box<dyn Hittable> ) -> () { if hit_anything{
self.hittables.push(hittable); return Some(might_return);
} } else { return None; }
// pub fn clear(&mut self) -> () { }
// self.hittables.clear();
// }
}
impl Hittable for HittableList{ Hittable::Sphere { center, radius, material } => {
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord>{ let oc = r.orig - *center;
let mut might_return = HitRecord { let a = r.dir.length_squared();
p: Vec3::zero(), let half_b = Vec3::dot(oc, r.dir);
normal: Vec3::zero(), let c = oc.length_squared() - radius * radius;
material: None, let discriminant = half_b*half_b - a*c;
t: t_max,
front_face: false,
};
let mut hit_anything = false;
for item in &self.hittables { if discriminant < 0.0 {
if let Some(record) = item.hit(r, t_min, might_return.t){ return None;
hit_anything = true; }
might_return = record; 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); pub fn push(&mut self, item: Hittable) {
} else { return None; } if let Hittable::HittableList { hittables } = self {
hittables.push(item);
}
} }
} }

View File

@@ -5,15 +5,11 @@ mod ray;
mod camera; mod camera;
mod material; mod material;
mod hittable; mod hittable;
mod sphere;
use crate::vec3::Vec3; use crate::vec3::Vec3;
use crate::ray::Ray; use crate::ray::Ray;
use crate::sphere::Sphere; use crate::hittable::Hittable;
use crate::hittable::{ use crate::hittable::Hittable::{HittableList, Sphere};
Hittable,
HittableList,
};
use crate::material::Material; use crate::material::Material;
use crate::camera::Camera; use crate::camera::Camera;
@@ -68,7 +64,7 @@ fn main() {
eprintln!("Done!"); 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<Vec3> { 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<Vec3> {
let distrib_zero_one = Uniform::new(0.0, 1.0); let distrib_zero_one = Uniform::new(0.0, 1.0);
let distrib_plusminus_one = Uniform::new(-1.0, 1.0); let distrib_plusminus_one = Uniform::new(-1.0, 1.0);
let mut line = Vec::<Vec3>::new(); let mut line = Vec::<Vec3>::new();
@@ -85,7 +81,7 @@ fn render_line(y: i32, image: (i32, i32), samples_per_pixel: u32, world: &dyn Hi
return line; return line;
} }
fn ray_color(r: Ray, world: &dyn Hittable, depth: u32, srng: &mut SmallRng, distrib: Uniform<f32> ) -> Vec3 { fn ray_color(r: Ray, world: &Hittable, depth: u32, srng: &mut SmallRng, distrib: Uniform<f32> ) -> Vec3 {
// recursion depth guard // recursion depth guard
if depth == 0 { if depth == 0 {
return Vec3::zero(); 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 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 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::<Hittable>::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); let distrib_zero_one = Uniform::new(0.0, 1.0);
for a in -11..11 { for a in -11..11 {
@@ -133,14 +129,12 @@ fn random_scene(srng: &mut SmallRng) -> HittableList {
// diffuse // diffuse
let albedo = Vec3::rand(srng, distrib_zero_one) * Vec3::rand(srng, distrib_zero_one); let albedo = Vec3::rand(srng, distrib_zero_one) * Vec3::rand(srng, distrib_zero_one);
let sphere_material = Material::Lambertian { albedo }; let sphere_material = Material::Lambertian { albedo };
world.add( world.push(
Box::new( Hittable::Sphere {
Sphere { center,
center, radius: 0.2,
radius: 0.2, material: Some(sphere_material),
material: Some(sphere_material), }
}
)
); );
} else if choose_mat < 0.95 { } else if choose_mat < 0.95 {
// metal // metal
@@ -150,26 +144,22 @@ fn random_scene(srng: &mut SmallRng) -> HittableList {
let albedo = Vec3::rand(srng, distr_albedo); let albedo = Vec3::rand(srng, distr_albedo);
let fuzz = srng.sample(distr_fuzz); let fuzz = srng.sample(distr_fuzz);
let material = Material::Metal { albedo, fuzz }; let material = Material::Metal { albedo, fuzz };
world.add( world.push(
Box::new( Hittable::Sphere {
Sphere { center,
center, radius: 0.2,
radius: 0.2, material: Some(material),
material: Some(material), }
}
)
); );
} else { } else {
// glass // glass
let material = Material::Dielectric { index_refraction: 1.5 }; let material = Material::Dielectric { index_refraction: 1.5 };
world.add( world.push(
Box::new( Hittable::Sphere{
Sphere{ center,
center, radius: 0.2,
radius: 0.2, material: Some(material),
material: Some(material), }
}
)
); );
}; };
@@ -178,13 +168,25 @@ fn random_scene(srng: &mut SmallRng) -> HittableList {
} }
let material1 = Material::Dielectric { index_refraction: 1.5 }; 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) }; 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 }; 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; return world;
} }

View File

@@ -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<Material>,
}
impl Sphere {
pub fn new(x: f32, y: f32, z: f32, r: f32, mat: Option<Material>) -> 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<HitRecord>{
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)
}
}