From f31ebb06bda00b2124b3457b0bf93ae4d25bb92e Mon Sep 17 00:00:00 2001 From: STP Date: Sun, 19 Nov 2023 15:33:04 -0500 Subject: [PATCH] Working scripting --- Cargo.toml | 2 +- src/main.rs | 114 ++++++++++++++++++++-------------------- src/primitive.rs | 97 +++++++++++++++++----------------- src/raytracer.rs | 23 ++++----- src/scene.rs | 132 +++++++++++++++++++++++++++++++++++++++++------ 5 files changed, 236 insertions(+), 132 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2b4c8e6..5fb63bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,4 +20,4 @@ winit = "0.27" winit_input_helper = "0.13" pixels = "0.13" error-iter = "0.4.1" -rhai = "1.16.3" +rhai = {version = "1.16.3", features=["f32_float"]} diff --git a/src/main.rs b/src/main.rs index 033c3bf..1b772ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,20 +39,19 @@ const EPSILON_VECTOR: Vector3 = Vector3::new(EPSILON, EPSILON, EPSILON); const INFINITY_VECTOR: Vector3 = Vector3::new(INFINITY, INFINITY, INFINITY); struct State { - scene: Arc, + scene: Scene, window: Window, pixels: Arc>, gui: Gui, index: usize, - camera: Camera, rays: Arc>, } impl State { /// Create a new `World` instance that can draw a moving box. - fn new(window: Window, scene: Scene, camera: Camera) -> Self { + fn new(window: Window, scene: Scene) -> Self { let window_size = window.inner_size(); let pixels = { let surface_texture = @@ -65,14 +64,15 @@ impl State { .unwrap() }; let gui = Gui::new(&window, &pixels); - let rays = camera.cast_rays(window_size.width, window_size.height); + let rays = scene + .camera + .cast_rays(window_size.width, window_size.height); Self { - scene: Arc::new(scene), + scene, window, pixels: Arc::new(Mutex::new(pixels)), gui, - camera, index: 0, rays: Arc::new(rays), } @@ -92,8 +92,8 @@ impl State { return false; } self.index = 0; - self.camera.set_position(self.gui.camera_eye); - self.rays = Arc::new(self.camera.cast_rays(width_new, height_new)); + self.scene.camera.set_position(self.gui.camera_eye); + self.rays = Arc::new(self.scene.camera.cast_rays(width_new, height_new)); } true } @@ -201,55 +201,57 @@ impl State { fn main() -> Result<(), Error> { env_logger::init(); env::set_var("RUST_BACKTRACE", "1"); + Scene::init("test.rhai").expect("Could not read lua file"); + //Window let event_loop = EventLoop::new(); //SCENE - //Camera - let eye = Point3::new(10.0, 0.0, 10.0); - let target = Point3::new(0.0, 0.0, 0.0); - let up = Vector3::new(0.0, 1.0, 0.0); - let camera = Camera::new( - eye, - target, - up, - 90.0, - (START_WIDTH as f32 / START_HEIGHT as f32) as f32, - ); - // SETUP MATERIALS - let magenta = Arc::new(Material::magenta()); - let blue = Arc::new(Material::blue()); - let turquoise = Arc::new(Material::turquoise()); - let red = Arc::new(Material::red()); - // SETUP PRIMITIVES - let mut primitives: Vec> = Vec::new(); - //let cube = Box::new(Cube::unit(turquoise.clone())); - // primitives.push(cube); - let sphere = Box::new(Sphere::new(Point3::new(0.0, -2.0, 0.0), 1.0, red.clone())); - let sphere2 = Box::new(Sphere::new(Point3::new(0.0, 2.0, 0.0), 1.0, red.clone())); - let cone = Box::new(Cone::new(1.0, 1.0, -1.0, magenta.clone())); - primitives.push(sphere); - primitives.push(sphere2); - primitives.push(cone); - - // SETUP LIGHTS - let light_pos = Point3::new(0.0, 12.0, 4.0); - let light_colour = Vector3::new(0.4, 0.4, 0.6); - let light_falloff = [1.0, 0.00, 0.00]; - let light = Light::new(light_colour, light_pos, light_falloff); - - let light_pos2 = Point3::new(10.0, 12.0, -4.0); - let light_colour2 = Vector3::new(0.4, 0.0, 0.6); - let light_falloff2 = [1.0, 0.00, 0.00]; - let light2 = Light::new(light_colour2, light_pos2, light_falloff2); - - let ambient_light = Vector3::new(0.0, 0.0, 0.2); - - let scene = Scene::new( - primitives, - vec![light, light2], - vec![camera.clone()], - ambient_light, - ); + // //Camera + // let eye = Point3::new(10.0, 0.0, 10.0); + // let target = Point3::new(0.0, 0.0, 0.0); + // let up = Vector3::new(0.0, 1.0, 0.0); + // let camera = Camera::new( + // eye, + // target, + // up, + // 90.0, + // (START_WIDTH as f32 / START_HEIGHT as f32) as f32, + // ); + // // SETUP MATERIALS + // let magenta = Arc::new(Material::magenta()); + // let blue = Arc::new(Material::blue()); + // let turquoise = Arc::new(Material::turquoise()); + // let red = Arc::new(Material::red()); + // // SETUP PRIMITIVES + // let mut primitives: Vec> = Vec::new(); + // //let cube = Box::new(Cube::unit(turquoise.clone())); + // // primitives.push(cube); + // let sphere = Box::new(Sphere::new(Point3::new(0.0, -2.0, 0.0), 1.0, red.clone())); + // let sphere2 = Box::new(Sphere::new(Point3::new(0.0, 2.0, 0.0), 1.0, red.clone())); + // let cone = Box::new(Cone::new(1.0, 1.0, -1.0, magenta.clone())); + // primitives.push(sphere); + // primitives.push(sphere2); + // primitives.push(cone); + // + // // SETUP LIGHTS + // let light_pos = Point3::new(0.0, 12.0, 4.0); + // let light_colour = Vector3::new(0.4, 0.4, 0.6); + // let light_falloff = [1.0, 0.00, 0.00]; + // let light = Light::new(light_colour, light_pos, light_falloff); + // + // let light_pos2 = Point3::new(10.0, 12.0, -4.0); + // let light_colour2 = Vector3::new(0.4, 0.0, 0.6); + // let light_falloff2 = [1.0, 0.00, 0.00]; + // let light2 = Light::new(light_colour2, light_pos2, light_falloff2); + // + // let ambient_light = Vector3::new(0.0, 0.0, 0.2); + // + // let scene = Scene::new( + // primitives, + // vec![light, light2], + // vec![camera.clone()], + // ambient_light, + // ); //State let window = { let size = LogicalSize::new(START_WIDTH, START_HEIGHT); @@ -260,7 +262,8 @@ fn main() -> Result<(), Error> { .build(&event_loop) .unwrap() }; - let mut state = State::new(window, scene, camera); + let scene = Scene::empty(); + let mut state = State::new(window, scene); event_loop.run(move |event, _, control_flow| { // Draw the current frame @@ -269,6 +272,7 @@ fn main() -> Result<(), Error> { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => { *control_flow = ControlFlow::Exit; + return; } WindowEvent::Resized(size) => { state.resize(&size); diff --git a/src/primitive.rs b/src/primitive.rs index f789ae4..0b0b062 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -15,38 +15,38 @@ pub struct Material { pub shininess: f32, } impl Material { - pub fn new(kd: Vector3, ks: Vector3, shininess: f32) -> Self { - Material { kd, ks, shininess } + pub fn new(kd: Vector3, ks: Vector3, shininess: f32) -> Arc { + Arc::new(Material { kd, ks, shininess }) } - pub fn magenta() -> Self { + pub fn magenta() -> Arc { let kd = Vector3::new(1.0, 0.0, 1.0); let ks = Vector3::new(1.0, 0.0, 1.0); let shininess = 0.5; - Material { kd, ks, shininess } + Arc::new(Material { kd, ks, shininess }) } - pub fn turquoise() -> Self { + pub fn turquoise() -> Arc { let kd = Vector3::new(0.25, 0.3, 0.7); let ks = Vector3::new(0.25, 0.3, 0.7); let shininess = 0.5; - Material { kd, ks, shininess } + Arc::new(Material { kd, ks, shininess }) } - pub fn red() -> Self { + pub fn red() -> Arc { let kd = Vector3::new(0.8, 0.0, 0.3); let ks = Vector3::new(0.8, 0.3, 0.0); let shininess = 0.5; - Material { kd, ks, shininess } + Arc::new(Material { kd, ks, shininess }) } - pub fn blue() -> Self { + pub fn blue() -> Arc { let kd = Vector3::new(0.0, 0.3, 0.6); let ks = Vector3::new(0.3, 0.0, 0.6); let shininess = 0.5; - Material { kd, ks, shininess } + Arc::new(Material { kd, ks, shininess }) } - pub fn green() -> Self { + pub fn green() -> Arc { let kd = Vector3::new(0.0, 1.0, 0.0); let ks = Vector3::new(0.0, 1.0, 0.0); let shininess = 0.5; - Material { kd, ks, shininess } + Arc::new(Material { kd, ks, shininess }) } } // INTERSECTION ----------------------------------------------------------------- @@ -122,20 +122,20 @@ pub struct Sphere { } impl Sphere { - pub fn new(position: Point3, radius: f32, material: Arc) -> Self { + pub fn new(position: Point3, radius: f32, material: Arc) -> Arc { let radius_vec = Vector3::new(radius, radius, radius); let bln = position - radius_vec; let trf = position + radius_vec; let bounding_box = BoundingBox::new(bln, trf); - Sphere { + Arc::new(Sphere { position, radius, bounding_box, material, - } + }) } - pub fn unit(material: Arc) -> Self { + pub fn unit(material: Arc) -> Arc { Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0, material) } } @@ -204,21 +204,21 @@ impl Circle { radius: f32, normal: Vector3, material: Arc, - ) -> Self { + ) -> Arc { let radius_vec = Vector3::new(radius, radius, radius); let bln = position - radius_vec; let trf = position + radius_vec; let bounding_box = BoundingBox::new(bln, trf); - Circle { + Arc::new(Circle { position, radius, normal: normal.normalize(), material, bounding_box, - } + }) } - pub fn unit(material: Arc) -> Self { + pub fn unit(material: Arc) -> Arc { let position = Point3::new(0.0, 0.0, 0.0); let normal = Vector3::new(0.0, 1.0, 0.0); let radius = 1.0; @@ -228,13 +228,13 @@ impl Circle { let trf = Point3::new(radius, 0.0, EPSILON); let bounding_box = BoundingBox { bln, trf }; - Circle { + Arc::new(Circle { position, normal, radius, material, bounding_box, - } + }) } } @@ -303,13 +303,13 @@ pub struct Cone { radius: f32, base: f32, apex: f32, - circle: Circle, + circle: Arc, material: Arc, bounding_box: BoundingBox, } impl Cone { - pub fn new(radius: f32, apex: f32, base: f32, material: Arc) -> Self { + pub fn new(radius: f32, apex: f32, base: f32, material: Arc) -> Arc { let circle = Circle::new( Point3::new(0.0, base, 0.0), radius, @@ -318,16 +318,16 @@ impl Cone { ); let bln = Point3::new(-radius, base, -radius); let trf = Point3::new(radius, base + apex, radius); - Cone { + Arc::new(Cone { radius: radius / 2.0, base, apex, circle, material, bounding_box: BoundingBox { bln, trf }, - } + }) } - pub fn unit(material: Arc) -> Self { + pub fn unit(material: Arc) -> Arc { Cone::new(1.0, 2.0, -1.0, material) } @@ -431,13 +431,13 @@ impl Rectangle { width: f32, height: f32, material: Arc, - ) -> Self { + ) -> Arc { let normal = normal.normalize(); let width_direction = width_direction.normalize(); let height_direction = width_direction.cross(&normal); let bln = position - width / 2.0 * width_direction - height / 2.0 * height_direction; let trf = position + width / 2.0 * width_direction + height / 2.0 * height_direction; - Rectangle { + Arc::new(Rectangle { position, normal: normal.normalize(), width_direction: width_direction.normalize(), @@ -445,9 +445,9 @@ impl Rectangle { height, material, bounding_box: BoundingBox { bln, trf }, - } + }) } - pub fn unit(material: Arc) -> Self { + pub fn unit(material: Arc) -> Arc { Rectangle::new( Point3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 1.0, 0.0), @@ -510,18 +510,18 @@ pub struct Cube { } impl Cube { - fn new(width: f32, height: f32, depth: f32, material: Arc) -> Self { + fn new(width: f32, height: f32, depth: f32, material: Arc) -> Arc { let trf = Point3::new(width / 2.0, height / 2.0, depth / 2.0); let bln = Point3::new(-width / 2.0, -height / 2.0, -depth / 2.0); - Cube { + Arc::new(Cube { width, height, depth, material, bounding_box: BoundingBox { bln, trf }, - } + }) } - pub fn unit(material: Arc) -> Self { + pub fn unit(material: Arc) -> Arc { Cube::new(2.0, 2.0, 2.0, material) } } @@ -599,23 +599,28 @@ struct Triangle { } impl Triangle { - fn new(u: Point3, v: Point3, w: Point3, material: Arc) -> Self { + fn new( + u: Point3, + v: Point3, + w: Point3, + material: Arc, + ) -> Arc { let uv = v - u; let uw = w - u; let normal = uv.cross(&uw).normalize(); let bln = u.inf(&v).inf(&w); let trf = u.sup(&v).sup(&w); let bounding_box = BoundingBox { bln, trf }; - Triangle { + Arc::new(Triangle { u, v, w, normal, material, bounding_box, - } + }) } - pub fn unit(material: Arc) -> Self { + pub fn unit(material: Arc) -> Arc { let u = Point3::new(-1.0, 0.0, -1.0); let v = Point3::new(0.0, 0.0, 1.0); let w = Point3::new(1.0, 0.0, -1.0); @@ -675,24 +680,24 @@ impl Primitive for Triangle { // MESH ----------------------------------------------------------------- struct Mesh { - triangles: Vec, + triangles: Vec>, material: Arc, bounding_box: BoundingBox, } impl Mesh { - fn new(triangles: Vec, material: Arc) -> Self { + fn new(triangles: Vec>, material: Arc) -> Arc { // Calculate the bounding box for the entire mesh based on the bounding boxes of individual triangles let bounding_box = Mesh::compute_bounding_box(&triangles); - Mesh { + Arc::new(Mesh { triangles, material, bounding_box, - } + }) } - fn compute_bounding_box(triangles: &Vec) -> BoundingBox { + fn compute_bounding_box(triangles: &Vec>) -> BoundingBox { let mut bln = Point3::new(INFINITY, INFINITY, INFINITY); let mut trf = -bln; for triangle in triangles { @@ -707,8 +712,8 @@ impl Mesh { BoundingBox { bln, trf } } - fn from_file(filename: &str, material: Arc) -> Self { - let mut triangles: Vec = Vec::new(); + fn from_file(filename: &str, material: Arc) -> Arc { + let mut triangles: Vec> = Vec::new(); let mut vertices: Vec> = Vec::new(); let file = File::open(filename).expect("Failed to open file"); diff --git a/src/raytracer.rs b/src/raytracer.rs index 94c0feb..64387eb 100644 --- a/src/raytracer.rs +++ b/src/raytracer.rs @@ -2,9 +2,10 @@ use crate::{ light::Light, primitive::{Intersection, Primitive}, ray::Ray, - scene::Scene, + scene::*, INFINITY, }; +use std::collections::HashMap; use std::sync::Arc; use nalgebra::{distance, Matrix4, Point3, Unit, Vector3, Vector4}; @@ -16,7 +17,7 @@ pub fn shade_rays(scene: &Scene, rays: &Vec, width: i32, height: i32) -> Ve let mut pixel_data = vec![Vector3::new(0, 0, 0); (width * height) as usize]; for (pixel_index, ray) in rays.iter().enumerate() { - let intersect = get_closest_intersection(&scene.primitives, ray); + let intersect = get_closest_intersection(&scene.nodes, ray); let colour = match intersect { Some(intersect) => phong_shade_point(scene, &intersect), None => { @@ -30,7 +31,7 @@ pub fn shade_rays(scene: &Scene, rays: &Vec, width: i32, height: i32) -> Ve } //Shade a single ray pub fn shade_ray(scene: &Scene, ray: &Ray) -> Option> { - let intersect = get_closest_intersection(&scene.primitives, ray); + let intersect = get_closest_intersection(&scene.nodes, ray); match intersect { Some(intersect) => Some(phong_shade_point(&scene, &intersect)), None => None, @@ -38,15 +39,12 @@ pub fn shade_ray(scene: &Scene, ray: &Ray) -> Option> { } // Find the closest intersection, given a ray in world coordinates -pub fn get_closest_intersection( - primitives: &Vec>, - ray: &Ray, -) -> Option { +pub fn get_closest_intersection(nodes: &Vec, ray: &Ray) -> Option { let mut closest_distance = INFINITY; let mut closest_intersect: Option = None; - for arc_primitive in primitives { - let primitive = arc_primitive.clone(); + for node in nodes { + let primitive = node.primitive.clone(); if let Some(intersect) = primitive.intersect_ray(ray) { if intersect.distance < closest_distance { @@ -70,8 +68,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3 material, .. } = intersect; - let binding = scene.ambient_light.clone(); - let ambient_light = binding.as_ref(); + let ambient_light = &scene.ambient_light; let kd = material.kd; let ks = material.ks; let shininess = material.shininess; @@ -80,9 +77,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3 // Let us first compute the ambient light component and set it as out base colour let mut colour = kd.component_mul(ambient_light); - for arc_light in scene.lights.as_ref() { - let light = arc_light.clone(); - + for light in &scene.lights { let Light { position: light_position, colour: light_colour, diff --git a/src/scene.rs b/src/scene.rs index 3195be2..8180ba4 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,29 +1,129 @@ use crate::camera::Camera; use crate::light::Light; use crate::primitive::Primitive; -use nalgebra::Vector3; +use crate::primitive::*; +use nalgebra::{Matrix4, Point3, Vector3}; +use rhai::{Engine, EvalAltResult}; +use std::collections::HashMap; use std::sync::Arc; +const LIGHT_AMBIENT: f32 = 0.2; + +#[derive(Clone)] +pub struct Node { + pub primitive: Arc, + pub model_transform: Matrix4, +} + +impl Node { + pub fn new(primitive: Arc) -> Self { + Node { + primitive, + model_transform: 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; + let rotation_matrix = Matrix4::from_euler_angles(roll, pitch, yaw); + self.model_transform = rotation_matrix * self.model_transform; + } + pub fn translate(&mut self, translation: &Vector3) { + let translation_matrix = Matrix4::new_translation(translation); + self.model_transform = translation_matrix * self.model_transform; + } + pub fn scale(&mut self, scale: &Vector3) { + let scale_matrix = Matrix4::new_nonuniform_scaling(scale); + self.model_transform = scale_matrix * self.model_transform; + } +} +#[derive(Clone)] pub struct Scene { - pub primitives: Arc>>, - pub lights: Arc>, - pub cameras: Arc>, - pub ambient_light: Arc>, + pub nodes: Vec, + pub materials: Vec, + pub lights: Vec, + pub cameras: Vec, + pub ambient_light: Vector3, + pub camera: Camera, } impl Scene { - // Creates a scene - pub fn new( - primitives: Vec>, - lights: Vec, - cameras: Vec, - ambient_light: Vector3, - ) -> Self { + // Creates an emptry scene + pub fn empty() -> Self { Scene { - primitives: Arc::new(primitives), - lights: Arc::new(lights), - cameras: Arc::new(cameras), - ambient_light: Arc::new(ambient_light), + nodes: Vec::new(), + materials: Vec::new(), + lights: Vec::new(), + cameras: Vec::new(), + camera: Camera::new( + Point3::new(0.0, 0.0, -10.0), + Point3::new(0.0, 0.0, 0.0), + Vector3::new(0.0, 0.0, -1.0), + 120.0, + 1.0, + ), + ambient_light: Vector3::new(LIGHT_AMBIENT, LIGHT_AMBIENT, LIGHT_AMBIENT), } } + fn add_node(&mut self, node: Node) { + self.nodes.push(node); + } + fn add_material(&mut self, material: Material) { + self.materials.push(material); + } + fn add_light(&mut self, light: Light) { + self.lights.push(light); + } + fn add_camera(&mut self, camera: Camera) { + self.cameras.push(camera); + } + fn set_ambient(&mut self, intensity: Vector3) { + self.ambient_light = intensity; + } + fn set_camera(&mut self, camera: Camera) { + self.camera = camera; + } + + fn get_camera(&self) -> &Camera { + &self.camera + } + fn get_ambient(&self) -> Arc> { + Arc::new(self.ambient_light) + } + pub fn init(filename: &str) -> Result<(), Box> { + 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); + 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); + engine + .register_type::() + .register_fn("Light", Light::new); + engine + .register_type::() + .register_fn("Material", Material::new); + engine + .register_type::() + .register_fn("Sphere", Sphere::new); + + engine.run_file(filename.into())?; + Ok(()) + } }