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::{ Float, rand_utils };
use crate::tracer::{
hit::Hit, ray::Ray, Material, Texture, Color,
Medium, Object, Rectangle, Sampleable
};
#[cfg(test)]
mod scene_tests;
/// Empty cornell box, custom material for floor, and left and right walls.
mod empty_box;
/// Defines a scene in 3D space
#[derive(Default)]
pub struct Scene {
/// All of the objects in the scene.
pub objects: Vec<Box<dyn Object>>,
/// Contains all lights in the scene.
pub lights: Vec<Box<dyn Sampleable>>,
/// Medium that the scene is filled with
pub medium: Option<Medium>,
}
impl Scene {
/// Add a non-light object to the scene
pub fn add(&mut self, obj: Box<dyn Object>) {
// how to check material is not light?
self.objects.push(obj);
}
/// Adds a light to the scene
pub fn add_light(&mut self, light: Box<dyn Sampleable>) {
// how to check material is light?
self.lights.push(light);
}
/// Sets the volumetric medium of the scene
pub fn set_medium(&mut self, medium: Medium) {
self.medium = Some(medium);
}
/// Returns number of lights in the scene
pub fn num_lights(&self) -> usize {
self.lights.len()
}
/// Choose one of the lights uniformly at random. Crash if no lights.
pub fn uniform_random_light(&self) -> &dyn Sampleable {
let rnd = rand_utils::rand_float();
let idx = (rnd * self.lights.len() as Float).floor() as usize;
self.lights[idx].as_ref()
}
/// Returns the transmittance due to volumetric medium
pub fn transmittance(&self, t: Float) -> Color {
match &self.medium {
None => Color::WHITE,
Some(medium) => medium.transmittance(t),
}
}
/// Returns the closest object `r` hits and `None` if no hits
pub fn hit(&self, r: &Ray) -> Option<Hit> {
let mut t_max = crate::INF;
let mut h = None;
if let Some(medium) = &self.medium {
// if we hit an object, it must be closer than what we have
h = medium.hit(r, 0.0, t_max).or(h);
// update distance to closest found so far
t_max = h.as_ref().map_or(t_max, |hit| hit.t);
}
for object in &self.objects {
// if we hit an object, it must be closer than what we have
h = object.hit(r, 0.0, t_max).or(h);
// update distance to closest found so far
t_max = h.as_ref().map_or(t_max, |hit| hit.t);
}
// lazy, something better should be done.
// use enum wrapper? have issues with instances..
for light in &self.lights {
h = light.hit(r, 0.0, t_max).map(|mut hit| {
t_max = hit.t;
hit.light = Some(light.as_ref());
hit
}).or(h);
}
h
}
/// Does ray `r` reach the light object `light`?
pub fn hit_light<'a>(&'a self, r: &Ray, light: &'a dyn Sampleable) -> Option<Hit> {
let light_hit = match light.hit(r, 0.0, crate::INF) {
None => return None,
Some(hi) => hi,
};
// consider also checking medium
let t_max = light_hit.t - crate::EPSILON;
for object in &self.objects {
if object.hit(r, 0.0, t_max).is_some() {
return None;
}
}
for light in &self.lights {
if light.hit(r, 0.0, t_max).is_some() {
return None;
}
}
Some( light_hit )
}
}