From a33dd60392bc6283ef87a77b9e1eca3a8f6b153d Mon Sep 17 00:00:00 2001 From: Piotr Siuszko Date: Thu, 9 Jan 2025 17:39:04 +0100 Subject: [PATCH] Bevy crate improvements, example, docs --- Cargo.lock | 377 ++++++++++++++++++- README.md | 20 +- crates/bevy_rpack/Cargo.toml | 22 +- crates/bevy_rpack/README.md | 59 +++ crates/bevy_rpack/assets/Tilemap.png | Bin 0 -> 8073 bytes crates/bevy_rpack/assets/tilemap.rpack.json | 72 ++++ crates/bevy_rpack/examples/simple.rs | 43 +++ crates/bevy_rpack/src/lib.rs | 25 +- crates/bevy_rpack/src/{bevy.rs => plugin.rs} | 118 +++++- crates/rpack/src/app.rs | 115 +----- crates/rpack/src/helpers.rs | 91 +++++ crates/rpack/src/lib.rs | 3 +- crates/rpack/src/main.rs | 4 +- 13 files changed, 825 insertions(+), 124 deletions(-) create mode 100644 crates/bevy_rpack/README.md create mode 100644 crates/bevy_rpack/assets/Tilemap.png create mode 100644 crates/bevy_rpack/assets/tilemap.rpack.json create mode 100644 crates/bevy_rpack/examples/simple.rs rename crates/bevy_rpack/src/{bevy.rs => plugin.rs} (53%) create mode 100644 crates/rpack/src/helpers.rs diff --git a/Cargo.lock b/Cargo.lock index 1f8b297..203f20b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,6 +253,15 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arbitrary" version = "1.4.1" @@ -811,6 +820,7 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b747210d7db09dfacc237707d4fd31c8b43d7744cd5e5829e2c4ca86b9e47baf" dependencies = [ + "arrayvec", "bevy_ecs_macros", "bevy_ptr", "bevy_reflect", @@ -941,6 +951,7 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd7fc4db9a1793ee71f79abb15e7a8fcfe4e2081e5f18ed91b802bf6cf30e088" dependencies = [ + "bevy_a11y", "bevy_app", "bevy_asset", "bevy_color", @@ -955,16 +966,22 @@ dependencies = [ "bevy_input", "bevy_log", "bevy_math", + "bevy_pbr", "bevy_picking", "bevy_ptr", "bevy_reflect", "bevy_render", "bevy_scene", "bevy_sprite", + "bevy_state", "bevy_tasks", + "bevy_text", "bevy_time", "bevy_transform", + "bevy_ui", "bevy_utils", + "bevy_window", + "bevy_winit", ] [[package]] @@ -1043,6 +1060,35 @@ dependencies = [ "glam", ] +[[package]] +name = "bevy_pbr" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7f17067399cf00f4441e93d39fb4c391a16cc223e0d35346ac388e66712c418" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_core_pipeline", + "bevy_derive", + "bevy_ecs", + "bevy_image", + "bevy_math", + "bevy_reflect", + "bevy_render", + "bevy_transform", + "bevy_utils", + "bevy_window", + "bitflags 2.6.0", + "bytemuck", + "derive_more", + "fixedbitset 0.5.7", + "nonmax", + "radsort", + "smallvec", + "static_assertions", +] + [[package]] name = "bevy_picking" version = "0.15.1" @@ -1223,19 +1269,75 @@ dependencies = [ "rectangle-pack", ] +[[package]] +name = "bevy_state" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd10c8b01a982642596406fc4486fcd52239aa9c4aa47fed27abab93a69fba59" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_hierarchy", + "bevy_reflect", + "bevy_state_macros", + "bevy_utils", +] + +[[package]] +name = "bevy_state_macros" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23773797bf8077a6ad9299f10b063b6947f22dad311d855c4b3523102ab4381b" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn 2.0.95", +] + [[package]] name = "bevy_tasks" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c28f2db2619203aa82342dbbe77e49aeea4f933212c0b7a1f285e94c4008e5b" dependencies = [ + "async-channel", "async-executor", + "concurrent-queue", "futures-channel", "futures-lite", "pin-project", "wasm-bindgen-futures", ] +[[package]] +name = "bevy_text" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ee0b5f52946d222521f93773a6230f42e868548f881c4c5bddb1393a96298b" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_image", + "bevy_math", + "bevy_reflect", + "bevy_render", + "bevy_sprite", + "bevy_transform", + "bevy_utils", + "bevy_window", + "cosmic-text", + "derive_more", + "serde", + "smallvec", + "sys-locale", + "unicode-bidi", +] + [[package]] name = "bevy_time" version = "0.15.1" @@ -1263,6 +1365,39 @@ dependencies = [ "derive_more", ] +[[package]] +name = "bevy_ui" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4556fc2202c6339f95e0c24ca4c96ee959854b702e23ecf73e05fb20e67d67b0" +dependencies = [ + "accesskit", + "bevy_a11y", + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_core_pipeline", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_image", + "bevy_input", + "bevy_math", + "bevy_picking", + "bevy_reflect", + "bevy_render", + "bevy_sprite", + "bevy_text", + "bevy_transform", + "bevy_utils", + "bevy_window", + "bytemuck", + "derive_more", + "nonmax", + "smallvec", + "taffy", +] + [[package]] name = "bevy_utils" version = "0.15.1" @@ -1307,6 +1442,35 @@ dependencies = [ "smol_str", ] +[[package]] +name = "bevy_winit" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36e84e7f94583cac93de4ba641029eb0b6551d35e559c829209f2b1b9fe532d8" +dependencies = [ + "accesskit", + "accesskit_winit", + "approx", + "bevy_a11y", + "bevy_app", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_input", + "bevy_log", + "bevy_math", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bevy_window", + "cfg-if", + "crossbeam-channel", + "raw-window-handle", + "wasm-bindgen", + "web-sys", + "winit", +] + [[package]] name = "bindgen" version = "0.70.1" @@ -1787,6 +1951,29 @@ dependencies = [ "libc", ] +[[package]] +name = "cosmic-text" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2" +dependencies = [ + "bitflags 2.6.0", + "fontdb", + "log", + "rangemap", + "rayon", + "rustc-hash", + "rustybuzz", + "self_cell", + "swash", + "sys-locale", + "ttf-parser 0.21.1", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", +] + [[package]] name = "cpufeatures" version = "0.2.16" @@ -2421,6 +2608,38 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +[[package]] +name = "font-types" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3971f9a5ca983419cdc386941ba3b9e1feba01a0ab888adf78739feb2798492" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7" +dependencies = [ + "roxmltree 0.20.0", +] + +[[package]] +name = "fontdb" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser 0.20.0", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -2730,6 +2949,12 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "grid" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be136d9dacc2a13cc70bb6c8f902b414fb2641f8db1314637c6b7933411a8f82" + [[package]] name = "guillotiere" version = "0.6.2" @@ -3855,7 +4080,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" dependencies = [ - "ttf-parser", + "ttf-parser 0.25.1", ] [[package]] @@ -4164,6 +4389,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" +[[package]] +name = "rangemap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" + [[package]] name = "rav1e" version = "0.7.1" @@ -4246,6 +4477,16 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" +[[package]] +name = "read-fonts" +version = "0.22.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69aacb76b5c29acfb7f90155d39759a29496aebb49395830e928a9703d2eec2f" +dependencies = [ + "bytemuck", + "font-types", +] + [[package]] name = "rectangle-pack" version = "0.4.2" @@ -4400,6 +4641,12 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + [[package]] name = "rpack" version = "0.1.0" @@ -4495,6 +4742,23 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.6.0", + "bytemuck", + "libm", + "smallvec", + "ttf-parser 0.21.1", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + [[package]] name = "ryu" version = "1.0.18" @@ -4522,6 +4786,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "self_cell" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" + [[package]] name = "send_wrapper" version = "0.6.0" @@ -4645,6 +4915,16 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "skrifa" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1c44ad1f6c5bdd4eefed8326711b7dbda9ea45dfd36068c427d332aa382cbe" +dependencies = [ + "bytemuck", + "read-fonts", +] + [[package]] name = "slab" version = "0.4.9" @@ -4806,6 +5086,17 @@ dependencies = [ "siphasher", ] +[[package]] +name = "swash" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd59f3f359ddd2c95af4758c18270eddd9c730dde98598023cdabff472c2ca2" +dependencies = [ + "skrifa", + "yazi", + "zeno", +] + [[package]] name = "syn" version = "1.0.109" @@ -4839,6 +5130,15 @@ dependencies = [ "syn 2.0.95", ] +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -4852,6 +5152,19 @@ dependencies = [ "version-compare", ] +[[package]] +name = "taffy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb893bff0f80ae17d3a57e030622a967b8dbc90e38284d9b4b1442e23873c94" +dependencies = [ + "arrayvec", + "grid", + "num-traits", + "serde", + "slotmap", +] + [[package]] name = "target-lexicon" version = "0.12.16" @@ -5132,6 +5445,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + [[package]] name = "ttf-parser" version = "0.25.1" @@ -5167,12 +5492,48 @@ version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-script" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" + [[package]] name = "unicode-segmentation" version = "1.12.0" @@ -5256,7 +5617,7 @@ dependencies = [ "imagesize", "kurbo", "log", - "roxmltree", + "roxmltree 0.19.0", "simplecss", "siphasher", "svgtypes", @@ -6095,6 +6456,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" +[[package]] +name = "yazi" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" + [[package]] name = "yoke" version = "0.7.5" @@ -6281,6 +6648,12 @@ dependencies = [ "zvariant 4.2.0", ] +[[package]] +name = "zeno" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/README.md b/README.md index 6e8b32d..596d8c0 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,26 @@ # rpack [![Build Status](https://github.com/Leinnan/rpack/workflows/CI/badge.svg)](https://github.com/Leinnan/rpack/actions?workflow=CI) +Create spritesheets in seconds! + +## rPack egui + +To open it in browser click one of the icons below: [![Itch.io](https://img.shields.io/badge/Itch-%23FF0B34.svg?style=for-the-badge&logo=Itch.io&logoColor=white)](https://mevlyshkin.itch.io/rpack) [![Github Pages](https://img.shields.io/badge/github%20pages-121013?style=for-the-badge&logo=github&logoColor=white)](http://rpack.mevlyshkin.com/) +A both desktop and web frontend for generating tilemaps. Just drag and drop images into the program and generate tilemaps. -Create spritesheets in seconds! - -Attempt to build texture atlas packer GUI. +Source code is available in `crates/rpack` directory of the repo. ![rpack_ebVVrMf3wm](https://github.com/user-attachments/assets/bb015348-3c1f-46be-9312-963b4f39f9c0) + +## Bevy rPack + +A Bevy plugin with support for the `rpack.json` atlases. + +More info available at [crates/bevy_rpack](https://github.com/Leinnan/rpack/tree/master/crates/bevy_rpack). + +## rPack CLI + +Command line interface for generating tilemaps. Code stored in `crates/rpack_cli` directory. Right now this is the part of the project that could change the most. \ No newline at end of file diff --git a/crates/bevy_rpack/Cargo.toml b/crates/bevy_rpack/Cargo.toml index 07da8ed..4f7c90a 100644 --- a/crates/bevy_rpack/Cargo.toml +++ b/crates/bevy_rpack/Cargo.toml @@ -17,7 +17,27 @@ bevy = { version = "0.15", optional = true, default-features = false, features = "bevy_asset", "bevy_sprite", "bevy_image", + "bevy_ui", ] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -thiserror = "2.0" \ No newline at end of file +thiserror = "2.0" + +[dev-dependencies] +bevy = { version = "0.15", default-features = false, features = [ + "bevy_asset", + "bevy_core_pipeline", + "bevy_render", + "bevy_sprite", + "bevy_state", + "bevy_window", + "bevy_winit", + "bevy_ui", + "multi_threaded", + "png", + "webgl2", +] } + +[lints.rust] +unsafe_code = "forbid" +missing_docs = "warn" \ No newline at end of file diff --git a/crates/bevy_rpack/README.md b/crates/bevy_rpack/README.md new file mode 100644 index 0000000..e9ddf50 --- /dev/null +++ b/crates/bevy_rpack/README.md @@ -0,0 +1,59 @@ +# Bevy_rPack + +A Bevy plugin with support for the `rpack.json` atlases. + +## Example + +```rust +use bevy::prelude::*; +use bevy_rpack::prelude::*; + +#[allow(dead_code)] +#[derive(Resource)] +struct Holder(pub Handle); + +fn main() { + App::new() + .add_plugins((DefaultPlugins, RpackAssetPlugin)) + .add_systems(Startup, setup) + .add_systems(Update, on_loaded) + .run(); +} + +fn setup(mut commands: Commands, asset_server: Res) { + commands.insert_resource(Holder(asset_server.load("Tilemap.rpack.json"))); + commands.spawn(Camera2d); +} + +fn on_loaded( + mut ev_asset: EventReader>, + assets: Res>, + mut commands: Commands, +) { + for ev in ev_asset.read() { + let AssetEvent::LoadedWithDependencies { id: _ } = ev else { + continue; + }; + + if let Ok(sprite) = assets.make_sprite_from_atlas("Sword006") { + commands.spawn(Sprite { + color: Color::linear_rgb(1.0, 0.0, 0.0), + ..sprite + }); + }; + if let Ok(image_node) = assets.make_image_node_from_atlas("Axe010") { + commands.spawn(image_node); + } + } +} +``` + +## Licence + +`bevy_rpack` is dual-licensed under MIT and Apache 2.0 at your option. + +## Bevy compatibility table + +Bevy version | Crate version +--- | --- +0.15 | 0.1 diff --git a/crates/bevy_rpack/assets/Tilemap.png b/crates/bevy_rpack/assets/Tilemap.png new file mode 100644 index 0000000000000000000000000000000000000000..a2d197aa00b4e7dfa4cd8bd8cf1c94de56ec4b2e GIT binary patch literal 8073 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 IZ{7C)1$5B`$p8QV literal 0 HcmV?d00001 diff --git a/crates/bevy_rpack/assets/tilemap.rpack.json b/crates/bevy_rpack/assets/tilemap.rpack.json new file mode 100644 index 0000000..284c9a1 --- /dev/null +++ b/crates/bevy_rpack/assets/tilemap.rpack.json @@ -0,0 +1,72 @@ +{ + "filename": "tilemap.png", + "frames": [ + { + "frame": { + "h": 34, + "w": 34, + "x": 74, + "y": 2 + }, + "key": "Sword007" + }, + { + "frame": { + "h": 34, + "w": 34, + "x": 38, + "y": 2 + }, + "key": "Axe011" + }, + { + "frame": { + "h": 34, + "w": 34, + "x": 2, + "y": 74 + }, + "key": "Axe010" + }, + { + "frame": { + "h": 34, + "w": 34, + "x": 2, + "y": 2 + }, + "key": "Sword006" + }, + { + "frame": { + "h": 34, + "w": 34, + "x": 2, + "y": 38 + }, + "key": "Sword009" + }, + { + "frame": { + "h": 34, + "w": 34, + "x": 74, + "y": 38 + }, + "key": "Axe009" + }, + { + "frame": { + "h": 34, + "w": 34, + "x": 38, + "y": 38 + }, + "key": "Sword008" + } + ], + "size": [ + 128, + 128 + ] +} \ No newline at end of file diff --git a/crates/bevy_rpack/examples/simple.rs b/crates/bevy_rpack/examples/simple.rs new file mode 100644 index 0000000..64f27d8 --- /dev/null +++ b/crates/bevy_rpack/examples/simple.rs @@ -0,0 +1,43 @@ +//! Simple example that loads the tilemap and once is loaded it creates a sprite with it. + +use bevy::prelude::*; +use bevy_rpack::prelude::*; + +#[allow(dead_code)] +#[derive(Resource)] +struct Holder(pub Handle); + +fn main() { + App::new() + .add_plugins((DefaultPlugins, RpackAssetPlugin)) + .add_systems(Startup, setup) + .add_systems(Update, on_loaded) + .run(); +} + +fn setup(mut commands: Commands, asset_server: Res) { + commands.insert_resource(Holder(asset_server.load("tilemap.rpack.json"))); + commands.spawn(Camera2d); +} + +fn on_loaded( + mut ev_asset: EventReader>, + assets: Res>, + mut commands: Commands, +) { + for ev in ev_asset.read() { + let AssetEvent::LoadedWithDependencies { id: _ } = ev else { + continue; + }; + + if let Ok(sprite) = assets.make_sprite_from_atlas("Sword006") { + commands.spawn(Sprite { + color: Color::linear_rgb(1.0, 0.0, 0.0), + ..sprite + }); + }; + if let Ok(image_node) = assets.make_image_node_from_atlas("Axe010") { + commands.spawn(image_node); + } + } +} diff --git a/crates/bevy_rpack/src/lib.rs b/crates/bevy_rpack/src/lib.rs index c72d56b..d2a3822 100644 --- a/crates/bevy_rpack/src/lib.rs +++ b/crates/bevy_rpack/src/lib.rs @@ -1,16 +1,24 @@ -#[cfg(feature = "bevy")] -mod bevy; +#![doc = include_str!("../README.md")] +#[cfg(feature = "bevy")] +/// Contains the Bevy plugin for handling `Rpack` assets and atlases. +mod plugin; + +/// Re-exports all types for working with texture atlases. pub mod prelude { #[cfg(feature = "bevy")] - pub use super::bevy::{ - RpackAssetPlugin, RpackAtlasAsset, RpackAtlasAssetError, RpackAtlasAssetLoader, + /// Provides easy access to `Rpack` asset-related functionality in a Bevy application. + pub use super::plugin::{ + RpackAssetHelper, RpackAssetPlugin, RpackAtlasAsset, RpackAtlasAssetError, + RpackAtlasAssetLoader, RpackAtlasError, }; + /// Re-exports core types for working with texture atlases. pub use super::{AtlasAsset, AtlasFrame, SerializableRect}; } /// Defines a rectangle in pixels with the origin at the top-left of the texture atlas. #[derive(Copy, Clone, Debug, serde::Deserialize, serde::Serialize)] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] pub struct SerializableRect { /// Horizontal position the rectangle begins at. pub x: u32, @@ -22,15 +30,24 @@ pub struct SerializableRect { pub h: u32, } +/// Represents a single frame within a texture atlas, including its identifier and position. #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Reflect))] pub struct AtlasFrame { + /// A unique identifier for the frame. pub key: String, + /// The rectangular area of the frame within the texture atlas. pub frame: SerializableRect, } +/// Represents an entire texture atlas asset, including its metadata and frames. #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[cfg_attr(feature = "bevy", derive(bevy::prelude::Asset, bevy::prelude::Reflect))] pub struct AtlasAsset { + /// The overall dimensions of the texture atlas in pixels (width, height). pub size: [u32; 2], + /// The filename associated with the atlas, typically used for loading or debugging. pub filename: String, + /// A collection of frames contained within the texture atlas. pub frames: Vec, } diff --git a/crates/bevy_rpack/src/bevy.rs b/crates/bevy_rpack/src/plugin.rs similarity index 53% rename from crates/bevy_rpack/src/bevy.rs rename to crates/bevy_rpack/src/plugin.rs index 24f8507..fed2fd8 100644 --- a/crates/bevy_rpack/src/bevy.rs +++ b/crates/bevy_rpack/src/plugin.rs @@ -4,6 +4,17 @@ use bevy::image::ImageSampler; use bevy::{prelude::*, utils::HashMap}; use thiserror::Error; +/// Errors that can occur while accessing and creating components from [`RpackAtlasAsset`]. +#[derive(Debug, Error)] +pub enum RpackAtlasError { + /// An error that occured due to no atlas being loaded yet + #[error("There is no atlas.")] + NoAtlas, + /// An error that occured because atlas does not contain provided key. + #[error("There is no frame with provided key.")] + WrongKey, +} + /// This is an asset containing the texture atlas image, the texture atlas layout, and a map of the original file names to their corresponding indices in the texture atlas. #[derive(Asset, Debug, Reflect)] pub struct RpackAtlasAsset { @@ -27,29 +38,100 @@ impl From for URect { } } -impl RpackAtlasAsset { - // When atlas contains the given key returns a copy of TextureAtlas and Image - pub fn get_atlas_data>(&self, key: T) -> Option<(TextureAtlas, Handle)> { - self.files.get(key.as_ref()).map(|s| { - ( +/// A helper trait for accessing and creating components from `Rpack` atlas data. +#[allow(dead_code)] +pub trait RpackAssetHelper { + /// Retrieves the atlas data (texture atlas and image) for the given atlas key, if available in any of the loaded Atlases. + fn find_atlas_data_by_key>( + &self, + key: T, + ) -> Result<(TextureAtlas, Handle), RpackAtlasError>; + /// Creates a [`Sprite`] component for the given atlas key, if available in any of the loaded Atlases. + fn make_sprite_from_atlas>(&self, key: T) -> Result; + /// Creates a [`ImageNode`] component for the given atlas key, if available in any of the loaded Atlases. + fn make_image_node_from_atlas>( + &self, + key: T, + ) -> Result; +} + +impl RpackAssetHelper for Assets { + fn find_atlas_data_by_key>( + &self, + key: T, + ) -> Result<(TextureAtlas, Handle), RpackAtlasError> { + if self.is_empty() { + return Err(RpackAtlasError::NoAtlas); + } + for (_, a) in self.iter() { + if let Ok(atlas_data) = a.find_atlas_data_by_key(key.as_ref()) { + return Ok(atlas_data); + } + } + Err(RpackAtlasError::WrongKey) + } + + fn make_sprite_from_atlas>(&self, key: T) -> Result { + if self.is_empty() { + return Err(RpackAtlasError::NoAtlas); + } + for (_, a) in self.iter() { + if let Ok(sprite) = a.make_sprite_from_atlas(key.as_ref()) { + return Ok(sprite); + } + } + Err(RpackAtlasError::WrongKey) + } + + fn make_image_node_from_atlas>( + &self, + key: T, + ) -> Result { + if self.is_empty() { + return Err(RpackAtlasError::NoAtlas); + } + for (_, a) in self.iter() { + if let Ok(image_node) = a.make_image_node_from_atlas(key.as_ref()) { + return Ok(image_node); + } + } + Err(RpackAtlasError::WrongKey) + } +} + +impl RpackAssetHelper for RpackAtlasAsset { + fn find_atlas_data_by_key>( + &self, + key: T, + ) -> Result<(TextureAtlas, Handle), RpackAtlasError> { + match self.files.get(key.as_ref()) { + Some(s) => Ok(( TextureAtlas { index: *s, layout: self.atlas.clone(), }, self.image.clone(), - ) - }) + )), + None => Err(RpackAtlasError::WrongKey), + } } - // When atlas contains the given key creates a Sprite component - pub fn make_sprite>(&self, key: T) -> Option { - if let Some((atlas, image)) = self.get_atlas_data(key) { - Some(Sprite { - image, - texture_atlas: Some(atlas), - ..Default::default() - }) + + fn make_sprite_from_atlas>(&self, key: T) -> Result { + if let Ok((atlas, image)) = self.find_atlas_data_by_key(key) { + Ok(Sprite::from_atlas_image(image, atlas)) } else { - None + Err(RpackAtlasError::WrongKey) + } + } + + fn make_image_node_from_atlas>( + &self, + key: T, + ) -> Result { + if let Ok((atlas, image)) = self.find_atlas_data_by_key(key) { + Ok(ImageNode::from_atlas_image(image, atlas)) + } else { + Err(RpackAtlasError::WrongKey) } } } @@ -69,12 +151,14 @@ pub struct RpackAssetPlugin; impl Plugin for RpackAssetPlugin { fn build(&self, app: &mut App) { + app.register_type::(); app.register_type::(); app.init_asset::(); app.init_asset_loader::(); } } +/// Errors that can occur while loading or processing a `RpackAtlasAsset`. #[non_exhaustive] #[derive(Debug, Error)] pub enum RpackAtlasAssetError { @@ -82,6 +166,7 @@ pub enum RpackAtlasAssetError { /// during parsing of a `.rpack.json` file. #[error("could not load asset: {0}")] Io(#[from] std::io::Error), + /// An error that occurred while parsing the `.rpack.json` file into an asset structure. #[error("could not parse asset: {0}")] ParsingError(#[from] serde_json::Error), /// A Bevy [`LoadDirectError`](bevy::asset::LoadDirectError) that occured @@ -101,6 +186,7 @@ impl From for RpackAtlasAssetError { } } +/// The loader responsible for loading `RpackAtlasAsset` files from `.rpack.json` files. #[derive(Default)] pub struct RpackAtlasAssetLoader; diff --git a/crates/rpack/src/app.rs b/crates/rpack/src/app.rs index 543beb1..98e63fd 100644 --- a/crates/rpack/src/app.rs +++ b/crates/rpack/src/app.rs @@ -1,9 +1,11 @@ -use std::{collections::HashMap, path::Path}; +use std::collections::HashMap; use egui::{CollapsingHeader, Color32, DroppedFile, FontFamily, FontId, Image, RichText}; -use image::{DynamicImage, GenericImageView}; +use image::GenericImageView; use rpack_cli::{ImageFile, Spritesheet}; -use texture_packer::{importer::ImageImporter, TexturePackerConfig}; +use texture_packer::TexturePackerConfig; + +use crate::helpers::DroppedFileHelper; pub const MY_ACCENT_COLOR32: Color32 = Color32::from_rgb(230, 102, 1); pub const TOP_SIDE_MARGIN: f32 = 10.0; pub const HEADER_HEIGHT: f32 = 45.0; @@ -13,7 +15,7 @@ pub const GIT_HASH: &str = env!("GIT_HASH"); /// We derive Deserialize/Serialize so we can persist app state on shutdown. #[derive(serde::Deserialize, serde::Serialize)] #[serde(default)] // if we add new fields, give them default values when deserializing old state -pub struct TemplateApp { +pub struct Application { #[serde(skip)] dropped_files: Vec, #[serde(skip)] @@ -33,7 +35,7 @@ pub struct TemplateApp { #[serde(skip)] image_data: HashMap, } -impl Default for TemplateApp { +impl Default for Application { fn default() -> Self { Self { dropped_files: vec![], @@ -57,13 +59,14 @@ impl Default for TemplateApp { } } -impl TemplateApp { +impl Application { pub fn rebuild_image_data(&mut self) { - let prefix = Self::get_common_prefix(&self.dropped_files); + let prefix = crate::helpers::get_common_prefix(&self.dropped_files); + self.image_data = self .dropped_files .iter() - .flat_map(|f| Self::image_from_dropped_file(f, &prefix)) + .flat_map(|f| f.create_image(&prefix)) .collect(); self.update_min_size(); } @@ -102,48 +105,6 @@ impl TemplateApp { Default::default() } - fn get_common_prefix(paths: &[DroppedFile]) -> String { - if paths.is_empty() { - return String::new(); - } else if paths.len() == 1 { - let full_name = paths[0].file_path(); - let path = Path::new(&full_name) - .file_name() - .unwrap_or_default() - .to_str() - .unwrap_or_default(); - return full_name.strip_suffix(&path).unwrap_or_default().to_owned(); - } - let mut prefix = paths[0].file_path(); - - for s in paths.iter().skip(1) { - let s = s.file_path(); - while !s.starts_with(&prefix) { - prefix.pop(); // Remove the last character of the prefix - if prefix.is_empty() { - return String::new(); - } - } - } - - prefix - } - pub fn image_from_dropped_file

