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)
}