STL loading and file dialogs
This commit is contained in:
parent
a2fc6590b4
commit
3606cabb53
|
@ -4,6 +4,8 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bytemuck = "^1.0"
|
||||||
|
crossbeam-channel = "^0.5"
|
||||||
cyborg = { path = "../", features = ["legion"] }
|
cyborg = { path = "../", features = ["legion"] }
|
||||||
egui = "0.17.0"
|
egui = "0.17.0"
|
||||||
egui-winit = "0.17.0"
|
egui-winit = "0.17.0"
|
||||||
|
@ -13,3 +15,5 @@ legion = "^0.4"
|
||||||
pollster = "0.2"
|
pollster = "0.2"
|
||||||
puffin = "^0.13"
|
puffin = "^0.13"
|
||||||
puffin_egui = "0.14.0"
|
puffin_egui = "0.14.0"
|
||||||
|
rfd = "^0.8"
|
||||||
|
stl = "0.2.1"
|
||||||
|
|
|
@ -25,6 +25,7 @@ struct Application {
|
||||||
ui: ui::UserInterface,
|
ui: ui::UserInterface,
|
||||||
viewport: ui::ViewportWidget,
|
viewport: ui::ViewportWidget,
|
||||||
render_state: render::RenderState,
|
render_state: render::RenderState,
|
||||||
|
file_receiver: crossbeam_channel::Receiver<ui::FileEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
|
@ -66,8 +67,16 @@ impl Application {
|
||||||
let egui_state = egui_winit::State::new(4096, &window);
|
let egui_state = egui_winit::State::new(4096, &window);
|
||||||
let egui_ctx = egui::Context::default();
|
let egui_ctx = egui::Context::default();
|
||||||
let mut egui_rp = egui_wgpu_backend::RenderPass::new(&device, config.format, 1);
|
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 render_state =
|
||||||
let viewport_texture = render_state.resources.get::<render::ViewportStore>().unwrap().viewport.egui_texture;
|
render::RenderState::new(device.clone(), queue.clone(), config.format, &mut egui_rp);
|
||||||
|
let viewport_texture = render_state
|
||||||
|
.resources
|
||||||
|
.get::<render::ViewportStore>()
|
||||||
|
.unwrap()
|
||||||
|
.viewport
|
||||||
|
.egui_texture;
|
||||||
|
|
||||||
|
let (file_sender, file_receiver) = crossbeam_channel::unbounded();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
window,
|
window,
|
||||||
|
@ -79,14 +88,98 @@ impl Application {
|
||||||
egui_state,
|
egui_state,
|
||||||
egui_ctx,
|
egui_ctx,
|
||||||
egui_rp,
|
egui_rp,
|
||||||
ui: ui::UserInterface::new(),
|
ui: ui::UserInterface::new(file_sender),
|
||||||
viewport: ui::ViewportWidget::new(viewport_texture),
|
viewport: ui::ViewportWidget::new(viewport_texture),
|
||||||
render_state,
|
render_state,
|
||||||
|
file_receiver,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self) {
|
pub fn update(&mut self) {
|
||||||
self.window.request_redraw();
|
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::<RenderPassBox<MeshPass>>()
|
||||||
|
.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<u32>) {
|
pub fn on_resize(&mut self, new_size: PhysicalSize<u32>) {
|
||||||
|
@ -108,12 +201,14 @@ impl Application {
|
||||||
let output = {
|
let output = {
|
||||||
puffin::profile_scope!("Draw egui");
|
puffin::profile_scope!("Draw egui");
|
||||||
let raw_input = self.egui_state.take_egui_input(&self.window);
|
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");
|
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();
|
self.render_state.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +259,6 @@ fn main() {
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
*control_flow = ControlFlow::Wait;
|
*control_flow = ControlFlow::Wait;
|
||||||
// println!("{:?}", event);
|
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::RedrawRequested(window_id) if window_id == app.window.id() => {
|
Event::RedrawRequested(window_id) if window_id == app.window.id() => {
|
||||||
|
|
|
@ -1,7 +1,22 @@
|
||||||
use crate::winit;
|
use crate::winit;
|
||||||
|
use crossbeam_channel::Sender;
|
||||||
use cyborg::camera::Flycam;
|
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 {
|
pub struct UserInterface {
|
||||||
|
file_sender: Sender<FileEvent>,
|
||||||
developer_mode: bool,
|
developer_mode: bool,
|
||||||
show_profiler: bool,
|
show_profiler: bool,
|
||||||
quit: bool,
|
quit: bool,
|
||||||
|
@ -10,8 +25,9 @@ pub struct UserInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserInterface {
|
impl UserInterface {
|
||||||
pub fn new() -> Self {
|
pub fn new(file_sender: Sender<FileEvent>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
file_sender,
|
||||||
developer_mode: true,
|
developer_mode: true,
|
||||||
show_profiler: false,
|
show_profiler: false,
|
||||||
quit: false,
|
quit: false,
|
||||||
|
@ -35,15 +51,39 @@ impl UserInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui.button("Save").clicked() {
|
if ui.button("Save").clicked() {
|
||||||
println!("Saving!");
|
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
|
self.file_sender.send(FileEvent::Save).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui.button("Save as...").clicked() {
|
if ui.button("Save as...").clicked() {
|
||||||
println!("Saving as!");
|
|
||||||
ui.close_menu();
|
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() {
|
if ui.button("Open...").clicked() {
|
||||||
println!("Opening!");
|
println!("Opening!");
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub struct ShaderInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
pub position: [f32; 3],
|
pub position: [f32; 3],
|
||||||
pub tan_frame: u32,
|
pub tan_frame: u32,
|
||||||
|
|
Loading…
Reference in New Issue