From 736c1e416cc1691eaac510a3fdcedc71f241176d Mon Sep 17 00:00:00 2001 From: bijan2005 <54818348+bijan2005@users.noreply.github.com> Date: Thu, 25 Mar 2021 21:05:13 -0400 Subject: [PATCH 01/10] Add MIT License --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d31d790 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 bijan2005 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 6d2a1407bf73565c648148b58ea46bc12ecd7826 Mon Sep 17 00:00:00 2001 From: bijan2005 <54818348+bijan2005@users.noreply.github.com> Date: Thu, 25 Mar 2021 21:11:46 -0400 Subject: [PATCH 02/10] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index d31d790..e9c3962 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 bijan2005 +Copyright (c) 2021 Bijan Sheibani Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From cd94c7ad6fa28203f8da308d585fa7b5c974cfe8 Mon Sep 17 00:00:00 2001 From: bijan-S Date: Wed, 5 May 2021 21:48:22 -0400 Subject: [PATCH 03/10] Fixed bug in plane intersection test --- src/object/plane.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/object/plane.rs b/src/object/plane.rs index 981fb5f..4d2fc83 100644 --- a/src/object/plane.rs +++ b/src/object/plane.rs @@ -67,7 +67,7 @@ impl Surface for Plane { fn intersect(&self, ray: Ray) -> Option { let d = self.normal.dot(&ray.direction); - if d < 1e-3 { return None; } + if d > -1e-3 { return None; } let t = (self.center - ray.origin).dot(&*self.normal) / d; From e2dd9f7e3a0f69db1293008fa099d1c48c871f26 Mon Sep 17 00:00:00 2001 From: bijan-S Date: Wed, 28 Jul 2021 22:10:01 -0400 Subject: [PATCH 04/10] Fixed bounding sphere generation --- Cargo.lock | 24 +++++++++---------- src/object/triangle.rs | 54 +++++++++++++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index add5afc..38aaf58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cfg-if" -version = "0.1.10" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cloudabi" @@ -61,18 +61,18 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "generic-array" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" dependencies = [ "typenum", ] [[package]] name = "getrandom" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if", "libc", @@ -81,9 +81,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.80" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" +checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" [[package]] name = "libm" @@ -93,9 +93,9 @@ checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" [[package]] name = "matrixmultiply" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4f7ec66360130972f34830bfad9ef05c6610a43938a467bcc9ab9369ab3478f" +checksum = "916806ba0031cd542105d916a97c8572e1fa6dd79c9c51e7eb43a09ec2dd84c1" dependencies = [ "rawpointer", ] @@ -336,9 +336,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" [[package]] name = "wasi" diff --git a/src/object/triangle.rs b/src/object/triangle.rs index c66f755..c09624f 100644 --- a/src/object/triangle.rs +++ b/src/object/triangle.rs @@ -160,6 +160,30 @@ impl Surface for TriangleMesh { // Uses Welzl's algorithm to solve the bounding sphere problem fn bound(&self) -> Bound { + fn smallest_sphere_plane(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f32) { + if points.len() == 0 || boundary.len() == 3 { + match boundary.len() { + 0 => (Point3::new(0.0, 0.0, 0.0), 0.0), + 1 => (*boundary[0], 0.0), + 2 => { let half_span = 0.5 * (boundary[1] - boundary[0]); + (*boundary[0] + half_span, half_span.norm()) }, + 3 => triangle_sphere(boundary[0], boundary[1], boundary[2]), + _ => unreachable!() + } + } else { + let removed = points[0]; + let points = Vec::from(&points[1..]); + + let bound = smallest_sphere(points.clone(), boundary.clone()); + if distance(&bound.0, removed) < bound.1 { return bound; } + + let mut boundary = boundary.clone(); + boundary.push(removed); + + smallest_sphere_plane(points, boundary) + } + } + fn triangle_sphere(point1: &Point3f, point2: &Point3f, point3: &Point3f) -> (Point3f, f32) { let a = point3 - point1; let b = point2 - point1; @@ -181,22 +205,30 @@ impl Surface for TriangleMesh { point4.to_homogeneous().transpose()]); let a = matrix.determinant() * 2.0; - let mut matrix_mut = matrix.clone(); - let squares = Vector4::new(point1.coords.norm_squared(), point2.coords.norm_squared(), point3.coords.norm_squared(), point4.coords.norm_squared()); - matrix_mut.set_column(0, &squares); - let center_x = matrix_mut.determinant(); + if (a != 0.0) { + let mut matrix_mut = matrix.clone(); - matrix_mut.set_column(1, &matrix.index((.., 0))); - let center_y = -matrix_mut.determinant(); + let squares = Vector4::new(point1.coords.norm_squared(), point2.coords.norm_squared(), point3.coords.norm_squared(), point4.coords.norm_squared()); + matrix_mut.set_column(0, &squares); + let center_x = matrix_mut.determinant(); - matrix_mut.set_column(2, &matrix.index((.., 1))); - let center_z = matrix_mut.determinant(); + matrix_mut.set_column(1, &matrix.index((.., 0))); + let center_y = -matrix_mut.determinant(); - let center = Point3::new(center_x / a, center_y / a, center_z / a); - let radius = distance(point1, ¢er); + matrix_mut.set_column(2, &matrix.index((.., 1))); + let center_z = matrix_mut.determinant(); - (center, radius) + let center = Point3::new(center_x / a, center_y / a, center_z / a); + let radius = distance(point1, ¢er); + + (center, radius) + } else { + let points = vec![point1, point2, point3, point4]; + let boundary = Vec::new(); + + smallest_sphere_plane(points, boundary) + } } fn smallest_sphere(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f32) { From 4e42390a8212f8cce4e58e0c131122fffa680272 Mon Sep 17 00:00:00 2001 From: Kiana Sheibani Date: Mon, 14 Oct 2024 17:54:36 -0400 Subject: [PATCH 05/10] docs: update author info --- Cargo.toml | 4 +--- LICENSE | 2 +- README.md | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ed2abd2..2967baf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,9 @@ [package] name = "render" version = "0.1.0" -authors = ["Bijan Sheibani"] +authors = ["Kiana Sheibani"] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] nalgebra = "0.18" rand = "0.7.3" diff --git a/LICENSE b/LICENSE index e9c3962..712386e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Bijan Sheibani +Copyright (c) 2024 Kiana Sheibani Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 9e985ae..b4409c4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # rust-render + A raytracing 3D renderer written in Rust. ### Checklist of features From 9879184d47c99a22007f1420cbe97cfa6e15045d Mon Sep 17 00:00:00 2001 From: Kiana Sheibani Date: Mon, 14 Oct 2024 17:55:47 -0400 Subject: [PATCH 06/10] feat: add nix build infrastructure --- .envrc | 3 + .gitignore | 3 +- flake.lock | 285 ++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 20 ++++ module.nix | 18 +++ rust-toolchain.toml | 2 + 6 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 module.nix create mode 100644 rust-toolchain.toml diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..b9238c3 --- /dev/null +++ b/.envrc @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +use flake diff --git a/.gitignore b/.gitignore index 914880f..4729dc0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ -# Generated by Cargo -# will have compiled files and executables debug/ target/ +.direnv/ # These are backup files generated by rustfmt **/*.rs.bk diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..cfec747 --- /dev/null +++ b/flake.lock @@ -0,0 +1,285 @@ +{ + "nodes": { + "crane": { + "flake": false, + "locked": { + "lastModified": 1727316705, + "narHash": "sha256-/mumx8AQ5xFuCJqxCIOFCHTVlxHkMT21idpbgbm/TIE=", + "owner": "ipetkov", + "repo": "crane", + "rev": "5b03654ce046b5167e7b0bccbd8244cb56c16f0e", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "ref": "v0.19.0", + "repo": "crane", + "type": "github" + } + }, + "dream2nix": { + "inputs": { + "nixpkgs": [ + "nci", + "nixpkgs" + ], + "purescript-overlay": "purescript-overlay", + "pyproject-nix": "pyproject-nix" + }, + "locked": { + "lastModified": 1728585693, + "narHash": "sha256-rhx5SYpIkPu7d+rjF9FGGBVxS0BwAEkmYIsJg2a3E20=", + "owner": "nix-community", + "repo": "dream2nix", + "rev": "c6935471f7e1a9e190aaa9ac9823dca34e00d92a", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "dream2nix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1727826117, + "narHash": "sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "3d04084d54bedc3d6b8b736c70ef449225c361b1", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "mk-naked-shell": { + "flake": false, + "locked": { + "lastModified": 1681286841, + "narHash": "sha256-3XlJrwlR0nBiREnuogoa5i1b4+w/XPe0z8bbrJASw0g=", + "owner": "yusdacra", + "repo": "mk-naked-shell", + "rev": "7612f828dd6f22b7fb332cc69440e839d7ffe6bd", + "type": "github" + }, + "original": { + "owner": "yusdacra", + "repo": "mk-naked-shell", + "type": "github" + } + }, + "nci": { + "inputs": { + "crane": "crane", + "dream2nix": "dream2nix", + "mk-naked-shell": "mk-naked-shell", + "nixpkgs": [ + "nixpkgs" + ], + "parts": "parts", + "rust-overlay": "rust-overlay", + "treefmt": "treefmt" + }, + "locked": { + "lastModified": 1728886586, + "narHash": "sha256-pg8O8Vborbo0dcNuE4K5PCVW6MXSJ7LEv9toAnSJ7nw=", + "owner": "yusdacra", + "repo": "nix-cargo-integration", + "rev": "b80fe61d4a7c7a7dd9f3d32c3e19aef2baae0bfa", + "type": "github" + }, + "original": { + "owner": "yusdacra", + "repo": "nix-cargo-integration", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1728538411, + "narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "parts": { + "inputs": { + "nixpkgs-lib": [ + "nci", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1727826117, + "narHash": "sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "3d04084d54bedc3d6b8b736c70ef449225c361b1", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "purescript-overlay": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "nci", + "dream2nix", + "nixpkgs" + ], + "slimlock": "slimlock" + }, + "locked": { + "lastModified": 1724504251, + "narHash": "sha256-TIw+sac0NX0FeAneud+sQZT+ql1G/WEb7/Vb436rUXM=", + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "rev": "988b09676c2a0e6a46dfa3589aa6763c90476b8a", + "type": "github" + }, + "original": { + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "type": "github" + } + }, + "pyproject-nix": { + "flake": false, + "locked": { + "lastModified": 1702448246, + "narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=", + "owner": "davhau", + "repo": "pyproject.nix", + "rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb", + "type": "github" + }, + "original": { + "owner": "davhau", + "ref": "dream2nix", + "repo": "pyproject.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nci": "nci", + "nixpkgs": "nixpkgs", + "systems": "systems" + } + }, + "rust-overlay": { + "flake": false, + "locked": { + "lastModified": 1728873041, + "narHash": "sha256-e4jz7yFADiZjMhv+iQwYtAN8AOUlOpbNQYnbwUFLjeM=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "bdbe1611c2029de90bca372ce0b1e3b4fa65f55a", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "slimlock": { + "inputs": { + "nixpkgs": [ + "nci", + "dream2nix", + "purescript-overlay", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688756706, + "narHash": "sha256-xzkkMv3neJJJ89zo3o2ojp7nFeaZc2G0fYwNXNJRFlo=", + "owner": "thomashoneyman", + "repo": "slimlock", + "rev": "cf72723f59e2340d24881fd7bf61cb113b4c407c", + "type": "github" + }, + "original": { + "owner": "thomashoneyman", + "repo": "slimlock", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt": { + "inputs": { + "nixpkgs": [ + "nci", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1727984844, + "narHash": "sha256-xpRqITAoD8rHlXQafYZOLvUXCF6cnZkPfoq67ThN0Hc=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "4446c7a6fc0775df028c5a3f6727945ba8400e64", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..bd7d11a --- /dev/null +++ b/flake.nix @@ -0,0 +1,20 @@ +{ + description = "Raytracing renderer written in Rust"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + systems.url = "github:nix-systems/default"; + + flake-parts.url = "github:hercules-ci/flake-parts"; + flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; + + nci.url = "github:yusdacra/nix-cargo-integration"; + nci.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = inputs@{ flake-parts, systems, nci, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + systems = import systems; + imports = [ nci.flakeModule ./module.nix ]; + }; +} diff --git a/module.nix b/module.nix new file mode 100644 index 0000000..3aea4eb --- /dev/null +++ b/module.nix @@ -0,0 +1,18 @@ +{ + perSystem = { pkgs, config, self', ... }: + let + cfg = config.nci.outputs.render; + in { + nci.toolchainConfig = ./rust-toolchain.toml; + + nci.projects.render.path = ./.; + + # Exports + checks.build = self'.packages.render; + packages.default = self'.packages.render; + packages.render = cfg.packages.release; + devShells.default = cfg.devShell.overrideAttrs (prev: { + buildInputs = prev.buildInputs ++ [ pkgs.rust-analyzer ]; + }); + }; +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..c9025df --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly-2024-10-01" From 763a4ff9238d6e542f46bb45162c572119b5aa45 Mon Sep 17 00:00:00 2001 From: Kiana Sheibani Date: Mon, 14 Oct 2024 18:06:50 -0400 Subject: [PATCH 07/10] refactor: use 64-bit types instead of 32-bit I don't know why I wasn't using 64 bit floats from the beginning, honestly. I had weird priorities back then --- src/camera.rs | 136 +++++++++++++++-------- src/object.rs | 42 ++++--- src/object/bound.rs | 16 ++- src/object/plane.rs | 82 +++++++++----- src/object/point_light.rs | 35 ++++-- src/object/sphere.rs | 64 +++++++---- src/object/triangle.rs | 227 +++++++++++++++++++++++++------------- src/render.rs | 28 +++-- src/types.rs | 82 ++++++++------ 9 files changed, 469 insertions(+), 243 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index c269cc0..88eae1c 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,87 +1,123 @@ extern crate nalgebra as na; -use na::*; use na::geometry::{Point2, Point3}; +use na::*; use crate::types::Ray; #[derive(Debug)] pub struct Camera { - matrix: Isometry3, // The transformation that stores the - // position and orientation of the camera. (Not actually a matrix, but w/e) + matrix: Isometry3, // The transformation that stores the + // position and orientation of the camera. (Not actually a matrix, but w/e) + focal_length: f64, // The distance from the camera origin to the canvas. + canvas_size: Vector2, // The size of the canvas within the world space. - focal_length: f32, // The distance from the camera origin to the canvas. - canvas_size: Vector2, // The size of the canvas within the world space. - - pub image_size: Vector2 // The size of the final image in pixels. + pub image_size: Vector2, // The size of the final image in pixels. } impl Camera { - // Constructs a new camera from a position and viewing direction. - pub fn new_(pos: Point3, dir: Vector3, up: Vector3, - focal_length: f32, aspect_ratio: f32, canvas_y: f32, image_y: u32) -> Self { + pub fn new_( + pos: Point3, + dir: Vector3, + up: Vector3, + focal_length: f64, + aspect_ratio: f64, + canvas_y: f64, + image_y: u64, + ) -> Self { let iso = Isometry3::face_towards(&pos, &(pos + dir), &up); Camera { matrix: iso, focal_length: focal_length, - canvas_size: Vector2::new(canvas_y * aspect_ratio, canvas_y), - image_size: Vector2::new((image_y as f32 * aspect_ratio) as u32, image_y) + canvas_size: Vector2::new(canvas_y * aspect_ratio, canvas_y), + image_size: Vector2::new((image_y as f64 * aspect_ratio) as u64, image_y), } } // Constructs a new camera from a position and viewing direction // (assuming the camera is oriented upright). - pub fn new(pos: Point3, dir: Vector3, - focal_length: f32, aspect_ratio: f32, canvas_y: f32, image_y: u32) -> Self - { Camera::new_(pos, dir, Vector3::y(), focal_length, aspect_ratio, canvas_y, image_y) } + pub fn new( + pos: Point3, + dir: Vector3, + focal_length: f64, + aspect_ratio: f64, + canvas_y: f64, + image_y: u64, + ) -> Self { + Camera::new_( + pos, + dir, + Vector3::y(), + focal_length, + aspect_ratio, + canvas_y, + image_y, + ) + } - pub fn pos(&self) -> Point3 { Point3::from(self.matrix.translation.vector) } + pub fn pos(&self) -> Point3 { + Point3::from(self.matrix.translation.vector) + } // Takes a 2D point in the image space and // maps it to the 3D point on the canvas. - fn project(&self, x: u32, y: u32) -> Point3 { + fn project(&self, x: u64, y: u64) -> Point3 { // convert point from raster coordinates to center-based coordinates - let pixelndc = Point2::new(x as f32 + 0.5 - self.image_size.x as f32 * 0.5, -(y as f32 + 0.5) + self.image_size.y as f32 * 0.5); + let pixelndc = Point2::new( + x as f64 + 0.5 - self.image_size.x as f64 * 0.5, + -(y as f64 + 0.5) + self.image_size.y as f64 * 0.5, + ); - let point: Point3 = Point::from(pixelndc.coords.component_div(&self.image_size.map(|x| x as f32)) - .component_mul(&self.canvas_size) - .fixed_resize(self.focal_length)); + let point: Point3 = Point::from( + pixelndc + .coords + .component_div(&self.image_size.map(|x| x as f64)) + .component_mul(&self.canvas_size) + .fixed_resize(self.focal_length), + ); self.matrix * point } // Takes a 2D point in the image space and // returns a ray in the world space, for use in raytracing. - pub fn raycast(&self, x: u32, y: u32) -> Ray { + pub fn raycast(&self, x: u64, y: u64) -> Ray { Ray::from_points(self.pos(), self.project(x, y)) } } - #[cfg(test)] mod tests { use super::*; - fn round(point: Point3) -> Point3 { + fn round(point: Point3) -> Point3 { Point::from(point.coords.map(|x| x.round())) } #[test] fn camera_pos() { - let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - 1.0, 1.0, - 2.0, 800); + let camera: Camera = Camera::new( + Point3::new(-5.0, 0.0, 0.0), + Vector3::new(1.0, 0.0, 0.0), + 1.0, + 1.0, + 2.0, + 800, + ); assert_eq!(camera.pos(), Point3::new(-5.0, 0.0, 0.0)); } #[test] fn camera_matrix1() { - let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - 1.0, 1.0, - 2.0, 800); + let camera: Camera = Camera::new( + Point3::new(-5.0, 0.0, 0.0), + Vector3::new(1.0, 0.0, 0.0), + 1.0, + 1.0, + 2.0, + 800, + ); let point = Point3::new(0.0, 0.0, 4.0); let point = camera.matrix * point; @@ -91,10 +127,14 @@ mod tests { #[test] fn camera_matrix2() { - let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - 1.0, 1.0, - 2.0, 800); + let camera: Camera = Camera::new( + Point3::new(-5.0, 0.0, 0.0), + Vector3::new(1.0, 0.0, 0.0), + 1.0, + 1.0, + 2.0, + 800, + ); let point = Point3::new(4.0, 0.0, 0.0); let point = camera.matrix * point; @@ -104,10 +144,14 @@ mod tests { #[test] fn camera_project1() { - let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - 1.0, 1.0, - 2.0, 800); + let camera: Camera = Camera::new( + Point3::new(-5.0, 0.0, 0.0), + Vector3::new(1.0, 0.0, 0.0), + 1.0, + 1.0, + 2.0, + 800, + ); let point = camera.project(400, 400); let point = round(point); // round to avoid errors @@ -116,10 +160,14 @@ mod tests { #[test] fn camera_project2() { - let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - 1.0, 1.0, - 2.0, 800); + let camera: Camera = Camera::new( + Point3::new(-5.0, 0.0, 0.0), + Vector3::new(1.0, 0.0, 0.0), + 1.0, + 1.0, + 2.0, + 800, + ); let point = camera.project(0, 0); let point = round(point); // round to avoid errors diff --git a/src/object.rs b/src/object.rs index d7d4d8b..9d7b108 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,19 +1,22 @@ - -mod sphere; pub use sphere::*; -mod plane; pub use plane::*; -mod triangle; pub use triangle::*; -mod bound; pub use bound::*; -mod point_light; pub use point_light::*; +mod sphere; +pub use sphere::*; +mod plane; +pub use plane::*; +mod triangle; +pub use triangle::*; +mod bound; +pub use bound::*; +mod point_light; +pub use point_light::*; 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; + 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. @@ -29,7 +32,7 @@ pub trait Surface { pub struct Object { pub surface: Box, - bound: Bound + bound: Bound, } impl Object { @@ -38,18 +41,23 @@ impl Object { let bound = surface.bound(); Object { surface: Box::new(surface), - bound + bound, } } - - pub fn intersect(&self, ray: Ray) -> Option { + pub fn intersect(&self, ray: Ray) -> Option { if self.bound.is_intersected(ray) { self.surface.intersect(ray) - } else { None } + } else { + None + } + } + pub fn normal(&self, point: Point3f) -> Unit3f { + self.surface.normal(point) + } + pub fn get_texture(&self, point: Point3f) -> Texture { + self.surface.get_texture(point) } - pub fn normal(&self, point: Point3f) -> Unit3f { self.surface.normal(point) } - pub fn get_texture(&self, point: Point3f) -> Texture { self.surface.get_texture(point) } } pub trait Light { @@ -60,7 +68,7 @@ pub trait Light { fn get_color(&self, point: Point3f) -> Color; // Compute intensity on a point. - fn intensity(&self, point: Point3f) -> f32; + fn intensity(&self, point: Point3f) -> f64; // Return the direction from the point to the light source. fn direction(&self, point: Point3f) -> Unit3f; @@ -69,5 +77,5 @@ pub trait Light { pub struct Scene { pub objects: Vec, pub lights: Vec>, - pub background: Color + pub background: Color, } diff --git a/src/object/bound.rs b/src/object/bound.rs index 49c0246..e295003 100644 --- a/src/object/bound.rs +++ b/src/object/bound.rs @@ -10,15 +10,17 @@ use crate::types::*; #[derive(Debug)] pub struct Bound { pub center: Point3f, - pub radius: f32, + pub radius: f64, // If true, then the bounding sphere is disabled. - pub bypass: bool + pub bypass: bool, } impl Bound { pub fn is_intersected(&self, ray: Ray) -> bool { - if self.bypass { return true; } + if self.bypass { + return true; + } let l = ray.origin - self.center; l.norm_squared() >= self.radius * self.radius @@ -26,5 +28,11 @@ impl Bound { // pub fn contains(&self, point: &Point3f) -> bool { distance(&self.center, point) < self.radius } - pub fn bypass() -> Self { Bound { center: Point3::origin(), radius: 0.0, bypass: true } } + pub fn bypass() -> Self { + Bound { + center: Point3::origin(), + radius: 0.0, + bypass: true, + } + } } diff --git a/src/object/plane.rs b/src/object/plane.rs index 4d2fc83..e02ac95 100644 --- a/src/object/plane.rs +++ b/src/object/plane.rs @@ -1,81 +1,107 @@ extern crate nalgebra as na; -use na::*; use na::geometry::Point3; +use na::*; +use super::{bound::*, Surface}; use crate::types::*; -use super::{Surface, bound::*}; pub struct Plane { - pub center: Point3f, // Plane origin (used for texture mapping). - pub normal: Unit3f, // Precomputed plane normal. + pub center: Point3f, // Plane origin (used for texture mapping). + pub normal: Unit3f, // Precomputed plane normal. x_axis: Vector3f, // Plane x-axis (The 3D direction that corresponds to the x-direction on the plane). y_axis: Vector3f, // Plane y-axis (The 3D direction that corresponds to the y-direction on the plane). - texture: Box Texture> // Texture map. - // Input coordinates are defined in terms of the axes above. + texture: Box Texture>, // Texture map. + // Input coordinates are defined in terms of the axes above. } #[allow(dead_code)] impl Plane { // Creates a new plane. pub fn new(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: F) -> Self - where F: Fn(f32, f32) -> Texture + where + F: Fn(f64, f64) -> Texture, { Plane { center, normal: Unit::new_normalize(x_axis.cross(&y_axis)), x_axis: x_axis, y_axis: y_axis, - texture: Box::new(texture) + texture: Box::new(texture), } } // Creates a new plane with the normal flipped. - pub fn new_flip(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: F) -> Self - where F: Fn(f32, f32) -> Texture + pub fn new_flip( + center: Point3f, + x_axis: Vector3f, + y_axis: Vector3f, + texture: F, + ) -> Self + where + F: Fn(f64, f64) -> Texture, { Plane { center: center, normal: Unit::new_normalize(y_axis.cross(&x_axis)), x_axis: x_axis, y_axis: y_axis, - texture: Box::new(texture) + texture: Box::new(texture), } } // Creates a new plane of a solid color. - pub fn new_solid(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: Texture) -> Self - { Plane::new(center, x_axis, y_axis, move |_, _| texture) } + pub fn new_solid( + center: Point3f, + x_axis: Vector3f, + y_axis: Vector3f, + texture: Texture, + ) -> Self { + Plane::new(center, x_axis, y_axis, move |_, _| texture) + } // Creates a new flipped plane of a solid color. - pub fn new_solid_flip(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: Texture) -> Self - { Plane::new_flip(center, x_axis, y_axis, move |_, _| texture) } - + pub fn new_solid_flip( + center: Point3f, + x_axis: Vector3f, + y_axis: Vector3f, + texture: Texture, + ) -> Self { + Plane::new_flip(center, x_axis, y_axis, move |_, _| texture) + } // Creates a new XY-plane with the given texture map. - pub fn xy(texture: impl 'static + Fn(f32, f32) -> Texture) -> Self - { Plane::new(Point3::origin(), Vector3::x(), Vector3::y(), texture) } + pub fn xy(texture: impl 'static + Fn(f64, f64) -> Texture) -> Self { + Plane::new(Point3::origin(), Vector3::x(), Vector3::y(), texture) + } // Creates a new XZ-plane with the given texture map. - pub fn xz(texture: impl 'static + Fn(f32, f32) -> Texture) -> Self - { Plane::new(Point3::origin(), Vector3::x(), Vector3::z(), texture) } + pub fn xz(texture: impl 'static + Fn(f64, f64) -> Texture) -> Self { + Plane::new(Point3::origin(), Vector3::x(), Vector3::z(), texture) + } } impl Surface for Plane { - fn intersect(&self, ray: Ray) -> Option { - + fn intersect(&self, ray: Ray) -> Option { let d = self.normal.dot(&ray.direction); - if d > -1e-3 { return None; } + if d > -1e-3 { + return None; + } let t = (self.center - ray.origin).dot(&*self.normal) / d; - if t >= 0.0 { Some(t) } - else { None } + if t >= 0.0 { + Some(t) + } else { + None + } } - fn normal(&self, _point: Point3f) -> Unit3f { self.normal } + fn normal(&self, _point: Point3f) -> Unit3f { + self.normal + } fn get_texture(&self, point: Point3f) -> Texture { let rel_pos = point - self.center; @@ -89,5 +115,7 @@ impl Surface for Plane { // Planes are infinite, so no finite // bounding sphere could possibly contain one. - fn bound(&self) -> Bound { Bound::bypass() } + fn bound(&self) -> Bound { + Bound::bypass() + } } diff --git a/src/object/point_light.rs b/src/object/point_light.rs index 3276356..cc6ad91 100644 --- a/src/object/point_light.rs +++ b/src/object/point_light.rs @@ -2,33 +2,42 @@ extern crate nalgebra as na; use na::*; -use crate::types::*; use super::*; +use crate::types::*; pub struct PointLight { pub pos: Point3f, pub color: Color, - pub intensity: f32 + pub intensity: f64, } #[allow(dead_code)] impl PointLight { - pub fn new(pos: Point3f, color: Color, intensity: f32) -> PointLight { - PointLight { pos, color, intensity } + pub fn new(pos: Point3f, color: Color, intensity: f64) -> PointLight { + PointLight { + pos, + color, + intensity, + } } } impl Light for PointLight { fn check_shadow(&self, point: Point3f, objects: &Vec) -> bool { let max_d = distance(&self.pos, &point); - objects.iter() - .filter_map(|obj| obj.intersect(Ray::from_points(self.pos, point))) - .all(|d| d - max_d > -1e-3 ) + objects + .iter() + .filter_map(|obj| obj.intersect(Ray::from_points(self.pos, point))) + .all(|d| d - max_d > -1e-3) } - fn get_color(&self, _point: Point3f) -> Color { self.color } + fn get_color(&self, _point: Point3f) -> Color { + self.color + } - fn intensity(&self, _point: Point3f) -> f32 { self.intensity } + fn intensity(&self, _point: Point3f) -> f64 { + self.intensity + } fn direction(&self, point: Point3f) -> Unit3f { Unit::new_normalize(self.pos - point) @@ -42,7 +51,13 @@ mod tests { #[test] fn point_light_check_shadow() { let light = PointLight::new(Point3::new(0.0, 1.0, 0.0), Color::white(), 1.0); - let block = Object::new(Sphere::new_solid(0.0, 0.5, 0.0, 0.1, Texture::new(0.0, 0.0, 0.0, 0.0))); + let block = Object::new(Sphere::new_solid( + 0.0, + 0.5, + 0.0, + 0.1, + Texture::new(0.0, 0.0, 0.0, 0.0), + )); assert!(light.check_shadow(Point3::origin(), &Vec::new())); assert!(!light.check_shadow(Point3::origin(), &vec![block])); diff --git a/src/object/sphere.rs b/src/object/sphere.rs index 232637f..5c5f60f 100644 --- a/src/object/sphere.rs +++ b/src/object/sphere.rs @@ -1,49 +1,57 @@ extern crate nalgebra as na; -use std::f32::consts::PI; +use std::f64::consts::PI; -use na::*; use na::geometry::Point3; +use na::*; +use super::{bound::*, Surface}; use crate::types::*; -use super::{Surface, bound::*}; pub struct Sphere { pub center: Point3f, // Center point of the sphere. - pub radius: f32, // Radius of the sphere. + pub radius: f64, // Radius of the sphere. - texture: Box Texture> // Texture map. - // Uses spherical coordinates (normalized from 0-1) as input. + texture: Box Texture>, // Texture map. + // Uses spherical coordinates (normalized from 0-1) as input. } #[allow(dead_code)] 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) -> Texture + pub fn new(x: f64, y: f64, z: f64, radius: f64, texture: F) -> Self + where + F: Fn(f64, f64) -> Texture, { Sphere { - center: Point3::new(x, y, z), radius, - texture: Box::new(texture) + center: Point3::new(x, y, z), + radius, + texture: Box::new(texture), } } // Creates a new sphere of a solid color. - pub fn new_solid(x: f32, y: f32, z: f32, radius: f32, texture: Texture) -> Self - { Sphere::new(x, y, z, radius, move |_, _| texture) } + pub fn new_solid(x: f64, y: f64, z: f64, radius: f64, texture: Texture) -> Self { + Sphere::new(x, y, z, radius, move |_, _| texture) + } } impl Surface for Sphere { - fn intersect(&self, ray: Ray) -> Option { - fn solve_quadratic(b: f32, c: f32) -> Option<(f32, f32)> { + fn intersect(&self, ray: Ray) -> Option { + fn solve_quadratic(b: f64, c: f64) -> Option<(f64, f64)> { let discr = b * b - 4.0 * c; - if discr < 0.0 { None } - else if discr == 0.0 { + if discr < 0.0 { + None + } else if discr == 0.0 { let x = -0.5 * b; Some((x, x)) } else { - let q = if b > 0.0 { -0.5 * (b + discr.sqrt()) } else { -0.5 * (b - discr.sqrt()) }; + let q = if b > 0.0 { + -0.5 * (b + discr.sqrt()) + } else { + -0.5 * (b - discr.sqrt()) + }; Some((q, c / q)) } } @@ -54,11 +62,17 @@ impl Surface for Sphere { let (mut t0, mut t1) = solve_quadratic(b, c)?; - if t0 > t1 { std::mem::swap(&mut t0, &mut t1); } + if t0 > t1 { + std::mem::swap(&mut t0, &mut t1); + } - if t0 >= 0.0 { Some(t0) } - else if t1 >= 0.0 { Some(t1) } - else { None } + if t0 >= 0.0 { + Some(t0) + } else if t1 >= 0.0 { + Some(t1) + } else { + None + } } fn normal(&self, point: Point3f) -> Unit3f { @@ -77,5 +91,11 @@ impl Surface for Sphere { (*self.texture)(x, y) } - fn bound(&self) -> Bound { Bound { center: self.center, radius: self.radius, bypass: false } } + fn bound(&self) -> Bound { + Bound { + center: self.center, + radius: self.radius, + bypass: false, + } + } } diff --git a/src/object/triangle.rs b/src/object/triangle.rs index c09624f..6186e6f 100644 --- a/src/object/triangle.rs +++ b/src/object/triangle.rs @@ -2,11 +2,11 @@ extern crate nalgebra as na; use std::cmp::Ordering; -use na::*; use na::geometry::Point3; +use na::*; +use super::{bound::*, Surface}; use crate::types::*; -use super::{Surface, bound::*}; pub struct Triangle { pub v1: usize, // Handles to 3 vertices. @@ -14,35 +14,45 @@ pub struct Triangle { pub v3: usize, normal: Unit3f, // Precalculated normal vector. - area: f32, // Precalculated area for barycentric calculations. + area: f64, // Precalculated area for barycentric calculations. - texture: Box Texture> // Texture map. - // Uses barycentric coordinates as input. + texture: Box Texture>, // Texture map. + // Uses barycentric coordinates as input. } pub struct TriangleMesh { pub vertices: Vec, - pub triangles: Vec + pub triangles: Vec, } -fn tri_area(a: &Point3f, b: &Point3f, c: &Point3f) -> f32 { - let prlg_area: f32 = (b - a).cross(&(c - a)).norm(); +fn tri_area(a: &Point3f, b: &Point3f, c: &Point3f) -> f64 { + let prlg_area: f64 = (b - a).cross(&(c - a)).norm(); prlg_area / 2.0 } impl Triangle { - fn vertex1<'a>(&self, vertices: &'a Vec) -> &'a Point3f { &vertices[self.v1] } - fn vertex2<'a>(&self, vertices: &'a Vec) -> &'a Point3f { &vertices[self.v2] } - fn vertex3<'a>(&self, vertices: &'a Vec) -> &'a Point3f { &vertices[self.v3] } + fn vertex1<'a>(&self, vertices: &'a Vec) -> &'a Point3f { + &vertices[self.v1] + } + fn vertex2<'a>(&self, vertices: &'a Vec) -> &'a Point3f { + &vertices[self.v2] + } + fn vertex3<'a>(&self, vertices: &'a Vec) -> &'a Point3f { + &vertices[self.v3] + } // Conversion of barycentric coordinates to // a point on the triangle. - fn from_bary(&self, vertices: &Vec, t: f32, u: f32, v: f32) -> Point3f { - Point::from(t * self.vertex1(vertices).coords + u * self.vertex2(vertices).coords + v * self.vertex3(vertices).coords) + fn from_bary(&self, vertices: &Vec, t: f64, u: f64, v: f64) -> Point3f { + Point::from( + t * self.vertex1(vertices).coords + + u * self.vertex2(vertices).coords + + v * self.vertex3(vertices).coords, + ) } // Conversion of a point to barycentric coordinates. - fn to_bary(&self, vertices: &Vec, point: Point3f) -> (f32, f32, f32) { + fn to_bary(&self, vertices: &Vec, point: Point3f) -> (f64, f64, f64) { let t = tri_area(self.vertex2(vertices), self.vertex3(vertices), &point) / self.area; let u = tri_area(self.vertex1(vertices), self.vertex3(vertices), &point) / self.area; let v = tri_area(self.vertex1(vertices), self.vertex2(vertices), &point) / self.area; @@ -50,32 +60,39 @@ impl Triangle { (t, u, v) } - fn intersect_(&self, vertices: &Vec, ray: Ray) -> Option<(f32, f32, f32)> { + fn intersect_(&self, vertices: &Vec, ray: Ray) -> Option<(f64, f64, f64)> { let vect2_1 = self.vertex2(vertices) - self.vertex1(vertices); let vect3_1 = self.vertex3(vertices) - self.vertex1(vertices); let p_vect = ray.direction.cross(&vect3_1); let det = p_vect.dot(&vect2_1); - if det.abs() < 1e-3 { return None; } + if det.abs() < 1e-3 { + return None; + } let t_vect = ray.origin - self.vertex1(vertices); let u = t_vect.dot(&p_vect) / det; - if u < 0.0 || u > 1.0 { return None; } + if u < 0.0 || u > 1.0 { + return None; + } let q_vect = t_vect.cross(&vect2_1); let v = ray.direction.dot(&q_vect) / det; - if v < 0.0 || (u + v) > 1.0 { return None; } + if v < 0.0 || (u + v) > 1.0 { + return None; + } let t = 1.0 - u - v; Some((t, u, v)) } - fn intersect(&self, vertices: &Vec, ray: Ray) -> Option { - self.intersect_(vertices, ray).map(|(t, u, v)| distance(&ray.origin, &self.from_bary(vertices, t, u, v))) + fn intersect(&self, vertices: &Vec, ray: Ray) -> Option { + self.intersect_(vertices, ray) + .map(|(t, u, v)| distance(&ray.origin, &self.from_bary(vertices, t, u, v))) } fn get_texture(&self, vertices: &Vec, point: Point3f) -> Texture { @@ -86,45 +103,81 @@ impl Triangle { #[allow(dead_code)] impl TriangleMesh { - pub fn new(vertices: Vec, tris: Vec<(usize, usize, usize, Box Texture>)>) -> Self { - let triangles = tris.into_iter() - .map(|(v1, v2, v3, f)| Triangle { - v1, v2, v3, - normal: Unit::new_normalize((&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1]))), - area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]), - texture: f - }).collect(); - TriangleMesh { - vertices, triangles - } - } - - pub fn new_solid(vertices: Vec, tris: Vec<(usize, usize, usize)>, texture: Texture) -> Self { - let triangles = tris.into_iter() - .map(|(v1, v2, v3)| Triangle { - v1, v2, v3, - normal: Unit::new_normalize((&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1]))), - area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]), - texture: Box::new(move |_, _, _| texture) - }).collect(); + pub fn new( + vertices: Vec, + tris: Vec<(usize, usize, usize, Box Texture>)>, + ) -> Self { + let triangles = tris + .into_iter() + .map(|(v1, v2, v3, f)| Triangle { + v1, + v2, + v3, + normal: Unit::new_normalize( + (&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1])), + ), + area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]), + texture: f, + }) + .collect(); TriangleMesh { vertices, - triangles + triangles, } } - pub fn singleton(vertex1: Point3f, vertex2: Point3f, vertex3: Point3f, texture: F) -> Self - where F: Fn(f32, f32, f32) -> Texture - { TriangleMesh::new(vec![vertex1, vertex2, vertex3], vec![(0, 1, 2, Box::new(texture))]) } + pub fn new_solid( + vertices: Vec, + tris: Vec<(usize, usize, usize)>, + texture: Texture, + ) -> Self { + let triangles = tris + .into_iter() + .map(|(v1, v2, v3)| Triangle { + v1, + v2, + v3, + normal: Unit::new_normalize( + (&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1])), + ), + area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]), + texture: Box::new(move |_, _, _| texture), + }) + .collect(); + TriangleMesh { + vertices, + triangles, + } + } - pub fn singleton_solid(vertex1: Point3f, vertex2: Point3f, vertex3: Point3f, texture: Texture) -> Self - { TriangleMesh::singleton(vertex1, vertex2, vertex3, move |_, _, _| texture) } + pub fn singleton( + vertex1: Point3f, + vertex2: Point3f, + vertex3: Point3f, + texture: F, + ) -> Self + where + F: Fn(f64, f64, f64) -> Texture, + { + TriangleMesh::new( + vec![vertex1, vertex2, vertex3], + vec![(0, 1, 2, Box::new(texture))], + ) + } + pub fn singleton_solid( + vertex1: Point3f, + vertex2: Point3f, + vertex3: Point3f, + texture: Texture, + ) -> Self { + TriangleMesh::singleton(vertex1, vertex2, vertex3, move |_, _, _| texture) + } fn closest_tri(&self, point: Point3f) -> &Triangle { - self.triangles.iter() + self.triangles + .iter() .map(move |tri| { - let rel_pos = point - tri.vertex1(&self.vertices); let proj_point3 = rel_pos - (*tri.normal * tri.normal.dot(&rel_pos)); @@ -139,15 +192,17 @@ impl TriangleMesh { (tri, distance(&point, &point_new)) }) .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal)) - .unwrap().0 + .unwrap() + .0 } } impl Surface for TriangleMesh { - fn intersect(&self, ray: Ray) -> Option { - self.triangles.iter() - .filter_map(|tri| tri.intersect(&self.vertices, ray)) - .min_by(|a, b| a.partial_cmp(&b).unwrap_or(Ordering::Equal)) + fn intersect(&self, ray: Ray) -> Option { + self.triangles + .iter() + .filter_map(|tri| tri.intersect(&self.vertices, ray)) + .min_by(|a, b| a.partial_cmp(&b).unwrap_or(Ordering::Equal)) } fn normal(&self, point: Point3f) -> Unit3f { @@ -160,22 +215,26 @@ impl Surface for TriangleMesh { // Uses Welzl's algorithm to solve the bounding sphere problem fn bound(&self) -> Bound { - fn smallest_sphere_plane(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f32) { + fn smallest_sphere_plane(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f64) { if points.len() == 0 || boundary.len() == 3 { match boundary.len() { 0 => (Point3::new(0.0, 0.0, 0.0), 0.0), 1 => (*boundary[0], 0.0), - 2 => { let half_span = 0.5 * (boundary[1] - boundary[0]); - (*boundary[0] + half_span, half_span.norm()) }, + 2 => { + let half_span = 0.5 * (boundary[1] - boundary[0]); + (*boundary[0] + half_span, half_span.norm()) + } 3 => triangle_sphere(boundary[0], boundary[1], boundary[2]), - _ => unreachable!() + _ => unreachable!(), } } else { let removed = points[0]; let points = Vec::from(&points[1..]); let bound = smallest_sphere(points.clone(), boundary.clone()); - if distance(&bound.0, removed) < bound.1 { return bound; } + if distance(&bound.0, removed) < bound.1 { + return bound; + } let mut boundary = boundary.clone(); boundary.push(removed); @@ -184,32 +243,44 @@ impl Surface for TriangleMesh { } } - fn triangle_sphere(point1: &Point3f, point2: &Point3f, point3: &Point3f) -> (Point3f, f32) { + fn triangle_sphere(point1: &Point3f, point2: &Point3f, point3: &Point3f) -> (Point3f, f64) { let a = point3 - point1; let b = point2 - point1; let crs = b.cross(&a); let to_center = (crs.cross(&b) * a.norm_squared() + a.cross(&crs) * b.norm_squared()) - / (2.0 * crs.norm_squared()); + / (2.0 * crs.norm_squared()); let radius = to_center.norm(); (point1 + to_center, radius) } - fn tetrahedron_sphere(point1: &Point3f, point2: &Point3f, point3: &Point3f, point4: &Point3f) -> (Point3f, f32) { - let matrix = Matrix4::from_rows(&[point1.to_homogeneous().transpose(), - point2.to_homogeneous().transpose(), - point3.to_homogeneous().transpose(), - point4.to_homogeneous().transpose()]); + fn tetrahedron_sphere( + point1: &Point3f, + point2: &Point3f, + point3: &Point3f, + point4: &Point3f, + ) -> (Point3f, f64) { + let matrix = Matrix4::from_rows(&[ + point1.to_homogeneous().transpose(), + point2.to_homogeneous().transpose(), + point3.to_homogeneous().transpose(), + point4.to_homogeneous().transpose(), + ]); let a = matrix.determinant() * 2.0; if (a != 0.0) { let mut matrix_mut = matrix.clone(); - let squares = Vector4::new(point1.coords.norm_squared(), point2.coords.norm_squared(), point3.coords.norm_squared(), point4.coords.norm_squared()); + let squares = Vector4::new( + point1.coords.norm_squared(), + point2.coords.norm_squared(), + point3.coords.norm_squared(), + point4.coords.norm_squared(), + ); matrix_mut.set_column(0, &squares); let center_x = matrix_mut.determinant(); @@ -231,23 +302,27 @@ impl Surface for TriangleMesh { } } - fn smallest_sphere(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f32) { + fn smallest_sphere(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f64) { if points.len() == 0 || boundary.len() == 4 { match boundary.len() { 0 => (Point3::new(0.0, 0.0, 0.0), 0.0), 1 => (*boundary[0], 0.0), - 2 => { let half_span = 0.5 * (boundary[1] - boundary[0]); - (*boundary[0] + half_span, half_span.norm()) }, + 2 => { + let half_span = 0.5 * (boundary[1] - boundary[0]); + (*boundary[0] + half_span, half_span.norm()) + } 3 => triangle_sphere(boundary[0], boundary[1], boundary[2]), 4 => tetrahedron_sphere(boundary[0], boundary[1], boundary[2], boundary[3]), - _ => unreachable!() + _ => unreachable!(), } } else { let removed = points[0]; let points = Vec::from(&points[1..]); let bound = smallest_sphere(points.clone(), boundary.clone()); - if distance(&bound.0, removed) < bound.1 { return bound; } + if distance(&bound.0, removed) < bound.1 { + return bound; + } let mut boundary = boundary.clone(); boundary.push(removed); @@ -257,14 +332,18 @@ impl Surface for TriangleMesh { } extern crate rand; - use rand::thread_rng; use rand::seq::SliceRandom; + use rand::thread_rng; let mut points: Vec<&Point3f> = self.vertices.iter().collect(); points.shuffle(&mut thread_rng()); let (center, radius) = smallest_sphere(points, Vec::new()); - Bound { center, radius: radius + 1e-3, bypass: false } + Bound { + center, + radius: radius + 1e-3, + bypass: false, + } } } diff --git a/src/render.rs b/src/render.rs index 8a6b174..f7ff970 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,18 +1,18 @@ extern crate nalgebra as na; use std::cmp::Ordering; -use std::f32::consts::PI; +use std::f64::consts::PI; -use na::*; use na::geometry::Point3; +use na::*; use crate::object::*; use crate::types::*; -fn trace(ray: Ray, objects: &Vec) -> Option<(&Object, f32)> { - objects.iter() - .filter_map(|obj| obj.intersect(ray) - .map(|x| (obj, x))) +fn trace(ray: Ray, objects: &Vec) -> Option<(&Object, f64)> { + objects + .iter() + .filter_map(|obj| obj.intersect(ray).map(|x| (obj, x))) .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal)) } @@ -20,7 +20,10 @@ fn light_point(objects: &Vec, obj: &Object, point: Point3f, light: &dyn if light.check_shadow(point, objects) { let texture = obj.get_texture(point); - light.get_color(point) * (texture.albedo / PI) * light.intensity(point) * obj.normal(point).dot(&*light.direction(point)) + light.get_color(point) + * (texture.albedo / PI) + * light.intensity(point) + * obj.normal(point).dot(&*light.direction(point)) } else { // Point is in shadow Color::black() @@ -32,8 +35,13 @@ pub fn cast_ray(ray: Ray, scene: &Scene) -> Color { let point = ray.project(dist); let surface_color = obj.get_texture(point).color; - scene.lights.iter() + scene + .lights + .iter() .map(|light| light_point(&scene.objects, obj, point, &**light)) - .fold(Color::black(), |acc, c| acc + c) * surface_color - } else { scene.background } + .fold(Color::black(), |acc, c| acc + c) + * surface_color + } else { + scene.background + } } diff --git a/src/types.rs b/src/types.rs index 6328873..08e43b3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,71 +2,83 @@ extern crate nalgebra as na; use std::ops::{Add, Mul}; -use na::*; use na::geometry::Point3; +use na::*; -pub type Point3f = Point3; -pub type Vector3f = Vector3; -pub type Unit3f = Unit>; +pub type Point3f = Point3; +pub type Vector3f = Vector3; +pub type Unit3f = Unit>; #[derive(Clone, Copy, Debug)] pub struct Ray { pub origin: Point3f, - pub direction: Unit3f + pub direction: Unit3f, } impl Ray { pub fn from_parts(origin: Point3f, direction: Unit3f) -> Self { Ray { origin, direction } } - pub fn new(origin: Point3f, direction: Vector3f) -> Self { Ray::from_parts(origin, Unit::new_normalize(direction)) } - pub fn from_points(origin: Point3f, points_to: Point3f) -> Self { Ray::new(origin, points_to - origin) } + pub fn new(origin: Point3f, direction: Vector3f) -> Self { + Ray::from_parts(origin, Unit::new_normalize(direction)) + } + pub fn from_points(origin: Point3f, points_to: Point3f) -> Self { + Ray::new(origin, points_to - origin) + } - pub fn project(&self, t: f32) -> Point3f { self.origin + t * self.direction.into_inner() } + pub fn project(&self, t: f64) -> Point3f { + self.origin + t * self.direction.into_inner() + } } #[derive(Clone, Copy, Debug, PartialEq)] pub struct Color { - pub red: f32, - pub green: f32, - pub blue: f32, + pub red: f64, + pub green: f64, + pub blue: f64, - _private: () // Private field prevents direct construction + _private: (), // Private field prevents direct construction } #[allow(dead_code)] impl Color { - pub fn new(red: f32, green: f32, blue: f32) -> Self { + pub fn new(red: f64, green: f64, blue: f64) -> Self { Color { - red: if red < 0.0 { 0.0 } else { red }, + red: if red < 0.0 { 0.0 } else { red }, green: if green < 0.0 { 0.0 } else { green }, - blue: if blue < 0.0 { 0.0 } else { blue }, + blue: if blue < 0.0 { 0.0 } else { blue }, - _private: () + _private: (), } } pub fn to_byte_array(&self) -> [u8; 3] { - let red = (255.0 * self.red) as u8; + let red = (255.0 * self.red) as u8; let green = (255.0 * self.green) as u8; - let blue = (255.0 * self.blue) as u8; + let blue = (255.0 * self.blue) as u8; [red, green, blue] } - pub fn gray(brightness: f32) -> Self { Color::new(brightness, brightness, brightness) } + pub fn gray(brightness: f64) -> Self { + Color::new(brightness, brightness, brightness) + } - pub fn black() -> Self { Color::gray(0.0) } - pub fn white() -> Self { Color::gray(1.0) } + pub fn black() -> Self { + Color::gray(0.0) + } + pub fn white() -> Self { + Color::gray(1.0) + } } impl Add for Color { type Output = Color; fn add(self, rhs: Color) -> Color { Color { - red: self.red + rhs.red, + red: self.red + rhs.red, green: self.green + rhs.green, - blue: self.blue + rhs.blue, - _private: () + blue: self.blue + rhs.blue, + _private: (), } } } @@ -75,22 +87,22 @@ impl Mul for Color { type Output = Color; fn mul(self, rhs: Color) -> Color { Color { - red: self.red * rhs.red, + red: self.red * rhs.red, green: self.green * rhs.green, - blue: self.blue * rhs.blue, - _private: () + blue: self.blue * rhs.blue, + _private: (), } } } -impl Mul for Color { +impl Mul for Color { type Output = Color; - fn mul(self, rhs: f32) -> Color { + fn mul(self, rhs: f64) -> Color { Color { - red: self.red * rhs, + red: self.red * rhs, green: self.green * rhs, - blue: self.blue * rhs, - _private: () + blue: self.blue * rhs, + _private: (), } } } @@ -98,15 +110,15 @@ impl Mul for Color { #[derive(Clone, Copy, Debug, PartialEq)] pub struct Texture { pub color: Color, - pub albedo: f32 + pub albedo: f64, } #[allow(dead_code)] impl Texture { - pub fn new(red: f32, green: f32, blue: f32, albedo: f32) -> Self { + pub fn new(red: f64, green: f64, blue: f64, albedo: f64) -> Self { Texture { color: Color::new(red, green, blue), - albedo + albedo, } } } From f33ee09b9d295eebe3d80c8ef674f95d95569448 Mon Sep 17 00:00:00 2001 From: Kiana Sheibani Date: Mon, 14 Oct 2024 18:11:19 -0400 Subject: [PATCH 08/10] refactor: rename `types.rs` to `util.rs` --- src/camera.rs | 2 +- src/main.rs | 40 +++++++++++++++++++++++---------------- src/object.rs | 2 +- src/object/bound.rs | 2 +- src/object/plane.rs | 2 +- src/object/point_light.rs | 2 +- src/object/sphere.rs | 2 +- src/object/triangle.rs | 2 +- src/render.rs | 5 +---- src/{types.rs => util.rs} | 0 10 files changed, 32 insertions(+), 27 deletions(-) rename src/{types.rs => util.rs} (100%) diff --git a/src/camera.rs b/src/camera.rs index 88eae1c..d479ef3 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -3,7 +3,7 @@ extern crate nalgebra as na; use na::geometry::{Point2, Point3}; use na::*; -use crate::types::Ray; +use crate::util::Ray; #[derive(Debug)] pub struct Camera { diff --git a/src/main.rs b/src/main.rs index 30f9fb8..e3757fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,22 @@ extern crate nalgebra as na; -use std::time::Instant; use std::fs::File; use std::io::Write; +use std::time::Instant; use na::*; -mod camera; use camera::*; -mod types; use types::*; -mod object; use object::*; -mod render; use render::*; +mod camera; +use camera::*; +mod util; +use util::*; +mod object; +use object::*; +mod render; +use render::*; fn render(camera: &Camera, scene: &Scene, filename: &str) -> std::io::Result<()> { - let width = camera.image_size.x; + let width = camera.image_size.x; let height = camera.image_size.y; let mut buffer: Vec = Vec::with_capacity((width * height) as usize); @@ -35,18 +39,22 @@ fn render(camera: &Camera, scene: &Scene, filename: &str) -> std::io::Result<()> } fn main() -> std::io::Result<()> { - - let camera = Camera::new(Point3::new(0.0,5.0,0.0), Vector3::new(0.0,-1.0,0.0), 1.0, 16.0 / 9.0, 2.0, 720); + let camera = Camera::new( + Point3::new(0.0, 5.0, 0.0), + Vector3::new(0.0, -1.0, 0.0), + 1.0, + 16.0 / 9.0, + 2.0, + 720, + ); let scene = Scene { - objects: vec![ - Object::new(Plane::xz(|_, _| Texture { color: Color::white(), albedo: 0.8 })), - - ], - lights: vec![ - - ], - background: Color::gray(0.5) + objects: vec![Object::new(Plane::xz(|_, _| Texture { + color: Color::white(), + albedo: 0.8, + }))], + lights: vec![], + background: Color::gray(0.5), }; let before = Instant::now(); diff --git a/src/object.rs b/src/object.rs index 9d7b108..76b3459 100644 --- a/src/object.rs +++ b/src/object.rs @@ -9,7 +9,7 @@ pub use bound::*; mod point_light; pub use point_light::*; -use crate::types::*; +use crate::util::*; // A trait for types that can be in Objects. pub trait Surface { diff --git a/src/object/bound.rs b/src/object/bound.rs index e295003..d368650 100644 --- a/src/object/bound.rs +++ b/src/object/bound.rs @@ -3,7 +3,7 @@ extern crate nalgebra as na; // use na::distance; use na::geometry::Point3; -use crate::types::*; +use crate::util::*; // A bounding sphere, used for // intersection test optimization. diff --git a/src/object/plane.rs b/src/object/plane.rs index e02ac95..c761ce6 100644 --- a/src/object/plane.rs +++ b/src/object/plane.rs @@ -4,7 +4,7 @@ use na::geometry::Point3; use na::*; use super::{bound::*, Surface}; -use crate::types::*; +use crate::util::*; pub struct Plane { pub center: Point3f, // Plane origin (used for texture mapping). diff --git a/src/object/point_light.rs b/src/object/point_light.rs index cc6ad91..b8e7b58 100644 --- a/src/object/point_light.rs +++ b/src/object/point_light.rs @@ -3,7 +3,7 @@ extern crate nalgebra as na; use na::*; use super::*; -use crate::types::*; +use crate::util::*; pub struct PointLight { pub pos: Point3f, diff --git a/src/object/sphere.rs b/src/object/sphere.rs index 5c5f60f..3838c61 100644 --- a/src/object/sphere.rs +++ b/src/object/sphere.rs @@ -6,7 +6,7 @@ use na::geometry::Point3; use na::*; use super::{bound::*, Surface}; -use crate::types::*; +use crate::util::*; pub struct Sphere { pub center: Point3f, // Center point of the sphere. diff --git a/src/object/triangle.rs b/src/object/triangle.rs index 6186e6f..205cb18 100644 --- a/src/object/triangle.rs +++ b/src/object/triangle.rs @@ -6,7 +6,7 @@ use na::geometry::Point3; use na::*; use super::{bound::*, Surface}; -use crate::types::*; +use crate::util::*; pub struct Triangle { pub v1: usize, // Handles to 3 vertices. diff --git a/src/render.rs b/src/render.rs index f7ff970..c17f2d8 100644 --- a/src/render.rs +++ b/src/render.rs @@ -3,11 +3,8 @@ extern crate nalgebra as na; use std::cmp::Ordering; use std::f64::consts::PI; -use na::geometry::Point3; -use na::*; - use crate::object::*; -use crate::types::*; +use crate::util::*; fn trace(ray: Ray, objects: &Vec) -> Option<(&Object, f64)> { objects diff --git a/src/types.rs b/src/util.rs similarity index 100% rename from src/types.rs rename to src/util.rs From 878f7c9ebb269a7e4dec7984dbcf30cb2b49fa56 Mon Sep 17 00:00:00 2001 From: Kiana Sheibani Date: Tue, 15 Oct 2024 14:17:55 -0400 Subject: [PATCH 09/10] style: stop renaming external crate --- src/camera.rs | 6 ++---- src/main.rs | 4 +--- src/object/bound.rs | 4 +--- src/object/plane.rs | 6 ++---- src/object/point_light.rs | 4 +--- src/object/sphere.rs | 6 ++---- src/object/triangle.rs | 6 ++---- src/render.rs | 2 -- src/util.rs | 6 ++---- 9 files changed, 13 insertions(+), 31 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index d479ef3..1e14362 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,7 +1,5 @@ -extern crate nalgebra as na; - -use na::geometry::{Point2, Point3}; -use na::*; +use nalgebra::geometry::{Point2, Point3}; +use nalgebra::*; use crate::util::Ray; diff --git a/src/main.rs b/src/main.rs index e3757fb..839ee33 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,8 @@ -extern crate nalgebra as na; - use std::fs::File; use std::io::Write; use std::time::Instant; -use na::*; +use nalgebra::*; mod camera; use camera::*; diff --git a/src/object/bound.rs b/src/object/bound.rs index d368650..d86c0ce 100644 --- a/src/object/bound.rs +++ b/src/object/bound.rs @@ -1,7 +1,5 @@ -extern crate nalgebra as na; - // use na::distance; -use na::geometry::Point3; +use nalgebra::geometry::Point3; use crate::util::*; diff --git a/src/object/plane.rs b/src/object/plane.rs index c761ce6..fa5a89f 100644 --- a/src/object/plane.rs +++ b/src/object/plane.rs @@ -1,7 +1,5 @@ -extern crate nalgebra as na; - -use na::geometry::Point3; -use na::*; +use nalgebra::geometry::Point3; +use nalgebra::*; use super::{bound::*, Surface}; use crate::util::*; diff --git a/src/object/point_light.rs b/src/object/point_light.rs index b8e7b58..2476d22 100644 --- a/src/object/point_light.rs +++ b/src/object/point_light.rs @@ -1,6 +1,4 @@ -extern crate nalgebra as na; - -use na::*; +use nalgebra::*; use super::*; use crate::util::*; diff --git a/src/object/sphere.rs b/src/object/sphere.rs index 3838c61..d5b1d8a 100644 --- a/src/object/sphere.rs +++ b/src/object/sphere.rs @@ -1,9 +1,7 @@ -extern crate nalgebra as na; - use std::f64::consts::PI; -use na::geometry::Point3; -use na::*; +use nalgebra::geometry::Point3; +use nalgebra::*; use super::{bound::*, Surface}; use crate::util::*; diff --git a/src/object/triangle.rs b/src/object/triangle.rs index 205cb18..8e0187f 100644 --- a/src/object/triangle.rs +++ b/src/object/triangle.rs @@ -1,9 +1,7 @@ -extern crate nalgebra as na; - use std::cmp::Ordering; -use na::geometry::Point3; -use na::*; +use nalgebra::geometry::Point3; +use nalgebra::*; use super::{bound::*, Surface}; use crate::util::*; diff --git a/src/render.rs b/src/render.rs index c17f2d8..f72f50e 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,5 +1,3 @@ -extern crate nalgebra as na; - use std::cmp::Ordering; use std::f64::consts::PI; diff --git a/src/util.rs b/src/util.rs index 08e43b3..30e7577 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,9 +1,7 @@ -extern crate nalgebra as na; - use std::ops::{Add, Mul}; -use na::geometry::Point3; -use na::*; +use nalgebra::geometry::Point3; +use nalgebra::*; pub type Point3f = Point3; pub type Vector3f = Vector3; From f0c8dae74cc2ab977734a47330345d15f1d6c423 Mon Sep 17 00:00:00 2001 From: Kiana Sheibani Date: Wed, 16 Oct 2024 15:48:42 -0400 Subject: [PATCH 10/10] refactor: use `Option` to bypass bounding sphere system --- src/object.rs | 6 +++--- src/object/bound.rs | 15 --------------- src/object/plane.rs | 7 +++---- src/object/sphere.rs | 7 +++---- src/object/triangle.rs | 9 ++++----- 5 files changed, 13 insertions(+), 31 deletions(-) diff --git a/src/object.rs b/src/object.rs index 76b3459..65b9bec 100644 --- a/src/object.rs +++ b/src/object.rs @@ -27,12 +27,12 @@ pub trait Surface { fn get_texture(&self, point: Point3f) -> Texture; // Creates a bounding sphere around the object. - fn bound(&self) -> Bound; + fn bound(&self) -> Option; } pub struct Object { pub surface: Box, - bound: Bound, + bound: Option, } impl Object { @@ -46,7 +46,7 @@ impl Object { } pub fn intersect(&self, ray: Ray) -> Option { - if self.bound.is_intersected(ray) { + if !self.bound.as_ref().is_some_and(|b| !b.is_intersected(ray)) { self.surface.intersect(ray) } else { None diff --git a/src/object/bound.rs b/src/object/bound.rs index d86c0ce..b461277 100644 --- a/src/object/bound.rs +++ b/src/object/bound.rs @@ -9,28 +9,13 @@ use crate::util::*; pub struct Bound { pub center: Point3f, pub radius: f64, - - // If true, then the bounding sphere is disabled. - pub bypass: bool, } impl Bound { pub fn is_intersected(&self, ray: Ray) -> bool { - if self.bypass { - return true; - } - let l = ray.origin - self.center; l.norm_squared() >= self.radius * self.radius } // pub fn contains(&self, point: &Point3f) -> bool { distance(&self.center, point) < self.radius } - - pub fn bypass() -> Self { - Bound { - center: Point3::origin(), - radius: 0.0, - bypass: true, - } - } } diff --git a/src/object/plane.rs b/src/object/plane.rs index fa5a89f..213e555 100644 --- a/src/object/plane.rs +++ b/src/object/plane.rs @@ -111,9 +111,8 @@ impl Surface for Plane { (*self.texture)(x, y) } - // Planes are infinite, so no finite - // bounding sphere could possibly contain one. - fn bound(&self) -> Bound { - Bound::bypass() + // Planes are infinite, so no finite bounding sphere could possibly contain one + fn bound(&self) -> Option { + None } } diff --git a/src/object/sphere.rs b/src/object/sphere.rs index d5b1d8a..6693e0a 100644 --- a/src/object/sphere.rs +++ b/src/object/sphere.rs @@ -89,11 +89,10 @@ impl Surface for Sphere { (*self.texture)(x, y) } - fn bound(&self) -> Bound { - Bound { + fn bound(&self) -> Option { + Some(Bound { center: self.center, radius: self.radius, - bypass: false, - } + }) } } diff --git a/src/object/triangle.rs b/src/object/triangle.rs index 8e0187f..f04c77e 100644 --- a/src/object/triangle.rs +++ b/src/object/triangle.rs @@ -212,7 +212,7 @@ impl Surface for TriangleMesh { } // Uses Welzl's algorithm to solve the bounding sphere problem - fn bound(&self) -> Bound { + fn bound(&self) -> Option { fn smallest_sphere_plane(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f64) { if points.len() == 0 || boundary.len() == 3 { match boundary.len() { @@ -270,7 +270,7 @@ impl Surface for TriangleMesh { let a = matrix.determinant() * 2.0; - if (a != 0.0) { + if a != 0.0 { let mut matrix_mut = matrix.clone(); let squares = Vector4::new( @@ -338,10 +338,9 @@ impl Surface for TriangleMesh { let (center, radius) = smallest_sphere(points, Vec::new()); - Bound { + Some(Bound { center, radius: radius + 1e-3, - bypass: false, - } + }) } }