diff --git a/README.md b/README.md index 2a660ce..227dca4 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ This list may be changed or extended in the future. - [x] Plane intersection test - [x] Color mapping on planes - [ ] Triangle objects - - [ ] Triangle struct + - [x] Triangle struct - [ ] Triangle intersection test - [ ] Triangle normal generation - [ ] Color mapping on triangles diff --git a/src/object.rs b/src/object.rs index 42bf6e2..e45ef28 100644 --- a/src/object.rs +++ b/src/object.rs @@ -7,11 +7,20 @@ use na::*; use crate::types::*; +// A trait for types that can be in Objects. pub trait Surface { + + // Takes in a ray and performs an intersection test + // on itself. If the ray intersects the object, + // returns the distance to the intersection point. fn intersect(&self, ray: Ray) -> Option; + // Takes in a point (assumed to be on the object's surface) + // and returns the normal vector off of that point. fn normal(&self, point: Point3) -> Unit>; + // Takes in a point (assumed to be on the object's surface) + // and returns the color information on that point. fn getcolor(&self, point: Point3) -> Color; } diff --git a/src/object/plane.rs b/src/object/plane.rs index c6fdffc..47712e3 100644 --- a/src/object/plane.rs +++ b/src/object/plane.rs @@ -7,15 +7,18 @@ use crate::types::*; use super::Surface; pub struct Plane { - pub center: Point3, - pub normal: Unit>, + pub center: Point3, // Plane origin (used for texture mapping). + pub normal: Unit>, // Precomputed plane normal. - x_axis: Vector3, - y_axis: Vector3, - texture: Box Color> + x_axis: Vector3, // Plane x-axis (The 3D direction that corresponds to the x-direction on the plane). + y_axis: Vector3, // Plane y-axis (The 3D direction that corresponds to the y-direction on the plane). + + texture: Box Color> // Texture map. + // Input coordinates are defined in terms of the axes above. } impl Plane { + // Creates a new plane. pub fn new(center: Point3, x_axis: Vector3, y_axis: Vector3, texture: F) -> Self where F: Fn(f32, f32) -> Color { @@ -28,12 +31,33 @@ impl Plane { } } + // Creates a new plane with the normal flipped. + pub fn new_flip(center: Point3, x_axis: Vector3, y_axis: Vector3, texture: F) -> Self + where F: Fn(f32, f32) -> Color + { + Plane { + center: center, + normal: Unit::new_normalize(y_axis.cross(&x_axis)), + x_axis: x_axis, + y_axis: y_axis, + texture: Box::new(texture) + } + } + + // Creates a new plane of a solid color. pub fn new_solid(center: Point3, x_axis: Vector3, y_axis: Vector3, color: Color) -> Self { Plane::new(center, x_axis, y_axis, move |_, _| color) } + // Creates a new flipped plane of a solid color. + pub fn new_solid_flip(center: Point3, x_axis: Vector3, y_axis: Vector3, color: Color) -> Self + { Plane::new_flip(center, x_axis, y_axis, move |_, _| color) } + + + // Creates a new XY-plane with the given texture map. pub fn xy Color>(texture: F) -> Self { Plane::new(Point3::origin(), Vector3::x(), Vector3::y(), texture) } + // Creates a new XZ-plane with the given texture map. pub fn xz Color>(texture: F) -> Self { Plane::new(Point3::origin(), Vector3::x(), Vector3::z(), texture) } } @@ -42,7 +66,7 @@ impl Surface for Plane { fn intersect(&self, ray: Ray) -> Option { let d = self.normal.dot(&ray.direction); - if d < 1e-6 { return None; } + if d < 1e-5 { return None; } let t = (self.center - ray.origin).dot(&*self.normal) / d; diff --git a/src/object/sphere.rs b/src/object/sphere.rs index fa04633..91383d5 100644 --- a/src/object/sphere.rs +++ b/src/object/sphere.rs @@ -9,13 +9,15 @@ use crate::types::*; use super::Surface; pub struct Sphere { - pub center: Point3, - pub radius: f32, + pub center: Point3, // Center point of the sphere. + pub radius: f32, // Radius of the sphere. - texture: Box Color> + texture: Box Color> // Texture map. + // Uses spherical coordinates (normalized from 0-1) as input. } impl Sphere { + // Creates a new sphere. pub fn new(x: f32, y: f32, z: f32, radius: f32, texture: F) -> Self where F: Fn(f32, f32) -> Color { @@ -26,6 +28,7 @@ impl Sphere { } } + // Creates a new sphere of a solid color. pub fn new_solid(x: f32, y: f32, z: f32, radius: f32, color: Color) -> Self { Sphere::new(x, y, z, radius, move |_, _| color) } } @@ -69,8 +72,6 @@ impl Surface for Sphere { // In this particular case, the normal is simular to a point on a unit sphere // centred around the origin. We can thus use the normal coordinates to compute // the spherical coordinates of the point. - // atan2 returns a value in the range [-pi, pi] and we need to remap it to range [0, 1] - // acosf returns a value in the range [0, pi] and we also need to remap it to the range [0, 1] let x = 0.5 + normal.z.atan2(normal.x) / (2.0 * PI); let y = normal.y.acos() / PI; diff --git a/src/object/triangle.rs b/src/object/triangle.rs index e69de29..14d54c8 100644 --- a/src/object/triangle.rs +++ b/src/object/triangle.rs @@ -0,0 +1,74 @@ +extern crate nalgebra as na; + +use na::*; +use na::geometry::Point3; + +use crate::types::*; +use super::Surface; + +pub struct Triangle<'a> { + pub vertex1: &'a Point3, // References to 3 vertices. + pub vertex2: &'a Point3, + pub vertex3: &'a Point3, + + area: f32, // Precalculated area for barycentric calculations. + + texture: Box Color> // Texture map. + // Uses barycentric coordinates as input. +} + +pub struct TriangleMesh<'a> { + pub points: Vec>>, + pub tris: Vec> +} + +fn tri_area(a: &Point3, b: &Point3, c: &Point3) -> f32 { + let prlg_area: f32 = (b - a).cross(&(c - a)).norm(); + prlg_area / 2.0 +} + +impl<'a> Triangle<'a> { + pub fn new(vertex1: &'a Point3, + vertex2: &'a Point3, + vertex3: &'a Point3, + texture: F) -> Self + where F: Fn(f32, f32, f32) -> Color + { + Triangle { + vertex1: vertex1, + vertex2: vertex2, + vertex3: vertex3, + area: tri_area(vertex1, vertex2, vertex3), + texture: Box::new(texture) + } + } + + pub fn new_solid(vertex1: &'a Point3, + vertex2: &'a Point3, + vertex3: &'a Point3, color: Color) -> Self + { Triangle::new(vertex1, vertex2, vertex3, move |_, _, _| color) } + + // Conversion of barycentric coordinates to + // a point on the triangle. + pub fn from_bary(&self, t: f32, u: f32, v: f32) -> Point3 { + Point::from(t * self.vertex1.coords + u * self.vertex2.coords + v * self.vertex3.coords) + } + + // Conversion of a point to barycentric coordinates. + pub fn to_bary(&self, point: Point3) -> (f32, f32, f32) { + let t = tri_area(self.vertex2, self.vertex3, &point) / self.area; + let u = tri_area(self.vertex1, self.vertex3, &point) / self.area; + let v = tri_area(self.vertex1, self.vertex2, &point) / self.area; + + (t, u, v) + } +} + +impl<'a> TriangleMesh<'a> { + pub fn new(points: Vec>>, tris: Vec>) -> Self { + TriangleMesh { + points: points, + tris: tris + } + } +}