diff --git a/scene.rhai b/scene.rhai index b44aff6..e4ed72c 100644 --- a/scene.rhai +++ b/scene.rhai @@ -1,35 +1,33 @@ let scene = Scene(); -let eye = P(0.0, 0.0, 3.0); -let target = P(0.0, 0.0, 0.0); -let up = V(0.0, 1.0, 3.0); - -let cam = Camera(eye, target, up, 70.0); - let material = Material(V(0.5,0.5,0.5), V(0.8, 0.8, 0.8), 25.0); -let ambient = Light(P(10.0,0.0,0.0), V(1.0,1.0,1.0), V(0.0, 0.0, 0.0)); +//let ambient = Light(P(10.0,0.0,0.0), V(1.0,1.0,1.0), V(0.0, 0.0, 0.0)); +//scene.addLight(ambient); -let light2 = Light(P(0.0,0.0,10.0), V(0.0,1.0,1.0), V(0.1, 0.01, 0.001)); - -scene.addLight(light2); -scene.addLight(ambient); +let light = Light(P(3.0,3.0,1.0), V(0.0,1.0,1.0), V(0.1, 0.01, 0.001)); +scene.addLight(light); -// let sphere = Sphere(P(0.0,0.0,0.0), 1.0, material); -// let sphere_node = Node(sphere); +let sphere = Sphere(P(0.0,0.0,0.0), 1.0, material); +let sphere_node = Node(sphere); +// let child = sphere_node.child(sphere); +// child.translate(V(1.0,1.0,1.0)); // scene.addNode(sphere_node); +// scene.addNode(child); // let cube = CubeUnit(material); // let cube_node = Node(cube); // scene.addNode(cube_node); -let gnonom = Gnonom(material); -let gnonom_node = Node(gnonom); -scene.addNode(gnonom_node); +// let gnonom = Gnonom(material); +// let gnonom_node = Node(gnonom); +// scene.addNode(gnonom_node); -// let cylinder = Cylinder(1.0,1.0, material); -// let cylinder_node = Node(cylinder); -// scene.addNode(cylinder_node); +let cylinder = Cylinder(1.0,1.0, material); +let cylinder_node = Node(cylinder); +cylinder_node.scale(V(4.0,0.3,0.3)); +cylinder_node.translate(V(0.0,-4.0,1.0)); +scene.addNode(cylinder_node); scene \ No newline at end of file diff --git a/src/bvh.rs b/src/bvh.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/bvh.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/camera.rs b/src/camera.rs index d75fbae..066321f 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,9 +1,4 @@ -use crate::ray::Ray; -use crate::{EPSILON, INFINITY}; -use nalgebra::{Matrix4, Perspective3, Point3, Unit, Vector3}; - -const ZNEAR: f64 = EPSILON; -const ZFAR: f64 = INFINITY; +use nalgebra::{Matrix4, Point3, Vector3}; #[allow(dead_code)] #[derive(Clone)] @@ -11,8 +6,8 @@ pub struct Camera { eye: Point3, target: Point3, up: Vector3, - view: Matrix4, - inv_view: Matrix4, + pub view: Matrix4, + pub inv_view: Matrix4, } #[allow(dead_code)] diff --git a/src/gui.rs b/src/gui.rs index a888df1..569cf8a 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -1,16 +1,24 @@ -use crate::{camera::Camera, scene::Scene, state::INIT_FILE, UP_VECTOR_F32, ZERO_VECTOR_F32}; +use crate::{ + camera::Camera, + light::Light, + primitive::*, + scene::{Node, Scene}, + state::INIT_FILE, + UP_VECTOR_F32, ZERO_VECTOR_F32, +}; use imgui::*; use nalgebra::{Point3, Vector3}; use pixels::{wgpu, PixelsContext}; +use rhai::Engine; use std::time::Instant; const BUFFER_PROPORTION_INIT: f32 = 1.0; const BUFFER_PROPORTION_MIN: f32 = 0.5; const BUFFER_PROPORTION_MAX: f32 = 1.0; -const RAYS_INIT: i32 = 9000; +const RAYS_INIT: i32 = 1000; const RAYS_MIN: i32 = 100; -const RAYS_MAX: i32 = 10000; +const RAYS_MAX: i32 = 100000; const CAMERA_MIN_FOV: f32 = 10.0; const CAMERA_MAX_FOV: f32 = 160.0; @@ -18,7 +26,7 @@ const CAMERA_INIT: f32 = 5.0; /// Manages all state required for rendering Dear ImGui over `Pixels`test. pub enum GuiEvent { - BufferResize(f32), + BufferResize(f32, f32), CameraUpdate(Camera), SceneLoad(Scene), } @@ -34,6 +42,7 @@ pub struct Gui { script_filename: String, script: String, + engine: Engine, scene: Scene, pub ray_num: i32, @@ -63,7 +72,7 @@ impl Gui { // Configure Dear ImGui fonts let hidpi_factor = window.scale_factor(); - let font_size = (11.0 * hidpi_factor) as f32; + let font_size = (16.0 * hidpi_factor) as f32; imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32; imgui .fonts() @@ -96,6 +105,7 @@ impl Gui { script_filename: String::from(INIT_FILE), script: String::new(), + engine: init_engine(), scene: Scene::empty(), ray_num: RAYS_INIT, @@ -156,9 +166,13 @@ impl Gui { BUFFER_PROPORTION_MAX, &mut self.buffer_proportion, ); + ui.slider("fov", CAMERA_MIN_FOV, CAMERA_MAX_FOV, &mut self.camera_fov); //Apply changes if ui.button("Apply") { - self.event = Some(GuiEvent::BufferResize(self.buffer_proportion)); + self.event = Some(GuiEvent::BufferResize( + self.buffer_proportion, + self.camera_fov, + )); }; } //Camera options @@ -167,7 +181,6 @@ impl Gui { ui.input_float3("Eye", &mut self.camera_eye).build(); ui.input_float3("Target", &mut self.camera_target).build(); ui.input_float3("Up", &mut self.camera_up).build(); - ui.slider("fov", CAMERA_MIN_FOV, CAMERA_MAX_FOV, &mut self.camera_fov); // Create three input fields for x, y, and z components if ui.button("Apply Camera") { println!("Camera changed: {:?}", self.camera_eye); @@ -175,14 +188,10 @@ impl Gui { let (ex, ey, ez) = (eye[0] as f64, eye[1] as f64, eye[2] as f64); let (tx, ty, tz) = (target[0] as f64, target[1] as f64, target[2] as f64); let (ux, uy, uz) = (up[0] as f64, up[1] as f64, up[2] as f64); - let camera = Camera::new( Point3::new(ex, ey, ez), Point3::new(tx, ty, tz), Vector3::new(ux, uy, uz), - 1, - 1, - self.camera_fov as f64, ); self.event = Some(GuiEvent::CameraUpdate(camera)); } @@ -199,7 +208,7 @@ impl Gui { } } if ui.button("Apply script") { - match Scene::from_rhai(&self.script) { + match self.engine.eval(&self.script) { Ok(scene) => { self.scene = scene; self.event = Some(GuiEvent::SceneLoad(self.scene.clone())); @@ -208,7 +217,7 @@ impl Gui { } } //Script block - ui.input_text_multiline("script", &mut self.script, [500., 900.]) + ui.input_text_multiline("script", &mut self.script, [600., 1500.]) .build(); } @@ -244,3 +253,74 @@ impl Gui { .handle_event(self.imgui.io_mut(), window, event); } } + +pub fn init_engine() -> Engine { + let mut engine = Engine::new(); + + engine + .register_type::>() + .register_fn("V", Vector3::::new); + engine + .register_type::>() + .register_fn("P", Point3::::new); + engine + .register_type::() + .register_fn("Camera", Camera::new); + engine + .register_type::() + .register_fn("Scene", Scene::empty) + .register_fn("addNode", Scene::add_node) + .register_fn("addLight", Scene::add_light); + + engine + .register_type::() + .register_fn("Node", Node::new) + .register_fn("translate", Node::translate) + .register_fn("rotate", Node::rotate) + .register_fn("scale", Node::scale) + .register_fn("child", Node::child); + engine + .register_type::() + .register_fn("Light", Light::new); + engine + .register_type::() + .register_fn("Material", Material::new) + .register_fn("MaterialRed", Material::red) + .register_fn("MaterialBlue", Material::blue) + .register_fn("MaterialGreen", Material::green) + .register_fn("MaterialMagenta", Material::magenta) + .register_fn("MaterialTurquoise", Material::turquoise); + engine + .register_type::() + .register_fn("Sphere", Sphere::new) + .register_fn("SphereUnit", Sphere::unit); + engine + .register_type::() + .register_fn("Cube", Cube::new) + .register_fn("CubeUnit", Cube::unit); + engine + .register_type::() + .register_fn("Cone", Cone::new) + .register_fn("ConeUnit", Cone::unit); + engine + .register_type::() + .register_fn("Cylinder", Cylinder::new); + engine + .register_type::() + .register_fn("Circle", Circle::new) + .register_fn("CircleUnit", Circle::unit); + engine + .register_type::() + .register_fn("Rectangle", Rectangle::new) + .register_fn("RectangleUnit", Rectangle::unit); + engine + .register_type::() + .register_fn("Steiner", SteinerSurface::new); + engine + .register_type::() + .register_fn("Torus", Torus::new); + engine + .register_type::() + .register_fn("Gnonom", Gnonom::new); + engine +} diff --git a/src/happy-tree.png b/src/happy-tree.png deleted file mode 100644 index fc86db3..0000000 Binary files a/src/happy-tree.png and /dev/null differ diff --git a/src/main.rs b/src/main.rs index 33df581..fcdddf2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use crate::state::run; use error_iter::ErrorIter; -const EPSILON: f64 = 1e-9; +const EPSILON: f64 = 1e-6; const INFINITY: f64 = f64::MAX; const EPSILON_VECTOR: Vector3 = Vector3::new(EPSILON, EPSILON, EPSILON); static ZERO_VECTOR: Vector3 = Vector3::new(0.0, 0.0, 0.0); diff --git a/src/primitive.rs b/src/primitive.rs index 0d27275..da4e0bf 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -1,8 +1,8 @@ #[allow(dead_code)] use crate::ray::Ray; use crate::{EPSILON, EPSILON_VECTOR, INFINITY}; -use nalgebra::{distance, Point3, Unit, Vector3}; -use roots::{find_roots_cubic, find_roots_quadratic, find_roots_quartic, Roots}; +use nalgebra::{distance, Matrix4, Point3, Vector3}; +use roots::{find_roots_quadratic, find_roots_quartic, Roots}; use std::fs::File; use std::io::{BufRead, BufReader}; use std::sync::Arc; @@ -54,11 +54,22 @@ impl Material { pub struct Intersection { // Information about an intersection pub point: Point3, - pub normal: Unit>, - pub incidence: Unit>, + pub normal: Vector3, + pub incidence: Vector3, pub material: Arc, pub distance: f64, } +impl Intersection { + pub fn transform(&self, trans: &Matrix4, inv_trans: &Matrix4) -> Intersection { + Intersection { + point: trans.transform_point(&self.point), + normal: inv_trans.transpose().transform_vector(&self.normal), + incidence: trans.transform_vector(&self.incidence), + material: self.material.clone(), + distance: self.distance, + } + } +} // BOUNDING BOX ----------------------------------------------------------------- #[derive(Clone)] @@ -154,7 +165,7 @@ impl Primitive for Sphere { }; let intersect = ray.at_t(t); - let normal = Unit::new_normalize(intersect - self.position); + let normal = (intersect - self.position).normalize(); Some(Intersection { point: intersect, normal, @@ -238,7 +249,7 @@ impl Primitive for Circle { false => { return Some(Intersection { point: intersect, - normal: Unit::new_normalize(self.normal), + normal: self.normal.normalize(), incidence: ray.b, material: Arc::clone(&self.material), distance: t, @@ -329,9 +340,9 @@ impl Primitive for Cylinder { let normal = Vector3::new(2.0 * intersect.x, 0.0, 2.0 * intersect.z); Some(Intersection { point: intersect, - normal: Unit::new_normalize(normal), - material: Arc::clone(&self.material), + normal: normal, incidence: ray.b, + material: Arc::clone(&self.material), distance: t, }) } else { @@ -462,9 +473,9 @@ impl Primitive for Cone { match intersect.y >= self.base && intersect.y <= self.apex { true => Some(Intersection { point: intersect, - normal: Unit::new_normalize(self.get_normal(intersect)), - material: Arc::clone(&self.material), + normal: self.get_normal(intersect), incidence: ray.b, + material: Arc::clone(&self.material), distance: t, }), false => None, @@ -568,7 +579,7 @@ impl Primitive for Rectangle { if pi_dot_r1 >= -w2 && pi_dot_r1 <= w2 && pi_dot_r2 >= -h2 && pi_dot_r2 <= h2 { return Some(Intersection { point: intersect, - normal: Unit::new_normalize(self.normal), + normal: self.normal, incidence: ray.b, material: Arc::clone(&self.material), distance: t, @@ -657,7 +668,7 @@ impl Primitive for Cube { Some(Intersection { point: intersect, - normal: Unit::new_normalize(normal), + normal: normal, incidence: ray.b, material: Arc::clone(&self.material), distance: tmin, @@ -748,7 +759,7 @@ impl Primitive for Triangle { { Some(Intersection { point: intersect, - normal: Unit::new_normalize(normal), + normal: normal, incidence: ray.b, material: Arc::clone(&self.material), distance: t, @@ -958,11 +969,12 @@ impl Primitive for SteinerSurface { //Now we have the smallest non-zero t let point = ray.at_t(t); let (x, y, z) = (point.x, point.y, point.z); - let normal = Unit::new_normalize(Vector3::new( + let normal = Vector3::new( 2.0 * x * y * y + 2.0 * x * z * z + y * z, 2.0 * x * x * y + 2.0 * z * z * y + x * z, 2.0 * x * x * z + 2.0 * z * y * y + x * y, - )); + ) + .normalize(); Some(Intersection { point, @@ -1106,7 +1118,7 @@ impl Primitive for Torus { -4.0 * (r2.powf(2.0) - r1.powf(2.0) + x.powf(2.0) + y.powf(2.0) + z.powf(2.0)) * r1; let dz = -8.0 * r2.powf(2.0) * x + 4.0 * (r2.powf(2.0) - r1.powf(2.0) + x.powf(2.0) + y.powf(2.0) + z.powf(2.0)) * x; - let normal = Unit::new_normalize(Vector3::new(dx, dy, dz)); + let normal = Vector3::new(dx, dy, dz).normalize(); Some(Intersection { point, diff --git a/src/ray.rs b/src/ray.rs index 0b50f54..f808f11 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -1,52 +1,61 @@ use crate::{ - primitive::Intersection, + primitive::{self, Intersection}, raytracer::phong_shade_point, scene::{Node, Scene}, EPSILON, INFINITY, }; -use nalgebra::{Point3, Unit, Vector3}; +use nalgebra::{Matrix4, Point3, Vector3}; #[derive(Clone)] pub struct Ray { pub a: Point3, - pub b: Unit>, + pub b: Vector3, } impl Ray { - pub fn new(a: Point3, b: Unit>) -> Ray { - Ray { a, b } + pub fn new(a: Point3, b: Vector3) -> Ray { + Ray { + a, + b: b.normalize(), + } } pub fn unit() -> Ray { let a = Point3::new(0.0, 0.0, 0.0); - let b = Unit::new_normalize(Vector3::new(0.0, 1.0, 0.0)); + let b = Vector3::new(0.0, 1.0, 0.0); Ray { a, b } } pub fn at_t(&self, t: f64) -> Point3 { - self.a + self.b.into_inner() * (t + EPSILON) + self.a + self.b * t } //Shade a single ray pub fn shade_ray(&self, scene: &Scene) -> Option> { let intersect = self.get_closest_intersection(&scene.nodes); + match intersect { Some(intersect) => Some(phong_shade_point(&scene, &intersect)), None => None, } } - // Find the closest intersection, given a ray in world coordinates + // Find the closest intersection pub fn get_closest_intersection(&self, nodes: &Vec) -> Option { let mut closest_distance = INFINITY; let mut closest_intersect: Option = None; for node in nodes { let primitive = node.primitive.clone(); - let trans = node.trans; - let inv_trans = node.inv_trans; - if let Some(intersect) = primitive.intersect_ray(self) { - if intersect.distance < closest_distance { - closest_distance = intersect.distance; - closest_intersect = Some(intersect); + //Transform ray to view coords + let ray = self.transform(&node.inv_viewmodel); + + if primitive.intersect_bounding_box(&ray).is_some() { + if let Some(intersect) = primitive.intersect_ray(&ray) { + if intersect.distance < closest_distance { + closest_distance = intersect.distance; + //Convert back to world coords + let intersect = intersect.transform(&node.model, &node.inv_model); + closest_intersect = Some(intersect); + } } } } @@ -54,30 +63,40 @@ impl Ray { closest_intersect } + pub fn transform(&self, trans: &Matrix4) -> Ray { + Ray { + a: trans.transform_point(&self.a), + b: trans.transform_vector(&self.b), + } + } + pub fn cast_rays(fovy: f64, width: u32, height: u32) -> Vec { let aspect = width as f64 / height as f64; let fovy_radians = fovy.to_radians(); - //Verify this part later + let fovh_radians = 2.0 * ((fovy_radians / 2.0).tan() * aspect).atan(); + let dir = Vector3::new(0.0, 0.0, 1.0); let up = Vector3::new(0.0, 1.0, 0.0); let hor = Vector3::new(1.0, 0.0, 0.0); - let half_height = fovy_radians.tan(); - let half_width = aspect * half_height; + let vheight = 2.0 * (fovy_radians / 2.0).tan(); + let vwidth = 2.0 * (fovh_radians / 2.0).tan(); - let d_hor_vec = hor * (2.0 * half_width / width as f64) as f64; - let d_vert_vec = up * (2.0 * half_height / height as f64) as f64; + let d_hor_vec = hor * (vwidth / width as f64) as f64; + let d_vert_vec = up * (vheight / height as f64) as f64; - //All good + let half_width = width / 2; + let half_height = height / 2; let mut rays = Vec::with_capacity(width as usize * height as usize); for j in 0..height as i32 { for i in 0..width as i32 { - let horizontal = (i - half_width as i32) as f64 * (d_hor_vec); - let vertical = (-j + half_height as i32) as f64 * (d_vert_vec); - + let x = i - half_width as i32; + let y = -j + half_height as i32; + let horizontal = x as f64 * d_hor_vec; + let vertical = y as f64 * (d_vert_vec); let direction = dir + horizontal + vertical; - let ray = Ray::new(Point3::new(0.0, 0.0, 0.0), Unit::new_normalize(direction)); + let ray = Ray::new(Point3::new(0.0, 0.0, 0.0), direction); rays.push(ray); } } diff --git a/src/raytracer.rs b/src/raytracer.rs index b88004f..91accb2 100644 --- a/src/raytracer.rs +++ b/src/raytracer.rs @@ -1,4 +1,4 @@ -use crate::{light::Light, primitive::Intersection, scene::*, ZERO_VECTOR}; +use crate::{light::Light, primitive::Intersection, ray::Ray, scene::*, EPSILON, ZERO_VECTOR}; use nalgebra::{Unit, Vector3}; @@ -11,6 +11,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3 material, .. } = intersect; + let kd = material.kd; let ks = material.ks; let shininess = material.shininess; @@ -28,9 +29,15 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3 // Point to light let to_light = light_position - point; let light_distance = to_light.norm(); - let to_light = Unit::new_normalize(to_light); + let to_light = to_light; + + let to_light_ray = Ray::new(point.clone() + normal * EPSILON, to_light); + if light_blocked(scene, to_light_ray) { + continue; + } + // Point to camera - let to_camera = Unit::new_normalize(-incidence.into_inner()); + let to_camera = -incidence; // Diffuse component let n_dot_l = normal.dot(&to_light).max(0.0); let diffuse = n_dot_l * kd; @@ -58,3 +65,15 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3 let (r, g, b) = (colour.x as u8, colour.y as u8, colour.z as u8); Vector3::new(r, g, b) } + +fn light_blocked(scene: &Scene, ray: Ray) -> bool { + for node in &scene.nodes { + let ray = ray.transform(&node.inv_model); + if node.primitive.intersect_bounding_box(&ray).is_some() { + if node.primitive.intersect_ray(&ray).is_some() { + return true; + } + } + } + false +} diff --git a/src/scene.rs b/src/scene.rs index 12f2dbf..4b23d6a 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,38 +1,66 @@ use crate::camera::Camera; use crate::light::Light; use crate::primitive::*; -use nalgebra::{Matrix4, Point3, Vector3}; -use rhai::{Engine, EvalAltResult}; +use nalgebra::{Matrix4, Vector3}; use std::sync::Arc; #[derive(Clone)] pub struct Node { pub primitive: Arc, - pub model_transform: Matrix4, + pub model: Matrix4, + pub inv_model: Matrix4, + pub viewmodel: Matrix4, + pub inv_viewmodel: Matrix4, } impl Node { - pub fn new(primitive: Arc) -> Self { + pub fn new(primitive: Arc) -> Node { Node { primitive, - model_transform: Matrix4::identity(), + model: Matrix4::identity(), + inv_model: Matrix4::identity(), + viewmodel: Matrix4::identity(), + inv_viewmodel: Matrix4::identity(), } } - pub fn rotate(&mut self, roll: f32, pitch: f32, yaw: f32) { - let roll = (roll as f64).to_radians() as f32; - let pitch = (pitch as f64).to_radians() as f32; - let yaw = (yaw as f64).to_radians() as f32; + pub fn rotate(&mut self, roll: f64, pitch: f64, yaw: f64) { + let roll = roll.to_radians(); + let pitch = pitch.to_radians(); + let yaw = yaw.to_radians(); let rotation_matrix = Matrix4::from_euler_angles(roll, pitch, yaw); - self.model_transform = rotation_matrix * self.model_transform; + self.model = rotation_matrix * self.model; + self.inv_model = self.model.try_inverse().unwrap(); + self.viewmodel = rotation_matrix * self.viewmodel; + self.inv_viewmodel = self.inv_viewmodel.try_inverse().unwrap(); } - pub fn translate(&mut self, translation: &Vector3) { - let translation_matrix = Matrix4::new_translation(translation); - self.model_transform = translation_matrix * self.model_transform; + pub fn translate(&mut self, translation: Vector3) { + let translation_matrix = Matrix4::new_translation(&translation); + self.model = translation_matrix * self.model; + self.inv_model = self.model.try_inverse().unwrap(); + self.viewmodel = translation_matrix * self.viewmodel; + self.inv_viewmodel = self.inv_viewmodel.try_inverse().unwrap(); } - pub fn scale(&mut self, scale: &Vector3) { - let scale_matrix = Matrix4::new_nonuniform_scaling(scale); - self.model_transform = scale_matrix * self.model_transform; + pub fn scale(&mut self, scale: Vector3) { + let scale_matrix = Matrix4::new_nonuniform_scaling(&scale); + self.model = scale_matrix * self.model; + self.inv_model = self.model.try_inverse().unwrap(); + self.viewmodel = scale_matrix * self.viewmodel; + self.inv_viewmodel = self.inv_viewmodel.try_inverse().unwrap(); + } + pub fn child(self, primitive: Arc) -> Node { + Node { + primitive, + model: self.model, + inv_model: self.inv_model, + viewmodel: self.model, + inv_viewmodel: self.inv_model, + } + } + pub fn compute(&mut self, view: &Matrix4, inv_view: &Matrix4) { + self.viewmodel = view * self.model; + self.inv_viewmodel = self.inv_model * inv_view; } } + #[derive(Clone)] pub struct Scene { pub nodes: Vec, @@ -51,94 +79,21 @@ impl Scene { cameras: Vec::new(), } } - fn add_node(&mut self, node: Node) { + pub fn add_node(&mut self, node: Node) { self.nodes.push(node); } - fn add_material(&mut self, material: Material) { + pub fn add_material(&mut self, material: Material) { self.materials.push(material); } - fn add_light(&mut self, light: Light) { + pub fn add_light(&mut self, light: Light) { self.lights.push(light); } - fn add_camera(&mut self, camera: Camera) { + pub fn add_camera(&mut self, camera: Camera) { self.cameras.push(camera); } - - pub fn from_rhai(script: &str) -> Result> { - let mut engine = Engine::new(); - - engine - .register_type::>() - .register_fn("V", Vector3::::new); - engine - .register_type::>() - .register_fn("P", Point3::::new); - engine - .register_type::() - .register_fn("Scene", Scene::empty) - .register_fn("addNode", Scene::add_node) - .register_fn("addLight", Scene::add_light); - - engine - .register_type::() - .register_fn("Node", Node::new) - .register_fn("translate", Node::translate) - .register_fn("rotate", Node::rotate) - .register_fn("scale", Node::scale); - engine - .register_type::() - .register_fn("Camera", Camera::new_sizeless); - engine - .register_type::() - .register_fn("Light", Light::new); - engine - .register_type::() - .register_fn("Material", Material::new) - .register_fn("MaterialRed", Material::red) - .register_fn("MaterialBlue", Material::blue) - .register_fn("MaterialGreen", Material::green) - .register_fn("MaterialMagenta", Material::magenta) - .register_fn("MaterialTurquoise", Material::turquoise); - engine - .register_type::() - .register_fn("Sphere", Sphere::new) - .register_fn("SphereUnit", Sphere::unit); - engine - .register_type::() - .register_fn("Cube", Cube::new) - .register_fn("CubeUnit", Cube::unit); - engine - .register_type::() - .register_fn("Cone", Cone::new) - .register_fn("ConeUnit", Cone::unit); - engine - .register_type::() - .register_fn("Cylinder", Cylinder::new); - engine - .register_type::() - .register_fn("Circle", Circle::new) - .register_fn("CircleUnit", Circle::unit); - engine - .register_type::() - .register_fn("Rectangle", Rectangle::new) - .register_fn("RectangleUnit", Rectangle::unit); - engine - .register_type::() - .register_fn("Steiner", SteinerSurface::new); - engine - .register_type::() - .register_fn("Torus", Torus::new); - engine - .register_type::() - .register_fn("Adam", AdamShape::new); - engine - .register_type::() - .register_fn("Adam2", AdamShape2::new); - engine - .register_type::() - .register_fn("Adam3", AdamShape3::new); - - let scene: Scene = engine.eval(script.into())?; - Ok(scene) + pub fn compute(&mut self, view: &Matrix4, inv_view: &Matrix4) { + for node in &mut self.nodes { + node.compute(view, inv_view); + } } } diff --git a/src/state.rs b/src/state.rs index 269343a..7eb28d5 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,6 +1,7 @@ //Use linear algebra module use crate::camera::Camera; +use crate::ray::Ray; use crate::{gui::Gui, scene::Scene}; use crate::{gui::GuiEvent, log_error}; @@ -35,6 +36,7 @@ pub struct State { pixels: Arc>, gui: Gui, + rays: Vec, ray_queue: Vec, } @@ -43,6 +45,7 @@ impl State { let scene = Scene::empty(); let window_size = window.inner_size(); let camera = Camera::unit(); + let rays = Vec::new(); Self { scene, @@ -52,6 +55,7 @@ impl State { buffer_height: window_size.height as u32, pixels: Arc::new(Mutex::new(pixels)), gui, + rays, ray_queue: Vec::new(), } } @@ -59,25 +63,33 @@ impl State { fn update(&mut self) -> Result<(), Box> { if let Some(event) = self.gui.event.take() { match event { - GuiEvent::BufferResize(proportion) => self.resize_buffer(proportion)?, - GuiEvent::CameraUpdate(camera) => self.set_camera(camera)?, + GuiEvent::BufferResize(proportion, fov) => { + self.resize_buffer(proportion, fov as f64)? + } + GuiEvent::CameraUpdate(camera) => { + self.camera = camera; + self.clear()?; + self.reset_queue(); + } GuiEvent::SceneLoad(scene) => { self.scene = scene; - self.clear()?; + self.reset_queue(); } } }; Ok(()) } - fn resize_buffer(&mut self, proportion: f32) -> Result<(), Box> { + fn resize_buffer(&mut self, proportion: f32, fovy: f64) -> Result<(), Box> { let size = self.window.inner_size(); self.buffer_width = (size.width as f32 * proportion) as u32; self.buffer_height = (size.height as f32 * proportion) as u32; - self.camera.set_size(self.buffer_width, self.buffer_height); self.clear()?; + self.reset_queue(); + + self.rays = Ray::cast_rays(fovy, self.buffer_width, self.buffer_height); let mut pixels = self.pixels.lock().unwrap(); pixels.resize_buffer(self.buffer_width, self.buffer_height)?; @@ -85,6 +97,9 @@ impl State { } fn resize(&mut self, size: &PhysicalSize) -> Result<(), Box> { + self.buffer_width = (size.width) as u32; + self.buffer_height = (size.height) as u32; + self.reset_queue(); let mut pixels = self.pixels.lock().unwrap(); pixels.resize_surface(size.width, size.height)?; Ok(()) @@ -105,7 +120,7 @@ impl State { //Get random index from queue let index = self.ray_queue.pop().unwrap(); //Shade colour for selected ray - let colour = &self.camera.rays[index].shade_ray(&self.scene); + let colour = &self.rays[index].shade_ray(&self.scene); //Assign colour to frame let rgba = colour.map_or(COLOUR_CLEAR, |colour| [colour.x, colour.y, colour.z, 255]); let mut pixels = self.pixels.lock().unwrap(); @@ -128,19 +143,12 @@ impl State { Ok(()) } - fn set_camera(&mut self, camera: Camera) -> Result<(), Box> { - self.clear()?; - self.reset_queue(); - self.camera = camera; - self.camera.set_size(self.buffer_width, self.buffer_height); - Ok(()) - } - fn reset_queue(&mut self) { let size = self.buffer_height as usize * self.buffer_width as usize; let mut ray_queue: Vec = (0..size).collect(); ray_queue.shuffle(&mut thread_rng()); self.ray_queue = ray_queue; + self.scene.compute(&self.camera.view, &self.camera.inv_view); } fn render(&mut self) -> Result<(), Box> { @@ -171,7 +179,7 @@ pub fn run() -> Result<(), Box> { let gui = Gui::new(&window, &pixels); let mut state = State::new(window, pixels, gui); - state.resize_buffer(1.0)?; + state.resize_buffer(1.0, 90.0)?; event_loop.run(move |event, _, control_flow| { state.gui.handle_event(&state.window, &event); @@ -186,6 +194,7 @@ pub fn run() -> Result<(), Box> { *control_flow = ControlFlow::Exit; } } + Event::RedrawRequested(_) => { if let Err(_e) = state.render() { *control_flow = ControlFlow::Exit; @@ -193,7 +202,7 @@ pub fn run() -> Result<(), Box> { } _ => state.window.request_redraw(), } - }); + }) } fn create_window(event_loop: &EventLoop<()>) -> Window {