From 3606cabb53606e0471c7eb71f8be68dc8426382b Mon Sep 17 00:00:00 2001 From: mars Date: Sun, 15 May 2022 23:29:34 -0600 Subject: [PATCH] STL loading and file dialogs --- editor/Cargo.toml | 4 ++ editor/src/main.rs | 106 ++++++++++++++++++++++++++++++++++++++++++--- editor/src/ui.rs | 46 ++++++++++++++++++-- src/pass/mesh.rs | 2 +- 4 files changed, 148 insertions(+), 10 deletions(-) diff --git a/editor/Cargo.toml b/editor/Cargo.toml index 7c0560f..2e04027 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +bytemuck = "^1.0" +crossbeam-channel = "^0.5" cyborg = { path = "../", features = ["legion"] } egui = "0.17.0" egui-winit = "0.17.0" @@ -13,3 +15,5 @@ legion = "^0.4" pollster = "0.2" puffin = "^0.13" puffin_egui = "0.14.0" +rfd = "^0.8" +stl = "0.2.1" diff --git a/editor/src/main.rs b/editor/src/main.rs index b55703c..f324367 100644 --- a/editor/src/main.rs +++ b/editor/src/main.rs @@ -25,6 +25,7 @@ struct Application { ui: ui::UserInterface, viewport: ui::ViewportWidget, render_state: render::RenderState, + file_receiver: crossbeam_channel::Receiver, } impl Application { @@ -66,8 +67,16 @@ impl Application { let egui_state = egui_winit::State::new(4096, &window); let egui_ctx = egui::Context::default(); let mut egui_rp = egui_wgpu_backend::RenderPass::new(&device, config.format, 1); - let render_state = render::RenderState::new(device.clone(), queue.clone(), config.format, &mut egui_rp); - let viewport_texture = render_state.resources.get::().unwrap().viewport.egui_texture; + let render_state = + render::RenderState::new(device.clone(), queue.clone(), config.format, &mut egui_rp); + let viewport_texture = render_state + .resources + .get::() + .unwrap() + .viewport + .egui_texture; + + let (file_sender, file_receiver) = crossbeam_channel::unbounded(); Self { window, @@ -79,14 +88,98 @@ impl Application { egui_state, egui_ctx, egui_rp, - ui: ui::UserInterface::new(), + ui: ui::UserInterface::new(file_sender), viewport: ui::ViewportWidget::new(viewport_texture), render_state, + file_receiver, } } pub fn update(&mut self) { self.window.request_redraw(); + + loop { + match self.file_receiver.try_recv() { + Ok(ui::FileEvent::Save) => println!("Saving!"), + Ok(ui::FileEvent::SaveAs(path)) => println!("Saving as: {:?}", path), + Ok(ui::FileEvent::Import(kind, path)) => match kind { + ui::ImportKind::Stl => { + self.load_stl(path); + } + }, + Err(crossbeam_channel::TryRecvError::Empty) => break, + Err(crossbeam_channel::TryRecvError::Disconnected) => { + panic!("File event sender hung up!"); + } + } + } + } + + pub fn load_stl(&mut self, path: std::path::PathBuf) { + use cyborg::pass::{ + mesh::{Index, MeshPass, Vertex}, + RenderPassBox, + }; + use cyborg::storage::mesh::{AttrBuffer, MeshBuffer}; + + eprintln!("loading {:?}", path); + + let mut file = std::fs::File::open(path).unwrap(); + let stl = stl::read_stl(&mut file).unwrap(); + + let mut vertices = Vec::new(); + let mut indices = Vec::new(); + + for tri in stl.triangles.iter() { + let tan_frame = 0; // TODO make from normal + indices.push(vertices.len() as Index); + vertices.push(Vertex { + position: tri.v1, + tan_frame, + }); + indices.push(vertices.len() as Index); + vertices.push(Vertex { + position: tri.v2, + tan_frame, + }); + indices.push(vertices.len() as Index); + vertices.push(Vertex { + position: tri.v3, + tan_frame, + }); + } + + let mesh_pass = self + .render_state + .resources + .get::>() + .unwrap(); + let attributes = mesh_pass.get_attributes(); + + let vertices = AttrBuffer { + id: attributes.vertex, + count: vertices.len(), + data: bytemuck::cast_slice(&vertices).to_vec(), + }; + + let indices = AttrBuffer { + id: attributes.index, + count: indices.len(), + data: bytemuck::cast_slice(&indices).to_vec(), + }; + + let mut mesh = MeshBuffer::default(); + mesh.attributes.push(vertices); + mesh.attributes.push(indices); + + self.render_state.world.push(( + cyborg::scene::Mesh { + mesh: mesh_pass.get_mesh_pool().load(mesh).unwrap(), + }, + cyborg::scene::Transform { + transform: glam::Mat4::from_translation(glam::Vec3::new(0.0, -10.0, 0.0)), + }, + )); } pub fn on_resize(&mut self, new_size: PhysicalSize) { @@ -108,12 +201,14 @@ impl Application { let output = { puffin::profile_scope!("Draw egui"); let raw_input = self.egui_state.take_egui_input(&self.window); - self.egui_ctx.run(raw_input, |ctx| self.ui.run(ctx, &mut self.viewport)) + self.egui_ctx + .run(raw_input, |ctx| self.ui.run(ctx, &mut self.viewport)) }; { puffin::profile_scope!("Main render"); - self.render_state.update_viewport(&mut self.egui_rp, &mut self.viewport); + self.render_state + .update_viewport(&mut self.egui_rp, &mut self.viewport); self.render_state.render(); } @@ -164,7 +259,6 @@ fn main() { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - // println!("{:?}", event); match event { Event::RedrawRequested(window_id) if window_id == app.window.id() => { diff --git a/editor/src/ui.rs b/editor/src/ui.rs index 9060412..c678e45 100644 --- a/editor/src/ui.rs +++ b/editor/src/ui.rs @@ -1,7 +1,22 @@ use crate::winit; +use crossbeam_channel::Sender; use cyborg::camera::Flycam; +use std::path::PathBuf; + +#[derive(Clone, Debug)] +pub enum FileEvent { + Save, + SaveAs(PathBuf), + Import(ImportKind, PathBuf), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum ImportKind { + Stl, +} pub struct UserInterface { + file_sender: Sender, developer_mode: bool, show_profiler: bool, quit: bool, @@ -10,8 +25,9 @@ pub struct UserInterface { } impl UserInterface { - pub fn new() -> Self { + pub fn new(file_sender: Sender) -> Self { Self { + file_sender, developer_mode: true, show_profiler: false, quit: false, @@ -35,15 +51,39 @@ impl UserInterface { } if ui.button("Save").clicked() { - println!("Saving!"); ui.close_menu(); + self.file_sender.send(FileEvent::Save).unwrap(); } if ui.button("Save as...").clicked() { - println!("Saving as!"); ui.close_menu(); + + let file_sender = self.file_sender.to_owned(); + std::thread::spawn(move || { + if let Some(path) = rfd::FileDialog::new().save_file() { + file_sender.send(FileEvent::SaveAs(path)).unwrap(); + } + }); } + ui.menu_button("Import...", |ui| { + if ui.button("STL").clicked() { + ui.close_menu(); + + let import_kind = ImportKind::Stl; + let file_sender = self.file_sender.to_owned(); + std::thread::spawn(move || { + let dialog = rfd::FileDialog::new().add_filter("STL", &["stl"]); + if let Some(paths) = dialog.pick_files() { + for path in paths.iter() { + let event = FileEvent::Import(import_kind, path.into()); + file_sender.send(event).unwrap(); + } + } + }); + } + }); + if ui.button("Open...").clicked() { println!("Opening!"); ui.close_menu(); diff --git a/src/pass/mesh.rs b/src/pass/mesh.rs index 6f0027f..2b9959e 100644 --- a/src/pass/mesh.rs +++ b/src/pass/mesh.rs @@ -14,7 +14,7 @@ pub struct ShaderInfo { } #[repr(C)] -#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] pub struct Vertex { pub position: [f32; 3], pub tan_frame: u32,