From da1c3949a449f3fafe579c62ff6b14ffd993a197 Mon Sep 17 00:00:00 2001 From: Julian T Date: Sun, 21 Feb 2021 18:01:56 +0100 Subject: Add 3d bounding box and merged SceneIntersection and Intersection --- src/core/bound.rs | 140 --------------------------------------------------- src/core/bound2.rs | 127 ++++++++++++++++++++++++++++++++++++++++++++++ src/core/bound3.rs | 69 +++++++++++++++++++++++++ src/core/hittable.rs | 25 --------- src/core/mod.rs | 31 +++++++++--- src/core/vector3.rs | 16 +++++- 6 files changed, 235 insertions(+), 173 deletions(-) delete mode 100644 src/core/bound.rs create mode 100644 src/core/bound2.rs create mode 100644 src/core/bound3.rs delete mode 100644 src/core/hittable.rs (limited to 'src/core') diff --git a/src/core/bound.rs b/src/core/bound.rs deleted file mode 100644 index 37918d4..0000000 --- a/src/core/bound.rs +++ /dev/null @@ -1,140 +0,0 @@ -//! Implements a 2d region -use crate::{Number, Float}; -use super::vector2::Vector2; -use crate::core; - -/// Implements a region between min and max -#[derive(Clone)] -pub struct Bound2 { - pub min: Vector2, - pub max: Vector2 -} - -pub type Bound2i = Bound2; -pub type Bound2f = Bound2; - -fn min (a: T, b: T) -> T { - if b < a { - return b; - } - a -} - -fn max (a: T, b: T) -> T { - if b > a { - return b; - } - a -} - -impl Bound2 { - /// Creates a new bound from two points - /// - /// p0 does not have to be smaller than p1 - pub fn new(p0: &Vector2, p1: &Vector2) -> Self { - let min = Vector2::new_xy(min(p0.x, p1.x), min(p0.y, p1.y)); - let max = Vector2::new_xy(max(p0.x, p1.x), max(p0.y, p1.y)); - - Self { min, max } - } - - pub fn new_xyxy(x1: T, y1: T, x2: T, y2: T) -> Self { - Self::new( - &Vector2::new_xy(x1, y1), - &Vector2::new_xy(x2, y2), - ) - } - - /// Finds the intersected area between two bounds - pub fn intersect(&self, b: &Bound2) -> Bound2 { - Bound2::new( - &Vector2::new_xy(max(self.min.x, b.min.x), max(self.min.y, b.min.y)), - &Vector2::new_xy(min(self.max.x, b.max.x), min(self.max.y, b.max.y)), - ) - } - - - /// Calculates the diagonal vector - /// - /// Can be used to calculate the size of the bound - /// - /// # Examples - /// - /// ``` - /// use rendering::core::Bound2i; - /// let b = Bound2i::new_xyxy(2, 2, 6, 7); - /// let diag = b.diagonal(); - /// - /// assert!(diag.x == 4 && diag.y == 5); - /// ``` - pub fn diagonal(&self) -> Vector2 { - self.max - self.min - } - - /// Calculates the area of of the bounded region - /// - /// # Examples - /// - /// ``` - /// use rendering::core::Bound2i; - /// let b = Bound2i::new_xyxy(10, 10, 20, 20); - /// - /// assert!(b.area() == 100); - /// ``` - pub fn area(&self) -> T { - let diag = self.diagonal(); - diag.x * diag.y - } -} - -impl From<&Bound2i> for Bound2f { - fn from(b: &Bound2i) -> Self { - Self { - min: core::Vector2f::from(b.min), - max: core::Vector2f::from(b.max), - } - } -} - -impl From<&Bound2f> for Bound2i { - fn from(b: &Bound2f) -> Self { - Self { - min: core::Vector2i::from(b.min), - max: core::Vector2i::from(b.max), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn create_test() -> Bound2 { - Bound2::new( - &Vector2::new_xy(1, 2), - &Vector2::new_xy(10, 3) - ) - } - - #[test] - fn area() { - let b = create_test(); - - assert!(b.area() == 9); - } - - #[test] - fn intersect_test() { - let b1 = Bound2i::new_xyxy(10, 10, 20, 20); - let b2 = Bound2i::new_xyxy(2, 11, 22, 17); - - let b = b1.intersect(&b2); - - assert!( - b.min.x == 10 && - b.min.y == 11 && - b.max.x == 20 && - b.max.y == 17 - ) - } -} diff --git a/src/core/bound2.rs b/src/core/bound2.rs new file mode 100644 index 0000000..bc0e7cd --- /dev/null +++ b/src/core/bound2.rs @@ -0,0 +1,127 @@ +//! Implements a 2d axis aligned bounding box +use crate::{Number, Float}; +use super::vector2::Vector2; +use crate::core; +use crate::core::{min, max}; + +/// Implements a region between min and max +#[derive(Clone)] +pub struct Bound2 { + pub min: Vector2, + pub max: Vector2 +} + +pub type Bound2i = Bound2; +pub type Bound2f = Bound2; + +impl Bound2 { + /// Creates a new bound from two points + /// + /// p0 does not have to be smaller than p1 + pub fn new(p0: &Vector2, p1: &Vector2) -> Self { + let min = Vector2::new_xy(min(p0.x, p1.x), min(p0.y, p1.y)); + let max = Vector2::new_xy(max(p0.x, p1.x), max(p0.y, p1.y)); + + Self { min, max } + } + + pub fn new_xyxy(x1: T, y1: T, x2: T, y2: T) -> Self { + Self::new( + &Vector2::new_xy(x1, y1), + &Vector2::new_xy(x2, y2), + ) + } + + /// Finds the intersected area between two bounds + pub fn intersect(&self, b: &Bound2) -> Bound2 { + Bound2::new( + &Vector2::new_xy(max(self.min.x, b.min.x), max(self.min.y, b.min.y)), + &Vector2::new_xy(min(self.max.x, b.max.x), min(self.max.y, b.max.y)), + ) + } + + + /// Calculates the diagonal vector + /// + /// Can be used to calculate the size of the bound + /// + /// # Examples + /// + /// ``` + /// use rendering::core::Bound2i; + /// let b = Bound2i::new_xyxy(2, 2, 6, 7); + /// let diag = b.diagonal(); + /// + /// assert!(diag.x == 4 && diag.y == 5); + /// ``` + pub fn diagonal(&self) -> Vector2 { + self.max - self.min + } + + /// Calculates the area of of the bounded region + /// + /// # Examples + /// + /// ``` + /// use rendering::core::Bound2i; + /// let b = Bound2i::new_xyxy(10, 10, 20, 20); + /// + /// assert!(b.area() == 100); + /// ``` + pub fn area(&self) -> T { + let diag = self.diagonal(); + diag.x * diag.y + } +} + +impl From<&Bound2i> for Bound2f { + fn from(b: &Bound2i) -> Self { + Self { + min: core::Vector2f::from(b.min), + max: core::Vector2f::from(b.max), + } + } +} + +impl From<&Bound2f> for Bound2i { + fn from(b: &Bound2f) -> Self { + Self { + min: core::Vector2i::from(b.min), + max: core::Vector2i::from(b.max), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn create_test() -> Bound2 { + Bound2::new( + &Vector2::new_xy(1, 2), + &Vector2::new_xy(10, 3) + ) + } + + #[test] + fn area() { + let b = create_test(); + + assert!(b.area() == 9); + } + + #[test] + fn intersect_test() { + let b1 = Bound2i::new_xyxy(10, 10, 20, 20); + let b2 = Bound2i::new_xyxy(2, 11, 22, 17); + + let b = b1.intersect(&b2); + + assert!( + b.min.x == 10 && + b.min.y == 11 && + b.max.x == 20 && + b.max.y == 17 + ) + } +} diff --git a/src/core/bound3.rs b/src/core/bound3.rs new file mode 100644 index 0000000..7f54e03 --- /dev/null +++ b/src/core/bound3.rs @@ -0,0 +1,69 @@ +//! Implements 3d axis aligned bounding box +use crate::{Number, Float}; +use super::vector3::Vector3; +use crate::core::{min, max}; +use crate::core::Ray; + +#[derive(Clone)] +pub struct Bound3 { + pub min: Vector3, + pub max: Vector3 +} + +pub type Bound3f = Bound3; + +impl Bound3 { + /// Creates a bound from two points + pub fn new(p0: Vector3, p1: Vector3) -> Self { + // Elliminate some code duplication here + let min = Vector3::new_xyz( + min(p0.x, p1.x), + min(p0.y, p1.y), + min(p0.z, p1.z) + ); + let max = Vector3::new_xyz( + max(p0.x, p1.x), + max(p0.y, p1.y), + max(p0.z, p1.z) + ); + + Self {min, max} + } +} + +impl Bound3f { + /// Calculate whether there is a intersect between a bounding box and a ray + /// + /// # Examples: + /// + /// ``` + /// use rendering::core::{Bound3f, Vector3f, Ray}; + /// let b = Bound3f::new(Vector3f::new(7.0), Vector3f::new(10.0)); + /// let r1 = Ray::new_to(Vector3f::new(0.0), Vector3f::new(5.0)); + /// let r3 = Ray::new(Vector3f::new_xyz(-1.0, 0.0, 0.0), Vector3f::new_xyz(1.0, 0.0, 0.0)); + /// + /// assert!(b.intersect(&r1)); + /// assert!(!b.intersect(&r3)); + /// ``` + pub fn intersect(&self, ray: &Ray) -> bool { + // Method stolen from Ray tracing the next week. + // They mention its from pixar + for i in 0..3 { + let inv = 1.0 / ray.direction[i]; + let mut t0 = (self.min[i] - ray.origin[i]) * inv; + let mut t1 = (self.max[i] - ray.origin[i]) * inv; + + if inv < 0.0 { + let tmp = t0; + t0 = t1; + t1 = tmp; + } + + if t1 <= t0 { + return false; + } + } + + return true; + } +} diff --git a/src/core/hittable.rs b/src/core/hittable.rs deleted file mode 100644 index e495d5b..0000000 --- a/src/core/hittable.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::core::{Vector3f, Ray}; -use crate::Float; - -/// Returns the context of a intersection -pub struct Intersection { - /// Normal vector at intersection - pub n: Vector3f, - pub p: Vector3f, - pub t: Float, -} - -impl Intersection { - pub fn norm_against_ray(&self, r: &Ray) -> Vector3f { - if self.n.dot(&r.direction) < 0.0 { - self.n - } else { - -self.n - } - } -} - -/// Defines a common trait for objects in the scene -pub trait Hittable { - fn intersect(&self, ray: &Ray) -> Option; -} diff --git a/src/core/mod.rs b/src/core/mod.rs index c2b3771..07793ec 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -2,16 +2,33 @@ //! //! Also creates a shortcut for some common types -pub mod vector2; -pub mod vector3; -pub mod bound; -pub mod spectrum; -pub mod hittable; +mod vector2; +mod vector3; +mod bound2; +mod bound3; +mod spectrum; mod ray; pub use vector2::{Vector2i, Vector2f}; pub use vector3::Vector3f; -pub use bound::{Bound2i, Bound2f}; +pub use bound2::{Bound2i, Bound2f}; +pub use bound3::Bound3f; pub use spectrum::Spectrum; pub use ray::Ray; -pub use hittable::{Hittable, Intersection}; + +use crate::Number; + +fn min (a: T, b: T) -> T { + if b < a { + return b; + } + a +} + +fn max (a: T, b: T) -> T { + if b > a { + return b; + } + a +} + diff --git a/src/core/vector3.rs b/src/core/vector3.rs index e26c07c..1cc6f60 100644 --- a/src/core/vector3.rs +++ b/src/core/vector3.rs @@ -2,7 +2,7 @@ //! //! Also add more 3d math things needed for shading and 3d calculations. use crate::{Float, Number, NEAR_ZERO}; -use std::ops::{Mul, Sub, Add, DivAssign, Neg, AddAssign}; +use std::ops::{Mul, Sub, Add, DivAssign, Neg, AddAssign, Index}; use std::fmt; #[derive(Clone, Copy)] @@ -107,6 +107,20 @@ impl fmt::Display for Vector3 { } } +// Ohh god +impl Index for Vector3 { + type Output = T; + + fn index(&self, i: u32) -> &Self::Output { + match i { + 0 => &self.x, + 1 => &self.y, + 2 => &self.z, + _ => panic!("index out of bounds: index {} is not possible with 3d vector", i) + } + } +} + impl Vector3f { pub const ZERO: Self = Vector3f {x: 0.0, y: 0.0, z: 0.0}; -- cgit v1.2.3