diff --git a/Cargo.lock b/Cargo.lock index 08ac4b6..67f3213 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + [[package]] name = "bincode" version = "1.3.3" @@ -96,6 +102,9 @@ name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] [[package]] name = "block" @@ -186,6 +195,18 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "confy" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c" +dependencies = [ + "directories", + "ron", + "serde", + "thiserror", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -234,6 +255,26 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -425,6 +466,17 @@ dependencies = [ "system-deps", ] +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gif" version = "0.12.0" @@ -911,7 +963,7 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", "windows-targets 0.48.5", ] @@ -1074,6 +1126,15 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1083,6 +1144,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + [[package]] name = "regex" version = "0.1.80" @@ -1127,6 +1199,18 @@ dependencies = [ "windows", ] +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64", + "bitflags 2.4.1", + "serde", + "serde_derive", +] + [[package]] name = "rustc-serialize" version = "0.3.24" @@ -1265,6 +1349,26 @@ version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread-id" version = "2.0.0" @@ -1290,6 +1394,7 @@ version = "0.1.1" dependencies = [ "anyhow", "bincode", + "confy", "fs4", "glad-gl", "glfw", @@ -1298,6 +1403,7 @@ dependencies = [ "imgui", "mint", "rfd", + "serde", ] [[package]] @@ -1369,6 +1475,12 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.87" diff --git a/Cargo.toml b/Cargo.toml index d2b8e95..7574bcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,12 @@ fs4 = "0.7.0" [dependencies.imgui] version = "0.11.0" features = ["docking", "tables-api"] + +[dependencies.confy] +version = "0.5.1" +features = ["ron_conf"] +default-features = false + +[dependencies.serde] +version = "1.0.189" +features = ["derive"] diff --git a/TODO.md b/TODO.md index 9c02009..4ca86c0 100644 --- a/TODO.md +++ b/TODO.md @@ -22,14 +22,14 @@ - [x] There might be memory leaks somewhere (tracked it down to russimp, will have to write my own .obj and .stl readers) - [x] Reset object rotation when changing active model - [x] Drag'n'Drop models - - [ ] Config for consistent settings between restarts (home dir preferred) + - [x] Config for consistent settings between restarts (home dir preferred) - [x] (BUG) when removing a model that's not selected and the selected model is not the last, we select the last one - only select the last one if we removed the selected model - [x] Use filename for the root node - [x] Ship shaders directly in the executable - [x] Have the program accept model files as CLI args - [x] Have it so that when an instance of 3dobs is open, any other file associated with 3dobs is opened in the same instance - - [ ] Toggle for dis/allowing the one instance option + - [x] Toggle for dis/allowing the one instance option - [x] Show how much memory each loaded model is using - [ ] Move the invisible point lights to be around selected object from all sides so it's nicely illuminated diff --git a/src/ipc.rs b/src/ipc.rs index d38f5e5..0802b91 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -23,7 +23,11 @@ fn send_args_to_existing_instance(pipe_path: PathBuf, args_paths: Vec) .expect("Failed to send arguments"); } -pub fn init(lock_file: &File, args_paths: Vec) -> Receiver> { +pub fn init(lock_file: &File, args_paths: Vec, one_instance: bool) -> Option>> { + if !one_instance { + return None; + } + let pipe_name = "3dobs_pipe"; let pipe_path = std::env::temp_dir().join(pipe_name); @@ -61,5 +65,5 @@ pub fn init(lock_file: &File, args_paths: Vec) -> Receiver } }); - ipc_rx + Some(ipc_rx) } diff --git a/src/main.rs b/src/main.rs index 8dee25f..40fb81b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,8 @@ use std::path::PathBuf; use threedobs::{shader, ui::ui, utils, ipc}; fn main() -> anyhow::Result<(), Box> { + let settings: ui::Settings = confy::load("3dobs", "settings")?; + let args: Vec = env::args().collect(); let args_paths: Vec = args .iter() @@ -18,7 +20,7 @@ fn main() -> anyhow::Result<(), Box> { let lock_file_name = "3dobs.lock"; let lock_file_path = std::env::temp_dir().join(lock_file_name); let lock_file = File::create(&lock_file_path)?; - let ipc_rx = ipc::init(&lock_file, args_paths.clone()); + let ipc_rx = ipc::init(&lock_file, args_paths.clone(), settings.one_instance); let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS)?; @@ -32,7 +34,10 @@ fn main() -> anyhow::Result<(), Box> { window.set_cursor_mode(glfw::CursorMode::Disabled); window.make_current(); - let mut state = ui::State::default(); + let mut state = ui::State{ + settings, + ..Default::default() + }; glfw.set_swap_interval(glfw::SwapInterval::Sync(1)); @@ -128,20 +133,23 @@ fn main() -> anyhow::Result<(), Box> { let view_mat = glm::ext::look_at(state.camera.position, state.camera.position + state.camera.front, state.camera.up); let projection_mat = glm::ext::perspective(glm::radians(state.camera.fov), state.viewport_size[0] / state.viewport_size[1], 0.01, 200.0); - match ipc_rx.try_recv() { - Ok(paths) => { - window.focus(); - utils::import_models_from_paths(&paths, &mut state); - }, - Err(e) => { - match e { - std::sync::mpsc::TryRecvError::Empty => {}, - std::sync::mpsc::TryRecvError::Disconnected => { - panic!("Error: IPC thread channel disconnected"); + if let Some(rx) = &ipc_rx { + match rx.try_recv() { + Ok(paths) => { + window.focus(); + utils::import_models_from_paths(&paths, &mut state); + }, + Err(e) => { + match e { + std::sync::mpsc::TryRecvError::Empty => {}, + std::sync::mpsc::TryRecvError::Disconnected => { + panic!("Error: IPC thread channel disconnected"); + } } } } } + for (_, event) in glfw::flush_messages(&events) { // order of handling events is important here diff --git a/src/ui/ui.rs b/src/ui/ui.rs index bde08e7..6b1df6a 100644 --- a/src/ui/ui.rs +++ b/src/ui/ui.rs @@ -1,13 +1,20 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + use glad_gl::gl; +use serde::{Serialize, Deserialize}; use crate::{camera::Camera, model, imgui_glfw_support, imgui_opengl_renderer, mesh, ui, log, utils}; -use std::time::{SystemTime, UNIX_EPOCH}; +#[derive(Default, Debug, Serialize, Deserialize, Clone)] +pub struct Settings { + pub one_instance: bool, +} pub struct State { pub active_model: Option, pub show_console: bool, pub show_help_menu_about: bool, + pub show_settings: bool, pub is_cursor_captured: bool, pub can_capture_cursor: bool, pub draw_grid: bool, @@ -20,6 +27,7 @@ pub struct State { pub objects: Vec, pub viewport_size: [f32; 2], pub logger: log::Log, + pub settings: Settings, } impl Default for State { @@ -27,6 +35,8 @@ impl Default for State { Self { active_model: None, show_console: false, + show_help_menu_about: false, + show_settings: false, first_frame_drawn: false, is_cursor_captured: false, can_capture_cursor: false, @@ -34,12 +44,12 @@ impl Default for State { draw_aabb: false, fov_zoom: true, rotation_speed: 1.0, - show_help_menu_about: false, wireframe: false, camera: Camera::new(), objects: vec![], viewport_size: [0.0, 0.0], logger: log::Log::default(), + settings: Settings::default(), } } } @@ -102,6 +112,9 @@ pub fn draw_main_menu_bar(ui: &imgui::Ui, state: &mut State, window: &mut glfw:: if ui.menu_item_config("Import Model(s)").shortcut("Ctrl+O").build() { import_model(state); } + if ui.menu_item_config("Settings").build() { + state.show_settings = !state.show_settings; + } if ui.menu_item_config("Quit").shortcut("Ctrl+Q").build() { window.set_should_close(true); } @@ -124,6 +137,24 @@ pub fn draw_main_menu_bar(ui: &imgui::Ui, state: &mut State, window: &mut glfw:: }); } +pub fn draw_settings_window(ui: &imgui::Ui, state: &mut State) { + if !state.show_settings { + return; + } + let display_size = ui.io().display_size; + + ui.window("Settings") + .size([500.0, 100.0], imgui::Condition::FirstUseEver) + .opened(&mut state.show_settings) + .position([display_size[0] / 2.0, display_size[1] / 2.0], imgui::Condition::FirstUseEver) + .position_pivot([0.5, 0.5]) + .build(|| { + if ui.checkbox("Only allow one program instance (Reboot required when enabling)", &mut state.settings.one_instance) { + confy::store("3dobs", "settings", state.settings.clone()).unwrap(); + } + }); +} + fn draw_transformations(ui: &imgui::Ui, mesh: &mut mesh::Mesh) { imgui::Drag::new("###XPos") .range(f32::NEG_INFINITY, f32::INFINITY) @@ -428,6 +459,7 @@ pub fn draw_ui( draw_viewport(ui, state, scene_fb_texture); draw_objects_window(ui, state); draw_console(ui, state); + draw_settings_window(ui, state); ui.end_frame_early();