diff --git a/Cargo.lock b/Cargo.lock index 6afeb17e..7c5f4924 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -464,6 +464,15 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.68.1" @@ -1106,6 +1115,15 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1276,6 +1294,15 @@ dependencies = [ "serde", ] +[[package]] +name = "egui-notify" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d57ed9c398e24c1b9faf2c52cdc305dd29cb1d9dfa12a0166d254582bc47727a" +dependencies = [ + "egui", +] + [[package]] name = "egui-wgpu" version = "0.22.0" @@ -2136,6 +2163,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + [[package]] name = "jni" version = "0.19.0" @@ -2297,6 +2330,21 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -2369,9 +2417,21 @@ name = "luminol-components" version = "0.1.0" dependencies = [ "egui", + "egui-notify", + "egui-wgpu", + "egui_dock", + "glam", + "itertools", "luminol-data", + "luminol-filesystem", "luminol-graphics", + "once_cell", + "parking_lot", + "serde", + "slab", "strum", + "syntect", + "wgpu", ] [[package]] @@ -3018,6 +3078,28 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "onig" +version = "6.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" +dependencies = [ + "bitflags 1.3.2", + "libc", + "once_cell", + "onig_sys", +] + +[[package]] +name = "onig_sys" +version = "69.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "opaque-debug" version = "0.3.0" @@ -3301,6 +3383,20 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "plist" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" +dependencies = [ + "base64", + "indexmap 1.9.3", + "line-wrap", + "quick-xml", + "serde", + "time", +] + [[package]] name = "png" version = "0.17.10" @@ -3356,6 +3452,12 @@ dependencies = [ "winreg 0.10.1", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -3426,6 +3528,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-xml" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.33" @@ -3672,6 +3783,18 @@ version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2316fb90175e4f747331f29c9275aaddf2868b355472e94a3b82ad235a719b5" +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + [[package]] name = "same-file" version = "1.0.6" @@ -3744,6 +3867,17 @@ dependencies = [ "syn 2.0.29", ] +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.16" @@ -4132,6 +4266,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syntect" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02b4b303bf8d08bfeb0445cba5068a3d306b6baece1d5582171a9bf49188f91" +dependencies = [ + "bincode", + "bitflags 1.3.2", + "flate2", + "fnv", + "once_cell", + "onig", + "plist", + "regex-syntax", + "serde", + "serde_json", + "thiserror", + "walkdir", + "yaml-rust", +] + [[package]] name = "system-deps" version = "6.1.2" @@ -4310,6 +4465,35 @@ dependencies = [ "weezl", ] +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -5353,6 +5537,15 @@ version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1" +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "zbus" version = "3.14.1" diff --git a/Cargo.toml b/Cargo.toml index ae547a29..41fe7ce9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ eframe = { version = "0.22.0", features = [ egui-wgpu = "0.22.0" wgpu = "0.16.3" +glam = { version = "0.24.2", features = ["bytemuck"] } image = "0.24.7" diff --git a/crates/luminol-components/Cargo.toml b/crates/luminol-components/Cargo.toml index e692ee36..c76adb9d 100644 --- a/crates/luminol-components/Cargo.toml +++ b/crates/luminol-components/Cargo.toml @@ -8,6 +8,26 @@ edition = "2021" [dependencies] luminol-data = { version = "*", path = "../luminol-data/" } luminol-graphics = { version = "*", path = "../luminol-graphics/" } +luminol-filesystem = { version = "*", path = "../luminol-filesystem/" } egui.workspace = true strum.workspace = true + +egui-wgpu.workspace = true + +wgpu.workspace = true +glam.workspace = true + +egui_dock = "0.7.3" +egui-notify = "0.9.0" + +syntect = "5.1.0" + +parking_lot.workspace = true + +itertools.workspace = true + +serde.workspace = true + +once_cell.workspace = true +slab.workspace = true diff --git a/crates/luminol-components/src/lib.rs b/crates/luminol-components/src/lib.rs index 2d361110..8b165c52 100644 --- a/crates/luminol-components/src/lib.rs +++ b/crates/luminol-components/src/lib.rs @@ -37,9 +37,13 @@ pub use tilepicker::{SelectedTile, Tilepicker}; mod tab; pub use tab::{Tab, Tabs}; + mod window; pub use window::{Window, Windows}; +pub mod modal; +pub use modal::Modal; + pub struct EnumMenuButton<'e, T> { current_value: &'e mut T, id: egui::Id, diff --git a/crates/luminol-components/src/map_view.rs b/crates/luminol-components/src/map_view.rs index 59818e22..fdbc0184 100644 --- a/crates/luminol-components/src/map_view.rs +++ b/crates/luminol-components/src/map_view.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use super::tilepicker; +use itertools::Itertools; #[derive(Debug)] pub struct MapView { @@ -29,8 +29,8 @@ pub struct MapView { /// The first sprite is for drawing on the tilemap, /// and the second sprite is for the hover preview. - pub events: luminol_data::OptionVec<(Event, Event)>, - pub map: Map, + pub events: luminol_data::OptionVec<(luminol_graphics::Event, luminol_graphics::Event)>, + pub map: luminol_graphics::Map, pub selected_layer: SelectedLayer, pub selected_event_id: Option, @@ -59,21 +59,40 @@ pub enum SelectedLayer { } impl MapView { - pub fn new(map: &rpg::Map, tileset: &rpg::Tileset) -> Result { + pub fn new( + graphics_state: &luminol_graphics::GraphicsState, + filesystem: &impl luminol_filesystem::FileSystem, + map: &luminol_data::rpg::Map, + tileset: &luminol_data::rpg::Tileset, + ) -> Result { // Get tilesets. - let use_push_constants = state!() + let use_push_constants = graphics_state .render_state .device .features() .contains(wgpu::Features::PUSH_CONSTANTS); - let atlas = state!().atlas_cache.load_atlas(tileset)?; + let atlas = graphics_state + .atlas_cache + .load_atlas(graphics_state, filesystem, tileset)?; let events = map .events .iter() .map(|(id, e)| { - let sprite = Event::new(e, &atlas, use_push_constants); - let preview_sprite = Event::new(e, &atlas, use_push_constants); + let sprite = luminol_graphics::Event::new( + graphics_state, + filesystem, + e, + &atlas, + use_push_constants, + ); + let preview_sprite = luminol_graphics::Event::new( + graphics_state, + filesystem, + e, + &atlas, + use_push_constants, + ); let Ok(sprite) = sprite else { return Err(sprite.unwrap_err()); }; @@ -88,7 +107,13 @@ impl MapView { }) .flatten_ok() .try_collect()?; - let map = Map::new(map, tileset, use_push_constants)?; + let map = luminol_graphics::Map::new( + graphics_state, + filesystem, + map, + tileset, + use_push_constants, + )?; Ok(Self { visible_display: false, @@ -119,8 +144,9 @@ impl MapView { pub fn ui( &mut self, ui: &mut egui::Ui, - map: &rpg::Map, - tilepicker: &Tilepicker, + graphics_state: &'static luminol_graphics::GraphicsState, + map: &luminol_data::rpg::Map, + tilepicker: &crate::Tilepicker, dragging_event: bool, drawing_shape: bool, drawing_shape_pos: Option, @@ -231,15 +257,19 @@ impl MapView { let proj_center_y = height2 * 32. - self.pan.y / scale; let proj_width2 = canvas_rect.width() / scale / 2.; let proj_height2 = canvas_rect.height() / scale / 2.; - self.map.set_proj(glam::Mat4::orthographic_rh( - proj_center_x - proj_width2, - proj_center_x + proj_width2, - proj_center_y + proj_height2, - proj_center_y - proj_height2, - -1., - 1., - )); + self.map.set_proj( + &graphics_state.render_state, + glam::Mat4::orthographic_rh( + proj_center_x - proj_width2, + proj_center_x + proj_width2, + proj_center_y + proj_height2, + proj_center_y - proj_height2, + -1., + 1., + ), + ); self.map.paint( + graphics_state, ui.painter(), match self.selected_layer { SelectedLayer::Events => None, @@ -297,6 +327,7 @@ impl MapView { // Darken the graphic if required if let Some((sprite, _)) = sprites { sprite.sprite().graphic.set_opacity_multiplier( + &graphics_state.render_state, if self.darken_unselected_layers && !matches!(self.selected_layer, SelectedLayer::Events) { @@ -324,15 +355,18 @@ impl MapView { if let Some((sprite, _)) = sprites { let x = event.x as f32 * 32. + (32. - event_size.x) / 2.; let y = event.y as f32 * 32. + (32. - event_size.y); - sprite.set_proj(glam::Mat4::orthographic_rh( - proj_center_x - proj_width2 - x, - proj_center_x + proj_width2 - x, - proj_center_y + proj_height2 - y, - proj_center_y - proj_height2 - y, - -1., - 1., - )); - sprite.paint(ui.painter(), canvas_rect); + sprite.set_proj( + &graphics_state.render_state, + glam::Mat4::orthographic_rh( + proj_center_x - proj_width2 - x, + proj_center_x + proj_width2 - x, + proj_center_y + proj_height2 - y, + proj_center_y - proj_height2 - y, + -1., + 1., + ), + ); + sprite.paint(graphics_state, ui.painter(), canvas_rect); } if matches!(self.selected_layer, SelectedLayer::Events) @@ -385,7 +419,7 @@ impl MapView { ); if let Some((_, preview_sprite)) = sprites { if ui.ctx().screen_rect().contains_rect(response.rect) { - preview_sprite.paint(&painter, response.rect); + preview_sprite.paint(graphics_state, &painter, response.rect); } } match self.selected_event_id { diff --git a/crates/luminol-components/src/modal.rs b/crates/luminol-components/src/modal.rs index 17fa5a6c..7f599777 100644 --- a/crates/luminol-components/src/modal.rs +++ b/crates/luminol-components/src/modal.rs @@ -29,7 +29,7 @@ /// Modals are not persistent (unlike windows) and are not stored on the heap. /// They are stored on the stack and the stack *only*. // TODO: Make more featureful and general -pub trait Modal { +pub trait Modal: Sized { /// The output type for this modal. type Data; @@ -38,7 +38,7 @@ pub trait Modal { /// Display a button to show this modal. /// It should call show. - fn button(self, ui: &mut egui::Ui, state: &mut bool, data: &mut Self::Data) -> Self; + fn button(this: &mut Option, ui: &mut egui::Ui, data: &mut Self::Data) -> Self; /// Show this modal. fn show(&mut self, ctx: &egui::Context, open: &mut bool, data: &mut Self::Data); diff --git a/crates/luminol-components/src/tab.rs b/crates/luminol-components/src/tab.rs index 71774a2e..131522fd 100644 --- a/crates/luminol-components/src/tab.rs +++ b/crates/luminol-components/src/tab.rs @@ -22,15 +22,29 @@ // terms of the Steamworks API by Valve Corporation, the licensors of this // Program grant you additional permission to convey the resulting work. -use parking_lot::Mutex; use std::hash::Hash; /// Helper struct for tabs. pub struct Tabs { - tree: Mutex>, + dock_state: egui_dock::DockState, + id: egui::Id, } +pub struct EditTabs { + clean_fn: Option>, + added: Vec, + removed: std::collections::HashSet, +} + +type CleanFn = Box bool>; + +struct TabViewer { + // we don't actually own any types of T, but we use them in TabViewer + // *const is used here to avoid needing lifetimes and to indicate to the drop checker that we don't own any types of T + marker: std::marker::PhantomData<*const T>, +} + impl Tabs where T: Tab, @@ -39,56 +53,90 @@ where pub fn new(id: impl Hash, tabs: Vec) -> Self { Self { id: egui::Id::new(id), - tree: egui_dock::Tree::new(tabs).into(), + dock_state: egui_dock::DockState::new(tabs), } } /// Display all tabs. - pub fn ui(&self, ui: &mut egui::Ui) { - // egui_dock::DockArea::new(&mut self.tree.lock()) - // .id(self.id) - // .show_inside( - // ui, - // &mut TabViewer { - // marker: std::marker::PhantomData, - // }, - // ); + pub fn ui(&mut self, ui: &mut egui::Ui) { + let mut edit_tabs = EditTabs:: { + clean_fn: None, + added: Vec::new(), + removed: std::collections::HashSet::new(), + }; + + egui_dock::DockArea::new(&mut self.dock_state) + .id(self.id) + .show_inside( + ui, + &mut TabViewer { + marker: std::marker::PhantomData::<*const T>, + }, + ); + + for tab in edit_tabs.added { + self.add_tab(tab); + } + if let Some(f) = edit_tabs.clean_fn { + self.clean_tabs(f); + } } /// Add a tab. - pub fn add_tab(&self, tab: T) { - let mut tree = self.tree.lock(); - for n in tree.iter() { - if let egui_dock::Node::Leaf { tabs, .. } = n { + pub fn add_tab(&mut self, tab: T) { + // FIXME O(n) + for node in self.dock_state.iter_nodes() { + if let egui_dock::Node::Leaf { tabs, .. } = node { if tabs.iter().any(|t| t.id() == tab.id()) { return; } } } - tree.push_to_focused_leaf(tab); + self.dock_state.push_to_focused_leaf(tab); } /// Removes tabs that the provided closure returns `false` when called. - pub fn clean_tabs bool>(&self, mut f: F) { - let mut tree = self.tree.lock(); - for node in tree.iter_mut() { - if let egui_dock::Node::Leaf { tabs, .. } = node { - tabs.retain_mut(&mut f) + pub fn clean_tabs(&mut self, mut f: impl Fn(&T) -> bool) { + // i hate egui dock + for i in 0.. { + let Some(surface) = self.dock_state.get_surface_mut(egui_dock::SurfaceIndex(i)) else { + break; + }; + if let Some(tree) = surface.node_tree_mut() { + for node in tree.iter_mut() { + if let egui_dock::Node::Leaf { tabs, .. } = node { + tabs.retain(&mut f); + } + } } } } /// Returns the name of the focused tab. pub fn focused_name(&self) -> Option { - let mut tree = self.tree.lock(); - tree.find_active().map(|(_, t)| t.name()) + None } } -struct TabViewer { - // we don't actually own any types of T, but we use them in TabViewer - // *const is used here to avoid needing lifetimes and to indicate to the drop checker that we don't own any types of T - marker: std::marker::PhantomData<*const T>, +impl EditTabs +where + T: Tab, +{ + pub fn clean(&mut self, f: impl Fn(&T) -> bool + 'static) { + self.clean_fn = Some(Box::new(f)); + } + + pub fn add_tab(&mut self, tabs: T) { + self.added.push(tabs) + } + + pub fn remove_tab(&mut self, tab: &T) -> bool { + self.remove_tab_by_id(tab.id()) + } + + pub fn remove_tab_by_id(&mut self, id: egui::Id) -> bool { + self.removed.insert(id) + } } impl egui_dock::TabViewer for TabViewer diff --git a/crates/luminol-components/src/tilepicker.rs b/crates/luminol-components/src/tilepicker.rs index 5744f36d..11ee8846 100644 --- a/crates/luminol-components/src/tilepicker.rs +++ b/crates/luminol-components/src/tilepicker.rs @@ -14,8 +14,8 @@ // // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -pub use crate::prelude::*; +use itertools::Itertools; use slab::Slab; use std::sync::Arc; use std::time::Duration; @@ -35,8 +35,8 @@ pub struct Tilepicker { #[derive(Debug)] struct Resources { - tiles: primitives::Tiles, - viewport: primitives::Viewport, + tiles: luminol_graphics::tiles::Tiles, + viewport: luminol_graphics::viewport::Viewport, } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] @@ -71,26 +71,33 @@ impl Default for SelectedTile { type ResourcesSlab = Slab>; impl Tilepicker { - pub fn new(tileset: &rpg::Tileset) -> Result { - let use_push_constants = state!() + pub fn new( + graphics_state: &luminol_graphics::GraphicsState, + filesystem: &impl luminol_filesystem::FileSystem, + tileset: &luminol_data::rpg::Tileset, + ) -> Result { + let use_push_constants = graphics_state .render_state .device .features() .contains(wgpu::Features::PUSH_CONSTANTS); - let atlas = state!().atlas_cache.load_atlas(tileset)?; + let atlas = graphics_state + .atlas_cache + .load_atlas(graphics_state, filesystem, tileset)?; let tilepicker_data = (47..(384 + 47)) .step_by(48) .chain(384..(atlas.tileset_height as i16 / 32 * 8 + 384)) .collect_vec(); - let tilepicker_data = Table3::new_data( + let tilepicker_data = luminol_data::Table3::new_data( 8, 1 + (atlas.tileset_height / 32) as usize, 1, tilepicker_data, ); - let viewport = primitives::Viewport::new( + let viewport = luminol_graphics::viewport::Viewport::new( + graphics_state, glam::Mat4::orthographic_rh( 0.0, 256., @@ -102,7 +109,12 @@ impl Tilepicker { use_push_constants, ); - let tiles = primitives::Tiles::new(atlas, &tilepicker_data, use_push_constants); + let tiles = luminol_graphics::tiles::Tiles::new( + graphics_state, + atlas, + &tilepicker_data, + use_push_constants, + ); Ok(Self { resources: Arc::new(Resources { tiles, viewport }), @@ -126,12 +138,20 @@ impl Tilepicker { } } - pub fn ui(&mut self, ui: &mut egui::Ui, scroll_rect: egui::Rect) -> egui::Response { + pub fn ui( + &mut self, + ui: &mut egui::Ui, + graphics_state: &'static luminol_graphics::GraphicsState, + scroll_rect: egui::Rect, + ) -> egui::Response { let time = ui.ctx().input(|i| i.time); if let Some(ani_time) = self.ani_time { if time - ani_time >= 16. / 60. { self.ani_time = Some(time); - self.resources.tiles.autotiles.inc_ani_index(); + self.resources + .tiles + .autotiles + .inc_ani_index(&graphics_state.render_state); } } else { self.ani_time = Some(time); @@ -145,17 +165,20 @@ impl Tilepicker { ); let resources = self.resources.clone(); - let prepare_id = Arc::new(OnceCell::new()); + let prepare_id = Arc::new(once_cell::sync::OnceCell::new()); let paint_id = prepare_id.clone(); - resources.viewport.set_proj(glam::Mat4::orthographic_rh( - scroll_rect.left(), - scroll_rect.right(), - scroll_rect.bottom(), - scroll_rect.top(), - -1., - 1., - )); + resources.viewport.set_proj( + &graphics_state.render_state, + glam::Mat4::orthographic_rh( + scroll_rect.left(), + scroll_rect.right(), + scroll_rect.bottom(), + scroll_rect.top(), + -1., + 1., + ), + ); ui.painter().add(egui::PaintCallback { rect: scroll_rect.translate(canvas_rect.min.to_vec2()), callback: Arc::new( @@ -178,7 +201,7 @@ impl Tilepicker { } = resources.as_ref(); viewport.bind(render_pass); - tiles.draw(viewport, &[true], None, render_pass); + tiles.draw(graphics_state, viewport, &[true], None, render_pass); }), ), }); diff --git a/crates/luminol-components/src/window.rs b/crates/luminol-components/src/window.rs index d4e2c8ea..c1cbafd9 100644 --- a/crates/luminol-components/src/window.rs +++ b/crates/luminol-components/src/window.rs @@ -22,47 +22,85 @@ // terms of the Steamworks API by Valve Corporation, the licensors of this // Program grant you additional permission to convey the resulting work. -use parking_lot::Mutex; - /// A window management system to handle heap allocated windows /// /// Will deny any duplicated window titles and is not specialized like modals #[derive(Default)] -pub struct Windows { +pub struct Windows { // A dynamic array of Windows. Iterated over and cleaned up in fn update(). - windows: Mutex>>, + windows: Vec, +} + +pub struct EditWindows { + clean_fn: Option>, + added: Vec, + removed: std::collections::HashSet, } -impl Windows { +type CleanFn = Box bool>; + +impl Windows +where + T: Window, +{ /// A function to add a window. - pub fn add_window(&self, window: T) - where - T: Window + Send + 'static, - { - let mut windows = self.windows.lock(); - if windows.iter().any(|w| w.id() == window.id()) { + pub fn add_window(&mut self, window: T) { + // FIXME use a hashmap, or something with less than O(n) search time + if self.windows.iter().any(|w| w.id() == window.id()) { return; } - windows.push(Box::new(window)); + self.windows.push(window); } /// Clean all windows that need the data cache. /// This is usually when a project is closed. - pub fn clean_windows(&self) { - let mut windows = self.windows.lock(); - windows.retain(|window| !window.requires_filesystem()); + pub fn clean_windows(&mut self, f: impl Fn(&T) -> bool) { + self.windows.retain(f); } /// Update and draw all windows. - pub fn update(&self, ctx: &egui::Context) { + pub fn display(&mut self, ctx: &egui::Context) { + let mut edit_windows = EditWindows:: { + clean_fn: None, + added: Vec::new(), + removed: std::collections::HashSet::new(), + }; + // Iterate through all the windows and clean them up if necessary. - let mut windows = self.windows.lock(); - windows.retain_mut(|window| { + self.windows.retain_mut(|window| { // Pass in a bool requesting to see if the window open. let mut open = true; window.show(ctx, &mut open); open }); + + for window in edit_windows.added { + self.add_window(window); + } + if let Some(f) = edit_windows.clean_fn { + self.clean_windows(f) + } + } +} + +impl EditWindows +where + T: Window, +{ + pub fn clean(&mut self, f: impl Fn(&T) -> bool + 'static) { + self.clean_fn = Some(Box::new(f)); + } + + pub fn add_window(&mut self, window: T) { + self.added.push(window) + } + + pub fn remove_window(&mut self, window: &T) -> bool { + self.remove_window_by_id(window.id()) + } + + pub fn remove_window_by_id(&mut self, id: egui::Id) -> bool { + self.removed.insert(id) } } @@ -88,3 +126,21 @@ pub trait Window { false } } + +impl Window for Box { + fn show(&mut self, ctx: &egui::Context, open: &mut bool) { + self.show(ctx, open) + } + + fn name(&self) -> String { + self.name() + } + + fn id(&self) -> egui::Id { + self.id() + } + + fn requires_filesystem(&self) -> bool { + self.requires_filesystem() + } +} diff --git a/crates/luminol-graphics/Cargo.toml b/crates/luminol-graphics/Cargo.toml index 47847152..d082e04a 100644 --- a/crates/luminol-graphics/Cargo.toml +++ b/crates/luminol-graphics/Cargo.toml @@ -8,14 +8,13 @@ edition = "2021" [dependencies] image = "0.24.7" -glam = { version = "0.24.2", features = ["bytemuck"] } - const_format = "0.2.32" egui.workspace = true egui_extras.workspace = true egui-wgpu.workspace = true wgpu.workspace = true +glam.workspace = true once_cell.workspace = true crossbeam.workspace = true diff --git a/crates/luminol-graphics/src/atlas_cache.rs b/crates/luminol-graphics/src/atlas_cache.rs index a05697d2..f995d2ed 100644 --- a/crates/luminol-graphics/src/atlas_cache.rs +++ b/crates/luminol-graphics/src/atlas_cache.rs @@ -25,15 +25,12 @@ impl Cache { &self, graphics_state: &crate::GraphicsState, filesystem: &impl luminol_filesystem::FileSystem, - image_cache: &crate::image_cache::Cache, tileset: &luminol_data::rpg::Tileset, ) -> Result { Ok(self .atlases .entry(tileset.id) - .or_try_insert_with(|| { - crate::tiles::Atlas::new(graphics_state, filesystem, image_cache, tileset) - })? + .or_try_insert_with(|| crate::tiles::Atlas::new(graphics_state, filesystem, tileset))? .clone()) } @@ -41,7 +38,6 @@ impl Cache { &self, graphics_state: &crate::GraphicsState, filesystem: &impl luminol_filesystem::FileSystem, - image_cache: &crate::image_cache::Cache, tileset: &luminol_data::rpg::Tileset, ) -> Result { Ok(self @@ -50,7 +46,6 @@ impl Cache { .insert(crate::tiles::Atlas::new( graphics_state, filesystem, - image_cache, tileset, )?) .clone()) diff --git a/crates/luminol-graphics/src/lib.rs b/crates/luminol-graphics/src/lib.rs index 21e74fb8..91d8a124 100644 --- a/crates/luminol-graphics/src/lib.rs +++ b/crates/luminol-graphics/src/lib.rs @@ -15,15 +15,15 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -mod quad; -mod sprite; -mod tiles; -mod vertex; -mod viewport; +pub mod quad; +pub mod sprite; +pub mod tiles; +pub mod vertex; +pub mod viewport; -mod event; -mod map; -mod plane; +pub mod event; +pub mod map; +pub mod plane; pub mod atlas_cache; pub mod image_cache; @@ -33,9 +33,9 @@ pub use map::Map; pub use plane::Plane; pub struct GraphicsState { - image_cache: image_cache::Cache, - atlas_cache: atlas_cache::Cache, - render_state: egui_wgpu::RenderState, + pub image_cache: image_cache::Cache, + pub atlas_cache: atlas_cache::Cache, + pub render_state: egui_wgpu::RenderState, pipelines: Pipelines, bind_group_layouts: BindGroupLayouts, diff --git a/crates/luminol-graphics/src/map.rs b/crates/luminol-graphics/src/map.rs index 7aadad35..87cd4bfa 100644 --- a/crates/luminol-graphics/src/map.rs +++ b/crates/luminol-graphics/src/map.rs @@ -47,12 +47,9 @@ impl Map { tileset: &luminol_data::rpg::Tileset, use_push_constants: bool, ) -> Result { - let atlas = graphics_state.atlas_cache.load_atlas( - graphics_state, - filesystem, - &graphics_state.image_cache, - tileset, - )?; + let atlas = graphics_state + .atlas_cache + .load_atlas(graphics_state, filesystem, tileset)?; let tiles = crate::tiles::Tiles::new(graphics_state, atlas, &map.data, use_push_constants); diff --git a/crates/luminol-graphics/src/tiles/atlas.rs b/crates/luminol-graphics/src/tiles/atlas.rs index 9a38059c..4211b402 100644 --- a/crates/luminol-graphics/src/tiles/atlas.rs +++ b/crates/luminol-graphics/src/tiles/atlas.rs @@ -54,11 +54,11 @@ impl Atlas { pub fn new( graphics_state: &crate::GraphicsState, filesystem: &impl luminol_filesystem::FileSystem, - image_cache: &crate::image_cache::Cache, tileset: &luminol_data::rpg::Tileset, ) -> Result { let tileset_img = tileset.tileset_name.as_ref().and_then(|tileset_name| { - let tileset_img = image_cache + let tileset_img = graphics_state + .image_cache .load_image(filesystem, "Graphics/Tilesets", tileset_name) .ok()?; Some(tileset_img.to_rgba8()) @@ -76,7 +76,8 @@ impl Atlas { if s.is_empty() { Ok(None) } else { - image_cache + graphics_state + .image_cache .load_wgpu_image(graphics_state, filesystem, "Graphics/Autotiles", s) .map(Some) }