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:
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
78
src/main.rs
78
src/main.rs
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user