1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
use crate::calculus::calculus::{Point3, Ray, Vec3};
use crate::common::Color;
use crate::calculus::Interval;
use crate::material::{Material, MaterialType};
#[derive(Clone)]
pub struct HitRecord {
pub position: Point3,
pub normal: Vec3,
pub t: f32,
pub front_face: bool,
pub material: Material,
}
impl Default for HitRecord {
fn default() -> Self {
Self {
position: Point3 {x:0.0, y:0.0, z:0.0},
normal: Vec3 {x:0.0, y:0.0, z:0.0},
t: 0.0,
front_face: false,
material: Material::new(Color::new(1.0, 1.0, 1.0), MaterialType::Diffuse),
}
}
}
impl HitRecord {
pub fn set_face_normal(&mut self, r: &Ray, outward_normal: Vec3) {
self.front_face = r.direction.dot_prod(&outward_normal) < 0.0;
self.normal = if self.front_face { outward_normal } else { outward_normal.scalar_mul(-1.0) };
}
}
pub trait Hittable {
fn hit(&self, r: &Ray, ray_t: Interval, rec: &mut HitRecord) -> bool;
}
pub struct Sphere {
center: Point3,
radius: f32,
material: Material,
}
impl Sphere {
pub fn new(center: Vec3, radius: f32, material: Material) -> Self {
let radius = f32::max(0.0, radius);
Self { center, radius, material }
}
}
impl Hittable for Sphere {
fn hit(&self, r: &Ray, ray_t: Interval, rec: &mut HitRecord) -> bool {
let oc = self.center.sub(&r.origin);
let a = r.direction.mag_sqr();
let h = r.direction.dot_prod(&oc);
let c = oc.mag_sqr() - self.radius * self.radius;
let discriminant = h * h - a * c;
if discriminant < 0.0 {
return false;
}
let sqrtd = discriminant.sqrt();
let mut root = (h - sqrtd) / a;
if root <= ray_t.min || ray_t.max <= root {
root = (h + sqrtd) / a;
if root <= ray_t.min || ray_t.max <= root {
return false;
}
}
rec.t = root;
rec.position = r.at(rec.t);
let outward_normal = rec.position
.sub(&self.center)
.scalar_mul(1.0 / self.radius);
rec.set_face_normal(r, outward_normal);
rec.material = self.material.clone();
true
}
}
pub struct HittableList {
objects: Vec<Box<dyn Hittable>>,
}
impl HittableList {
pub fn new() -> Self {
Self { objects: vec![] }
}
pub fn push<T: Hittable + 'static>(&mut self, object: T) {
self.objects.push(Box::new(object));
}
}
impl Hittable for HittableList {
fn hit(&self, r: &Ray, ray_t: Interval, rec: &mut HitRecord) -> bool {
let mut temp_rec = rec.clone();
let mut hit_anything = false;
let mut closest_so_far = ray_t.max;
self.objects.iter().for_each(
|object| {
let interval = Interval::new(ray_t.min, closest_so_far);
if object.hit(r, interval, &mut temp_rec) {
hit_anything = true;
closest_so_far = temp_rec.t;
*rec = temp_rec.clone();
}
}
);
hit_anything
}
}
|