140 lines
4.5 KiB
Rust

use std::sync::Arc;
use crate::{
aabb::AABB,
cuboid::Cuboid,
hitable::{Hit, HitRecord},
material::Material,
ray::Ray,
vec3::Vec3,
};
#[derive(Debug)]
pub struct Glowybox {
p_min: Vec3,
p_max: Vec3,
main: Cuboid,
edges: [Cuboid; 12],
}
impl Glowybox {
// This clippy doesn't work right with Arc.
#[allow(clippy::needless_pass_by_value)]
pub fn new(
p_min: Vec3,
p_max: Vec3,
edge_thickness: f32,
main_material: Arc<dyn Material>,
edge_material: Arc<dyn Material>,
) -> Glowybox {
assert!(p_min.x < p_max.x);
assert!(p_min.y < p_max.y);
assert!(p_min.z < p_max.z);
let main = Cuboid::new(p_min, p_max, main_material);
// Top edges
let ht = edge_thickness / 2.;
let edges = [
// Top edges
Cuboid::new(
[p_min.x - ht, p_max.y - ht, p_min.z - ht].into(),
[p_min.x + ht, p_max.y + ht, p_max.z + ht].into(),
Arc::clone(&edge_material),
),
Cuboid::new(
[p_min.x - ht, p_max.y - ht, p_min.z - ht].into(),
[p_max.x + ht, p_max.y + ht, p_min.z + ht].into(),
Arc::clone(&edge_material),
),
Cuboid::new(
[p_max.x - ht, p_max.y - ht, p_min.z - ht].into(),
[p_max.x + ht, p_max.y + ht, p_max.z + ht].into(),
Arc::clone(&edge_material),
),
Cuboid::new(
[p_min.x - ht, p_max.y - ht, p_max.z - ht].into(),
[p_max.x + ht, p_max.y + ht, p_max.z + ht].into(),
Arc::clone(&edge_material),
),
// Bottom edges
Cuboid::new(
[p_min.x - ht, p_min.y - ht, p_min.z - ht].into(),
[p_min.x + ht, p_min.y + ht, p_max.z + ht].into(),
Arc::clone(&edge_material),
),
Cuboid::new(
[p_min.x - ht, p_min.y - ht, p_min.z - ht].into(),
[p_max.x + ht, p_min.y + ht, p_min.z + ht].into(),
Arc::clone(&edge_material),
),
Cuboid::new(
[p_max.x - ht, p_min.y - ht, p_min.z - ht].into(),
[p_max.x + ht, p_min.y + ht, p_max.z + ht].into(),
Arc::clone(&edge_material),
),
Cuboid::new(
[p_min.x - ht, p_min.y - ht, p_max.z - ht].into(),
[p_max.x + ht, p_min.y + ht, p_max.z + ht].into(),
Arc::clone(&edge_material),
),
// Middle edges
Cuboid::new(
[p_min.x - ht, p_min.y - ht, p_min.z - ht].into(),
[p_min.x + ht, p_max.y + ht, p_min.z + ht].into(),
Arc::clone(&edge_material),
),
Cuboid::new(
[p_min.x - ht, p_min.y - ht, p_max.z - ht].into(),
[p_min.x + ht, p_max.y + ht, p_max.z + ht].into(),
Arc::clone(&edge_material),
),
Cuboid::new(
[p_max.x - ht, p_min.y - ht, p_min.z - ht].into(),
[p_max.x + ht, p_max.y + ht, p_min.z + ht].into(),
Arc::clone(&edge_material),
),
Cuboid::new(
[p_max.x - ht, p_min.y - ht, p_max.z - ht].into(),
[p_max.x + ht, p_max.y + ht, p_max.z + ht].into(),
Arc::clone(&edge_material),
),
];
let p_min = [p_min.x - ht, p_min.y - ht, p_min.z - ht].into();
let p_max = [p_max.x + ht, p_max.y + ht, p_max.z + ht].into();
Glowybox {
p_min,
p_max,
main,
edges,
}
}
}
impl Hit for Glowybox {
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
let mut edge_hit = None;
for edge in &self.edges {
if let Some(hit) = edge.hit(r, t_min, t_max) {
edge_hit = Some(hit);
break;
}
}
let main_hit = self.main.hit(r, t_min, t_max);
match (edge_hit, main_hit) {
(Some(ehit), Some(mhit)) => {
if mhit.t < ehit.t {
Some(mhit)
} else {
Some(ehit)
}
}
(Some(ehit), None) => Some(ehit),
(None, Some(mhit)) => Some(mhit),
_ => None,
}
}
fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option<AABB> {
Some(AABB::new(self.p_min, self.p_max))
}
}