(file: &DroppedFile, prefix: P) -> Option<(String, ImageFile)> - where - P: AsRef, - { - let path = file.file_path(); - let base_id = path.replace(".png", ""); - - let id = base_id - .strip_prefix(prefix.as_ref()) - .unwrap_or(&base_id) - .to_owned() - .replace("\\", "/"); - - let image: DynamicImage = dynamic_image_from_file(file)?; - Some((path, ImageFile { id, image })) - } fn build_atlas(&mut self, ctx: &egui::Context) { self.data = None; @@ -232,7 +193,7 @@ impl TemplateApp { } } -impl eframe::App for TemplateApp { +impl eframe::App for Application { /// Called by the frame work to save state before shutdown. fn save(&mut self, storage: &mut dyn eframe::Storage) { eframe::set_value(storage, eframe::APP_KEY, self); @@ -273,6 +234,10 @@ impl eframe::App for TemplateApp { .min_width(200.0) .frame(egui::Frame::canvas(&ctx.style())) .show_animated(ctx, !self.image_data.is_empty(), |ui| { + + egui::ScrollArea::vertical() + .id_salt("leftPanel_scroll") + .show(ui, |ui| { CollapsingHeader::new("Settings") .default_open(true) .show(ui, |ui| { @@ -350,6 +315,7 @@ impl eframe::App for TemplateApp { } } }); + }); }); egui::CentralPanel::default().show(ctx, |ui| { egui::ScrollArea::vertical() @@ -415,8 +381,10 @@ impl eframe::App for TemplateApp { .add(egui::Button::new("Copy JSON to Clipboard")) .clicked() { + let s = data.atlas_asset_json.to_string(); + println!("JSON: {s}"); ui.output_mut(|o| { - o.copied_text = data.atlas_asset_json.to_string() + o.copied_text = s; }); }; ui.add_space(10.0); @@ -442,49 +410,6 @@ impl eframe::App for TemplateApp { } } -trait FilePath { - fn file_path(&self) -> String; -} - -impl FilePath for DroppedFile { - fn file_path(&self) -> String { - let id; - #[cfg(not(target_arch = "wasm32"))] - { - let path = self.path.as_ref().unwrap().clone(); - id = path.to_str().unwrap().to_owned(); - } - #[cfg(target_arch = "wasm32")] - { - id = self.name.clone(); - } - id.replace(".png", "") - } -} - -fn dynamic_image_from_file(file: &DroppedFile) -> Option { - #[cfg(target_arch = "wasm32")] - { - let bytes = file.bytes.as_ref().clone()?; - - if let Ok(r) = ImageImporter::import_from_memory(bytes) { - Some(r.into()) - } else { - None - } - } - #[cfg(not(target_arch = "wasm32"))] - { - let path = file.path.as_ref()?; - - if let Ok(r) = ImageImporter::import_from_file(path) { - Some(r) - } else { - None - } - } -} - fn powered_by_egui_and_eframe(ui: &mut egui::Ui) { ui.horizontal(|ui| { ui.hyperlink_to(format!("Build: {}", GIT_HASH), env!("CARGO_PKG_HOMEPAGE")); diff --git a/crates/rpack/src/helpers.rs b/crates/rpack/src/helpers.rs new file mode 100644 index 0000000..4116488 --- /dev/null +++ b/crates/rpack/src/helpers.rs @@ -0,0 +1,91 @@ +use std::path::Path; + +use egui::DroppedFile; +use image::DynamicImage; +use rpack_cli::ImageFile; +use texture_packer::importer::ImageImporter; + +pub fn get_common_prefix(paths: &[DroppedFile]) -> String { + if paths.is_empty() { + return String::new(); + } + let full_name = paths[0].file_path(); + let path = Path::new(&full_name) + .file_name() + .unwrap_or_default() + .to_str() + .unwrap_or_default(); + + let mut prefix = full_name.strip_suffix(&path).unwrap_or_default().to_owned(); + + for s in paths.iter().skip(1) { + let s = s.file_path(); + while !(s.starts_with(&prefix) || prefix.is_empty()) { + prefix.pop(); + } + } + + prefix +} + +pub trait DroppedFileHelper { + fn file_path(&self) -> String; + fn create_image

(&self, prefix: P) -> Option<(String, ImageFile)> + where + P: AsRef; + fn dynamic_image(&self) -> Option; +} + +impl DroppedFileHelper for DroppedFile { + fn file_path(&self) -> String { + let id; + #[cfg(not(target_arch = "wasm32"))] + { + let path = self.path.as_ref().unwrap().clone(); + id = path.to_str().unwrap().to_owned(); + } + #[cfg(target_arch = "wasm32")] + { + id = self.name.clone(); + } + id.replace(".png", "").replace("\\", "/") + } + fn create_image

(&self, prefix: P) -> Option<(String, ImageFile)> + where + P: AsRef, + { + let path = self.file_path(); + let base_id = path.replace(".png", ""); + + let id = base_id + .strip_prefix(prefix.as_ref()) + .unwrap_or(&base_id) + .to_owned(); + + let image: DynamicImage = self.dynamic_image()?; + Some((path, ImageFile { id, image })) + } + + fn dynamic_image(&self) -> Option { + #[cfg(target_arch = "wasm32")] + { + let bytes = self.bytes.as_ref().clone()?; + + if let Ok(r) = ImageImporter::import_from_memory(bytes) { + Some(r.into()) + } else { + None + } + } + #[cfg(not(target_arch = "wasm32"))] + { + let path = self.path.as_ref()?; + + if let Ok(r) = ImageImporter::import_from_file(path) { + Some(r) + } else { + None + } + } + } +} diff --git a/crates/rpack/src/lib.rs b/crates/rpack/src/lib.rs index f47af5d..030ef99 100644 --- a/crates/rpack/src/lib.rs +++ b/crates/rpack/src/lib.rs @@ -2,4 +2,5 @@ mod app; mod fonts; -pub use app::TemplateApp; +mod helpers; +pub use app::Application; diff --git a/crates/rpack/src/main.rs b/crates/rpack/src/main.rs index 5cc2a87..fe0437c 100644 --- a/crates/rpack/src/main.rs +++ b/crates/rpack/src/main.rs @@ -15,7 +15,7 @@ fn main() -> eframe::Result<()> { eframe::run_native( "rPack", native_options, - Box::new(|cc| Ok(Box::new(rpack::TemplateApp::new(cc)))), + Box::new(|cc| Ok(Box::new(rpack::Application::new(cc)))), ) } @@ -40,7 +40,7 @@ fn main() { .start( canvas, web_options, - Box::new(|cc| Ok(Box::new(rpack::TemplateApp::new(cc)))), + Box::new(|cc| Ok(Box::new(rpack::Application::new(cc)))), ) .await;