STL loading and file dialogs
This commit is contained in:
parent
a2fc6590b4
commit
3606cabb53
|
@ -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"
|
||||
|
|
|
@ -25,6 +25,7 @@ struct Application {
|
|||
ui: ui::UserInterface,
|
||||
viewport: ui::ViewportWidget,
|
||||
render_state: render::RenderState,
|
||||
file_receiver: crossbeam_channel::Receiver<ui::FileEvent>,
|
||||
}
|
||||
|
||||
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::<render::ViewportStore>().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::<render::ViewportStore>()
|
||||
.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::<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>) {
|
||||
|
@ -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() => {
|
||||
|
|
|
@ -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<FileEvent>,
|
||||
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<FileEvent>) -> 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();
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue