From e48bc02daa4ca700c62809fd83cf6042ca2fdd15 Mon Sep 17 00:00:00 2001 From: STP Date: Sun, 26 Nov 2023 20:49:09 -0500 Subject: [PATCH] Added scene editor --- scene.rhai => scripts/scene.rhai | 30 ++++---- src/camera.rs | 2 +- src/gui.rs | 120 ++++++++++++++++++++----------- src/light.rs | 4 +- src/primitive.rs | 1 - src/ray.rs | 5 +- src/raytracer.rs | 4 +- src/scene.rs | 57 +++++++-------- src/state.rs | 5 +- 9 files changed, 133 insertions(+), 95 deletions(-) rename scene.rhai => scripts/scene.rhai (63%) diff --git a/scene.rhai b/scripts/scene.rhai similarity index 63% rename from scene.rhai rename to scripts/scene.rhai index cacc15e..259a609 100644 --- a/scene.rhai +++ b/scripts/scene.rhai @@ -1,23 +1,27 @@ let scene = Scene(); -let material = Material(V(0.2,0.2,0.2), V(0.2, 0.8, 0.8), 10.0); +let camera = Camera( P(0.0,1.0,1.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0)); +scene.addCamera("camera", camera); -//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 material = Material(V(0.2,0.2,0.2), V(0.2, 0.8, 0.8), 10.0); +scene.addMaterial("bluegreen", material); let light = Light(P(0.0,7.0,0.0), V(0.0,0.0,1.0), V(0.1, 0.01, 0.001)); -scene.addLight(light); -let light = Light(P(2.0,7.0,0.0), V(0.0,1.0,0.0), V(0.1, 0.01, 0.001)); -scene.addLight(light); -let light = Light(P(-2.0,7.0,0.0), V(1.0,0.0,0.0), V(0.1, 0.01, 0.001)); -scene.addLight(light); +scene.addLight("blue", light); + +let light = Light( P(2.0,7.0,0.0), V(0.0,1.0,0.0), V(0.1, 0.01, 0.001)); +scene.addLight("green", light); + +let light = Light( P(-2.0,7.0,0.0), V(1.0,0.0,0.0), V(0.1, 0.01, 0.001)); +scene.addLight("red", light); + let light = Ambient(V(0.1,0.1,0.1)); -scene.addLight(light); +scene.addLight("ambient", light); let sphere = Sphere(P(0.0,0.0,0.0), 1.0, material); let sphere_node = Node(sphere); - scene.addNode(sphere_node); + scene.addNode("sphere", sphere_node); // for i in 0..6 { // let sphere = Sphere(P(0.0,0.0,0.0), 2.0, material); @@ -33,9 +37,9 @@ let sphere_node = Node(sphere); // 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", gnonom_node); // let cylinder = Cylinder(10.0,1.0, material); // let cylinder_node = Node(cylinder); diff --git a/src/camera.rs b/src/camera.rs index 74b1017..1105200 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -27,7 +27,7 @@ impl Camera { /// Create a unit camera with default parameters pub fn unit() -> Self { - let eye = Point3::new(0.0, 0.0, 1.0); + let eye = Point3::new(0.0, 0.0, 2.0); let target = Point3::new(0.0, 0.0, 0.0); let up = Vector3::new(0.0, 1.0, 0.0); Camera::new(eye, target, up) diff --git a/src/gui.rs b/src/gui.rs index 82c7f81..2ce4937 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -4,7 +4,6 @@ use crate::{ primitive::*, scene::{Node, Scene}, state::{INIT_FILE, SAVE_FILE}, - EPSILON, }; use imgui::*; use nalgebra::{Point3, Vector3}; @@ -12,16 +11,42 @@ use pixels::{wgpu, PixelsContext}; use rhai::Engine; use std::time::Instant; +//BUFFER CONSTANTS const BUFFER_PROPORTION_INIT: f32 = 0.2; const BUFFER_PROPORTION_MIN: f32 = 0.1; const BUFFER_PROPORTION_MAX: f32 = 1.0; +//RAY CONSTANTS const RAYS_INIT: i32 = 7000; const RAYS_MIN: i32 = 100; const RAYS_MAX: i32 = 30000; -const CAMERA_MIN_FOV: f32 = 10.0; -const CAMERA_MAX_FOV: f32 = 160.0; +//MATERIAL CONSTANTS +const MIN_D: f32 = 0.0; +const MIN_S: f32 = 0.0; +const MIN_SHINE: f32 = 0.0; +const MAX_D: f32 = 1.0; +const MAX_S: f32 = 1.0; +const MAX_SHINE: f32 = 50.0; + +//TRANSFORMATION CONSTANTS +const MIN_COLOUR: f32 = 0.0; +const MIN_FALLOFF: f32 = 0.0; +const MIN_SCALE: f64 = 0.0; +const MIN_POSITION: f64 = -10.0; +const MIN_ROTATION: f64 = -180.0; +const MIN_TRANSLATE: f64 = -10.0; +//-- +const MAX_COLOUR: f32 = 1.0; +const MAX_FALLOFF: f32 = 1.0; +const MAX_SCALE: f64 = 3.0; +const MAX_POSITION: f64 = 10.0; +const MAX_ROTATION: f64 = 180.0; +const MAX_TRANSLATE: f64 = 10.0; + +// CAMERA CONSTANTS +const MIN_FOV: f32 = 10.0; +const MAX_FOV: f32 = 160.0; const CAMERA_INIT: f32 = 5.0; /// Manages all state required for rendering Dear ImGui over `Pixels`test. @@ -49,9 +74,7 @@ pub struct Gui { buffer_proportion: f32, - camera_eye: [f32; 3], - camera_target: [f32; 3], - camera_up: [f32; 3], + camera: Camera, camera_fov: f32, image_filename: String, @@ -113,9 +136,7 @@ impl Gui { ray_num: RAYS_INIT, buffer_proportion: BUFFER_PROPORTION_INIT, - camera_eye: [CAMERA_INIT, CAMERA_INIT, CAMERA_INIT], - camera_target: Vector3::zeros().into(), - camera_up: Vector3::y().into(), + camera: Camera::unit(), camera_fov: 110.0, image_filename: String::from(SAVE_FILE), @@ -170,7 +191,7 @@ impl Gui { &mut self.buffer_proportion, ); // Fov of the buffer - ui.slider("fov", CAMERA_MIN_FOV, CAMERA_MAX_FOV, &mut self.camera_fov); + ui.slider("fov", MIN_FOV, MAX_FOV, &mut self.camera_fov); // Apply stored changes if ui.button("Apply") { self.event = Some(GuiEvent::BufferResize( @@ -183,21 +204,15 @@ impl Gui { if CollapsingHeader::new("Camera").build(ui) { // Eye, target and up vector inputs ui.text("Camera options:"); - 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_config("Eye", MIN_TRANSLATE, MAX_TRANSLATE) + .build_array(self.camera.eye.coords.as_mut_slice()); + ui.slider_config("Target", MIN_TRANSLATE, MAX_TRANSLATE) + .build_array(self.camera.target.coords.as_mut_slice()); + ui.slider_config("Up", 0.0, 1.0) + .build_array(self.camera.up.as_mut_slice()); if ui.button("Apply Camera") { - println!("Camera changed: {:?}", self.camera_eye); - let (eye, target, up) = (&self.camera_eye, &self.camera_target, &self.camera_up); - 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), - ); - self.event = Some(GuiEvent::CameraUpdate(camera, self.camera_fov)); + println!("Camera changed"); + self.event = Some(GuiEvent::CameraUpdate(self.camera.clone(), self.camera_fov)); } } // SCRIPTING -------------------------------------------- @@ -249,39 +264,58 @@ impl Gui { // SCENE -------------------------------------------- if CollapsingHeader::new("Scene").build(ui) { if ui.button("Update Scene") { - for node in &mut self.scene.nodes { + for (_, node) in &mut self.scene.nodes { node.compute(); } self.event = Some(GuiEvent::SceneLoad(self.scene.clone())); } // Edit transformation of nodes if let Some(_t) = ui.tree_node("Nodes") { - for node in &mut self.scene.nodes { - ui.text("node"); - ui.slider_config("Translation", -10.0, 10.0) - .build_array(&mut node.translation); - ui.slider_config("Rotation", -180.0, 180.0) - .build_array(&mut node.rotation); - ui.slider_config("Scale", -10.0, 10.0) - .build_array(&mut node.scale); + for (label, node) in &mut self.scene.nodes { + if let Some(_t) = ui.tree_node(label) { + ui.slider_config("Translation", MIN_TRANSLATE, MAX_TRANSLATE) + .build_array(&mut node.translation); + ui.slider_config("Rotation", MIN_ROTATION, MAX_ROTATION) + .build_array(&mut node.rotation); + ui.slider_config("Scale", MIN_SCALE, MAX_SCALE) + .build_array(&mut node.scale); + } } } + // Edit materials + // if let Some(_t) = ui.tree_node("Materials") { + // for (label, material) in &mut self.scene.materials { + // if let Some(_t) = ui.tree_node(label) { + // ui.slider_config("ks", MIN_D, MIN_D) + // .build_array(material.ks.as_mut_slice()); + // ui.slider_config("kd", MIN_S, MAX_S) + // .build_array(material.kd.as_mut_slice()); + // ui.slider("fov", MIN_SHINE, MAX_SHINE, &mut material.shininess); + // } + // } + // } //Edit color, position and falloff of lights if let Some(_t) = ui.tree_node("Lights") { - for light in &mut self.scene.lights { - ui.slider_config("Colour", 0.0, 1.0) - .build_array(light.colour.as_mut_slice()); - ui.slider_config("Position", -10.0, 10.0) - .build_array(light.position.coords.as_mut_slice()); - ui.slider_config("Falloff", 0.0, f32::MAX) - .build_array(light.falloff.as_mut_slice()); + for (label, light) in &mut self.scene.lights { + if let Some(_t) = ui.tree_node(label) { + ui.slider_config("Colour", MIN_COLOUR, MAX_COLOUR) + .build_array(light.colour.as_mut_slice()); + ui.slider_config("Position", MIN_TRANSLATE, MAX_TRANSLATE) + .build_array(light.position.coords.as_mut_slice()); + ui.slider_config("Falloff", MIN_FALLOFF, MAX_FALLOFF) + .build_array(light.falloff.as_mut_slice()); + } } } //Use different cameras in the scene if let Some(_t) = ui.tree_node("Cameras") { - for camera in &self.scene.cameras { - if ui.button("Use camera") { - GuiEvent::CameraUpdate(camera.clone(), self.camera_fov); + for (label, camera) in &self.scene.cameras { + if let Some(_t) = ui.tree_node(label) { + if ui.button("Use camera") { + self.camera = camera.clone(); + self.event = + Some(GuiEvent::CameraUpdate(camera.clone(), self.camera_fov)); + } } } } diff --git a/src/light.rs b/src/light.rs index 82d8f85..a2087ab 100644 --- a/src/light.rs +++ b/src/light.rs @@ -9,7 +9,7 @@ pub struct Light { } impl Light { - pub fn new(position: Point3, colour: Vector3, falloff: Vector3) -> Self { + pub fn new(position: Point3, colour: Vector3, falloff: Vector3) -> Light { let colour = colour.cast(); let falloff = falloff.cast(); Light { @@ -19,7 +19,7 @@ impl Light { ambient: false, } } - pub fn ambient(colour: Vector3) -> Self { + pub fn ambient(colour: Vector3) -> Light { Light { position: Point3::new(0.0, 0.0, 0.0), colour: colour.cast(), diff --git a/src/primitive.rs b/src/primitive.rs index d212e66..299f3c3 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -23,7 +23,6 @@ impl Material { } 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; Arc::new(Material { kd, ks, shininess }) diff --git a/src/ray.rs b/src/ray.rs index a726f8d..9193092 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -4,6 +4,7 @@ use crate::{ scene::{Node, Scene}, }; use nalgebra::{Matrix4, Point3, Vector3}; +use std::collections::HashMap; #[derive(Clone)] // Ray struct represents a ray in 3D space with a starting point 'a' and a direction 'b' @@ -43,12 +44,12 @@ impl Ray { } // Find the closest intersection - pub fn get_closest_intersection(&self, nodes: &Vec) -> Option { + pub fn get_closest_intersection(&self, nodes: &HashMap) -> Option { //Assign no intersection let mut closest_distance = f64::MAX; let mut closest_intersect: Option = None; - for node in nodes { + for (_, node) in nodes { // Clone arc to primitive let primitive = node.primitive.clone(); // Transform ray into local model cordinates diff --git a/src/raytracer.rs b/src/raytracer.rs index f367dc0..1c68dfd 100644 --- a/src/raytracer.rs +++ b/src/raytracer.rs @@ -19,7 +19,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3 // Compute the ambient light component and set it as base colour let mut colour = Vector3::zeros(); - for light in &scene.lights { + for (_, light) in &scene.lights { let Light { position: light_position, colour: light_colour, @@ -73,7 +73,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3 } fn light_blocked(scene: &Scene, ray: Ray) -> bool { - for node in &scene.nodes { + 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() { diff --git a/src/scene.rs b/src/scene.rs index fee510b..6644b4c 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -2,15 +2,16 @@ use crate::camera::Camera; use crate::light::Light; use crate::primitive::*; use nalgebra::{Matrix4, Vector3}; +use std::collections::HashMap; use std::sync::Arc; #[derive(Clone)] pub struct Node { //Primitive pub primitive: Arc, //Transformations - pub rotation: [f32; 3], - pub scale: [f32; 3], - pub translation: [f32; 3], + pub rotation: [f64; 3], + pub scale: [f64; 3], + pub translation: [f64; 3], //Model matricies pub model: Matrix4, pub inv_model: Matrix4, @@ -43,27 +44,27 @@ impl Node { let yaw = yaw.to_radians(); // Add the roll, pitch, and yaw to the current rotation - self.rotation[0] += roll as f32; - self.rotation[1] += pitch as f32; - self.rotation[2] += yaw as f32; + self.rotation[0] += roll; + self.rotation[1] += pitch; + self.rotation[2] += yaw; // Recompute the model and inverse model matrices self.compute(); } // Translate a mesh by adding to its current position pub fn translate(&mut self, x: f64, y: f64, z: f64) { - self.translation[0] += x as f32; - self.translation[1] += y as f32; - self.translation[2] += z as f32; + self.translation[0] += x; + self.translation[1] += y; + self.translation[2] += z; // Recompute the model and inverse model matrices self.compute(); } // Scale a mesh by adding to its current scale pub fn scale(&mut self, x: f64, y: f64, z: f64) { - self.scale[0] += x as f32; - self.scale[1] += y as f32; - self.scale[2] += z as f32; + self.scale[0] += x; + self.scale[1] += y; + self.scale[2] += z; // Recompute the model and inverse model matrices self.compute(); @@ -88,36 +89,36 @@ impl Node { #[derive(Clone)] pub struct Scene { - pub nodes: Vec, - pub materials: Vec, - pub lights: Vec, - pub cameras: Vec, + pub nodes: HashMap, + pub materials: HashMap>, + pub lights: HashMap, + pub cameras: HashMap, } impl Scene { // Creates an emptry scene pub fn empty() -> Self { Scene { - nodes: Vec::new(), - materials: Vec::new(), - lights: Vec::new(), - cameras: Vec::new(), + nodes: HashMap::new(), + materials: HashMap::new(), + lights: HashMap::new(), + cameras: HashMap::new(), } } // Adds a node to the scene - pub fn add_node(&mut self, node: Node) { - self.nodes.push(node); + pub fn add_node(&mut self, label: String, node: Node) { + self.nodes.insert(label, node); } // Adds a material to the scene - pub fn add_material(&mut self, material: Material) { - self.materials.push(material); + pub fn add_material(&mut self, label: String, material: Arc) { + self.materials.insert(label, material); } // Adds a light to the scene - pub fn add_light(&mut self, light: Light) { - self.lights.push(light); + pub fn add_light(&mut self, label: String, light: Light) { + self.lights.insert(label, light); } // Adds a camera to the scene - pub fn add_camera(&mut self, camera: Camera) { - self.cameras.push(camera); + pub fn add_camera(&mut self, label: String, camera: Camera) { + self.cameras.insert(label, camera); } } diff --git a/src/state.rs b/src/state.rs index 42d4b64..54ba223 100644 --- a/src/state.rs +++ b/src/state.rs @@ -6,7 +6,6 @@ use crate::{gui::Gui, scene::Scene}; use crate::{gui::GuiEvent, log_error}; use std::path::Path; -use nalgebra::{Point3, Vector3}; use rand::seq::SliceRandom; use rand::thread_rng; @@ -20,7 +19,7 @@ use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::{Window, WindowBuilder}; const START_WIDTH: i32 = 1200; -const START_HEIGHT: i32 = 1200; +const START_HEIGHT: i32 = 700; const COLOUR_CLEAR: [u8; 4] = [0x22, 0x00, 0x11, 0xff]; const PIXEL_CLEAR: [u8; 4] = [0x55, 0x00, 0x22, 0xff]; @@ -46,7 +45,7 @@ impl State { pub fn new(window: Window, pixels: Pixels, gui: Gui) -> Self { let scene = Scene::empty(); let window_size = window.inner_size(); - let camera = Camera::new(Point3::new(2.0, 2.0, 2.0), Point3::origin(), Vector3::y()); + let camera = Camera::unit(); let rays = Vec::new(); Self {