Added scene editor

This commit is contained in:
STP
2023-11-26 20:49:09 -05:00
parent eeae148fd5
commit e48bc02daa
9 changed files with 133 additions and 95 deletions

View File

@@ -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);

View File

@@ -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)

View File

@@ -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));
}
}
}
}

View File

@@ -9,7 +9,7 @@ pub struct Light {
}
impl Light {
pub fn new(position: Point3<f64>, colour: Vector3<f64>, falloff: Vector3<f64>) -> Self {
pub fn new(position: Point3<f64>, colour: Vector3<f64>, falloff: Vector3<f64>) -> Light {
let colour = colour.cast();
let falloff = falloff.cast();
Light {
@@ -19,7 +19,7 @@ impl Light {
ambient: false,
}
}
pub fn ambient(colour: Vector3<f64>) -> Self {
pub fn ambient(colour: Vector3<f64>) -> Light {
Light {
position: Point3::new(0.0, 0.0, 0.0),
colour: colour.cast(),

View File

@@ -23,7 +23,6 @@ impl Material {
}
pub fn magenta() -> Arc<Self> {
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 })

View File

@@ -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<Node>) -> Option<Intersection> {
pub fn get_closest_intersection(&self, nodes: &HashMap<String, Node>) -> Option<Intersection> {
//Assign no intersection
let mut closest_distance = f64::MAX;
let mut closest_intersect: Option<Intersection> = None;
for node in nodes {
for (_, node) in nodes {
// Clone arc to primitive
let primitive = node.primitive.clone();
// Transform ray into local model cordinates

View File

@@ -19,7 +19,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
// 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<u8>
}
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() {

View File

@@ -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<dyn Primitive>,
//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<f64>,
pub inv_model: Matrix4<f64>,
@@ -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<Node>,
pub materials: Vec<Material>,
pub lights: Vec<Light>,
pub cameras: Vec<Camera>,
pub nodes: HashMap<String, Node>,
pub materials: HashMap<String, Arc<Material>>,
pub lights: HashMap<String, Light>,
pub cameras: HashMap<String, Camera>,
}
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<Material>) {
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);
}
}

View File

@@ -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 {