aboutsummaryrefslogtreecommitdiff
path: root/src/world
diff options
context:
space:
mode:
Diffstat (limited to 'src/world')
-rw-r--r--src/world/mod.rs23
-rw-r--r--src/world/scene.rs52
-rw-r--r--src/world/shapes/mod.rs4
-rw-r--r--src/world/shapes/sphere.rs70
4 files changed, 149 insertions, 0 deletions
diff --git a/src/world/mod.rs b/src/world/mod.rs
new file mode 100644
index 0000000..ef239db
--- /dev/null
+++ b/src/world/mod.rs
@@ -0,0 +1,23 @@
+//! Manages world objects, and implements intersection
+pub mod shapes;
+
+mod scene;
+pub use scene::*;
+
+use std::rc::Rc;
+use crate::core::Hittable;
+use crate::material::Material;
+
+pub struct Object {
+ pub shape: Box<dyn Hittable>,
+ pub mat: Rc<dyn Material>,
+}
+
+impl Object {
+ pub fn new(mat: Rc<dyn Material>, shape: Box<dyn Hittable>) -> Self {
+ Object {
+ mat,
+ shape,
+ }
+ }
+}
diff --git a/src/world/scene.rs b/src/world/scene.rs
new file mode 100644
index 0000000..444e915
--- /dev/null
+++ b/src/world/scene.rs
@@ -0,0 +1,52 @@
+use crate::core::{Ray, Intersection};
+use crate::material::Material;
+
+use super::Object;
+
+pub struct Scene {
+ objs: Vec<Object>,
+}
+
+pub struct SceneIntersect<'a> {
+ pub mat: &'a dyn Material,
+ pub i: Intersection,
+}
+
+impl Scene {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn add_object(&mut self, obj: Object) {
+ self.objs.push(obj);
+ }
+
+ pub fn add_objects(&mut self, objs: Vec<Object>) {
+ for obj in objs {
+ self.add_object(obj);
+ }
+ }
+
+ pub fn intersect(&self, ray: &Ray) -> Option<SceneIntersect> {
+ let mut min: Option<SceneIntersect> = None;
+
+ for obj in self.objs.iter() {
+ if let Some(i) = obj.shape.intersect(&ray) {
+ match min {
+ Some(ref si) if si.i.t < i.t => (),
+ _ => min = Some(SceneIntersect {i, mat: obj.mat.as_ref() }),
+ }
+ }
+ }
+
+ min
+ }
+}
+
+impl Default for Scene {
+ fn default() -> Self {
+ Self {
+ objs: Vec::new(),
+ }
+ }
+}
diff --git a/src/world/shapes/mod.rs b/src/world/shapes/mod.rs
new file mode 100644
index 0000000..d7583ad
--- /dev/null
+++ b/src/world/shapes/mod.rs
@@ -0,0 +1,4 @@
+mod sphere;
+
+pub use sphere::Sphere;
+
diff --git a/src/world/shapes/sphere.rs b/src/world/shapes/sphere.rs
new file mode 100644
index 0000000..8e0816b
--- /dev/null
+++ b/src/world/shapes/sphere.rs
@@ -0,0 +1,70 @@
+//! Implements sphere
+//!
+//! Spheres are relatively easy to calculate intersections between
+use crate::Float;
+use crate::core::{Ray, Vector3f, Hittable, Intersection};
+
+pub struct Sphere {
+ radius: Float,
+ center: Vector3f,
+}
+
+impl Sphere {
+ pub fn new(radius: Float, center: Vector3f) -> Sphere {
+ Sphere {
+ radius,
+ center,
+ }
+ }
+
+ fn norm_at(&self, point: &Vector3f) -> Vector3f {
+ let mut v = *point - self.center;
+ v /= self.radius;
+ v
+ }
+}
+
+impl Hittable for Sphere {
+ // Implementation from ray tracing in a weekend
+ fn intersect(&self, ray: &Ray) -> Option<Intersection> {
+ let oc = ray.origin - self.center;
+ let a = ray.direction.len_squared();
+ let half_b = oc.dot(&ray.direction);
+ let c = oc.len_squared() - self.radius * self.radius;
+ let disc = half_b*half_b - a*c;
+
+ if disc < 0.0 {
+ None
+ } else {
+ let distance = (-half_b - disc.sqrt()) / a;
+ if distance < 0.0 {
+ return None
+ }
+ let w = ray.at(distance);
+ Some(Intersection {
+ n: self.norm_at(&w),
+ p: w,
+ t: distance,
+ })
+ }
+
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn sphere_intersect() {
+ let sph = Sphere::new(2.0, Vector3f::new_xyz(2.0, 3.0, 4.0));
+
+ let ray = Ray {
+ origin: Vector3f::new_xyz(1.0, 0.0, 0.0),
+ direction: Vector3f::new_xyz(0.0, 1.0, 1.5).norm(),
+ };
+
+ let dist = sph.intersect(&ray).unwrap();
+ assert!((dist.t - 3.28).abs() < 0.01);
+ }
+}