diff --git a/crates/bevy_rpack/CHANGELOG.md b/crates/bevy_rpack/CHANGELOG.md index 201512d..65ef7a9 100644 --- a/crates/bevy_rpack/CHANGELOG.md +++ b/crates/bevy_rpack/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- more documentation ## [0.1.1] - 2025-01-27 diff --git a/crates/bevy_rpack/Cargo.toml b/crates/bevy_rpack/Cargo.toml index 01403a5..7166afd 100644 --- a/crates/bevy_rpack/Cargo.toml +++ b/crates/bevy_rpack/Cargo.toml @@ -8,7 +8,7 @@ homepage = "https://github.com/Leinnan/rpack" authors = ["Piotr Siuszko "] license = "MIT OR Apache-2.0" keywords = ["bevy", "2d", "plugin"] -exclude = ["assets","tiles","*.rpack_gen.json", "justfile"] +exclude = ["assets", "tiles", "*.rpack_gen.json", "justfile"] [features] default = ["bevy"] @@ -42,4 +42,4 @@ bevy = { version = "0.15", default-features = false, features = [ [lints.rust] unsafe_code = "forbid" -missing_docs = "warn" \ No newline at end of file +missing_docs = "warn" diff --git a/crates/bevy_rpack/README.md b/crates/bevy_rpack/README.md index 0decc33..5e38ac6 100644 --- a/crates/bevy_rpack/README.md +++ b/crates/bevy_rpack/README.md @@ -2,6 +2,64 @@ A Bevy plugin with support for the `rpack.json` atlases. +## Getting the rpack.json atlases + +Generating a rpack atlas can be done in two ways: +- using `rpack_egui` application at [itch.io](https://mevlyshkin.itch.io/rpack). Just drop in files into program, specify settings and it should be possible to download both the json and image file for atlas. +- using `rpack_cli` CLI tool as described below. + +### rpack_cli use guide + +First install the tool: + +```sh +cargo install rpack_cli +``` + +Guide assumptions: +- Commands are executed from the root of the Bevy project. +- Images used to generate the atlas are stored in subdirectories within the `tiles` directory of the Bevy project. +- Game assets are stored in the `assets` directory. + +In order to generate a tilemap execute this command: +```sh +rpack_cli generate --source-paths "tiles/**/*" --size 1024 assets/game_tilemap +``` +It is also possible to generate a config file for `rpack_cli` and then use it to generate a tilemaps: +```sh +rpack_cli config-create --source-paths "tiles/**/*" --size 1024 --output-path assets/tilemap tiles_config +rpack_cli tiles_config.rpack_gen.json +``` +Both ways result in creation of two files: +- `assets/tilemap.png` containing atlas image +- `assets/tilemap.rpack.json` containing atlas image frames data used by bevy plugin. + +Second way generated additional `tiles_config.rpack_gen.json` file that + +With those files generated it should be possible to use atlases like in the example from `Example` docs section. + +#### Bonus- generating tilemaps on build/runtime + +Since `rpack_cli` is a rust library it can be called easily from Bevy application. +It can be useful for building special version of the game for artists in the team so they can run the game and be sure that they don't forget to rebuild atlases after doing changes in graphics. + +In order to achieve that add this dependency to the project: +```toml +rpack_cli = "0.1" +``` +Then in `main.rs` just before `App::new()` add something like this: + +```rust +rpack_cli::TilemapGenerationConfig::read_from_file("example_config.rpack_gen.json") + .expect("Failed to read config") + .generate() + .expect("Failed to generate tilemap"); +``` + +Then on each run of the game it will regenerate the atlas before running the game. + +> Disclaimer: It should be used carefully and in most cases be called only in development builds on desktop platforms. + ## Example ```rust @@ -16,7 +74,7 @@ fn main() { App::new() .add_plugins((DefaultPlugins, RpackAssetPlugin)) .add_systems(Startup, setup) - .add_systems(Update, on_loaded) + .add_systems(Update, atlas_loaded) .run(); } @@ -25,27 +83,25 @@ fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2d); } -fn on_loaded( +fn atlas_loaded( mut ev_asset: EventReader>, atlases: RpackAtlases, mut commands: Commands, ) { - for ev in ev_asset.read() { - if !matches!(ev, AssetEvent::LoadedWithDependencies { id: _ }) { - continue; - } - - if let Ok(sprite) = atlases.try_make_sprite("agents/spaceAstronauts_005") { - commands.spawn(Sprite { - color: Color::linear_rgb(1.0, 0.0, 0.0), - ..sprite - }); - }; - if let Ok(image_node) = atlases.try_make_image_node("agents/spaceShips_006") { - commands.spawn(image_node); - } + if !ev_asset + .read() + .any(|ev| matches!(ev, AssetEvent::LoadedWithDependencies { id: _ })) + { + return; + } + if let Ok(sprite) = atlases.try_make_sprite("agents/spaceAstronauts_005") { + commands.spawn(sprite); + }; + if let Ok(image_node) = atlases.try_make_image_node("agents/spaceShips_006") { + commands.spawn(image_node); } } + ``` ## Licence diff --git a/crates/bevy_rpack/assets/tilemap.rpack.json b/crates/bevy_rpack/assets/tilemap.rpack.json index 568fb09..8c0a22d 100644 --- a/crates/bevy_rpack/assets/tilemap.rpack.json +++ b/crates/bevy_rpack/assets/tilemap.rpack.json @@ -3,12 +3,30 @@ "frames": [ { "frame": { - "h": 42, - "w": 42, - "x": 462, - "y": 89 + "h": 44, + "w": 34, + "x": 2, + "y": 2 }, - "key": "ship/spaceBuilding_001" + "key": "agents/spaceAstronauts_004" + }, + { + "frame": { + "h": 44, + "w": 37, + "x": 41, + "y": 2 + }, + "key": "agents/spaceAstronauts_005" + }, + { + "frame": { + "h": 44, + "w": 37, + "x": 83, + "y": 2 + }, + "key": "agents/spaceAstronauts_008" }, { "frame": { @@ -19,24 +37,6 @@ }, "key": "agents/spaceAstronauts_012" }, - { - "frame": { - "h": 44, - "w": 37, - "x": 41, - "y": 2 - }, - "key": "agents/spaceAstronauts_005" - }, - { - "frame": { - "h": 28, - "w": 30, - "x": 439, - "y": 31 - }, - "key": "effects/spaceEffects_010" - }, { "frame": { "h": 44, @@ -48,48 +48,21 @@ }, { "frame": { - "h": 84, - "w": 42, - "x": 146, - "y": 92 - }, - "key": "ship/spaceBuilding_002" - }, - { - "frame": { - "h": 44, - "w": 37, - "x": 83, + "h": 94, + "w": 100, + "x": 235, "y": 2 }, - "key": "agents/spaceAstronauts_008" + "key": "agents/spaceShips_003" }, { "frame": { - "h": 57, - "w": 55, - "x": 268, - "y": 101 + "h": 148, + "w": 94, + "x": 340, + "y": 2 }, - "key": "ship/spaceBuilding_012" - }, - { - "frame": { - "h": 36, - "w": 37, - "x": 158, - "y": 51 - }, - "key": "effects/spaceEffects_013" - }, - { - "frame": { - "h": 32, - "w": 32, - "x": 121, - "y": 51 - }, - "key": "effects/spaceEffects_012" + "key": "agents/spaceShips_006" }, { "frame": { @@ -100,15 +73,6 @@ }, "key": "agents/spaceShips_009" }, - { - "frame": { - "h": 46, - "w": 46, - "x": 217, - "y": 101 - }, - "key": "ship/spaceBuilding_006" - }, { "frame": { "h": 21, @@ -129,30 +93,39 @@ }, { "frame": { - "h": 22, - "w": 16, + "h": 28, + "w": 30, + "x": 439, + "y": 31 + }, + "key": "effects/spaceEffects_010" + }, + { + "frame": { + "h": 26, + "w": 26, "x": 474, - "y": 62 + "y": 31 }, - "key": "missiles/spaceMissiles_014" + "key": "effects/spaceEffects_011" }, { "frame": { - "h": 44, - "w": 34, - "x": 2, - "y": 2 + "h": 32, + "w": 32, + "x": 121, + "y": 51 }, - "key": "agents/spaceAstronauts_004" + "key": "effects/spaceEffects_012" }, { "frame": { - "h": 148, - "w": 94, - "x": 340, - "y": 2 + "h": 36, + "w": 37, + "x": 158, + "y": 51 }, - "key": "agents/spaceShips_006" + "key": "effects/spaceEffects_013" }, { "frame": { @@ -165,21 +138,12 @@ }, { "frame": { - "h": 94, - "w": 100, - "x": 235, - "y": 2 - }, - "key": "agents/spaceShips_003" - }, - { - "frame": { - "h": 26, - "w": 26, + "h": 22, + "w": 16, "x": 474, - "y": 31 + "y": 62 }, - "key": "effects/spaceEffects_011" + "key": "missiles/spaceMissiles_014" }, { "frame": { @@ -198,6 +162,42 @@ "y": 88 }, "key": "missiles/spaceMissiles_040" + }, + { + "frame": { + "h": 42, + "w": 42, + "x": 462, + "y": 89 + }, + "key": "ship/spaceBuilding_001" + }, + { + "frame": { + "h": 84, + "w": 42, + "x": 146, + "y": 92 + }, + "key": "ship/spaceBuilding_002" + }, + { + "frame": { + "h": 46, + "w": 46, + "x": 217, + "y": 101 + }, + "key": "ship/spaceBuilding_006" + }, + { + "frame": { + "h": 57, + "w": 55, + "x": 268, + "y": 101 + }, + "key": "ship/spaceBuilding_012" } ], "size": [ diff --git a/crates/bevy_rpack/examples/simple.rs b/crates/bevy_rpack/examples/simple.rs index 7cfa318..33c5655 100644 --- a/crates/bevy_rpack/examples/simple.rs +++ b/crates/bevy_rpack/examples/simple.rs @@ -11,7 +11,7 @@ fn main() { App::new() .add_plugins((DefaultPlugins, RpackAssetPlugin)) .add_systems(Startup, setup) - .add_systems(Update, on_loaded) + .add_systems(Update, atlas_loaded) .run(); } @@ -20,24 +20,21 @@ fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2d); } -fn on_loaded( +fn atlas_loaded( mut ev_asset: EventReader>, atlases: RpackAtlases, mut commands: Commands, ) { - for ev in ev_asset.read() { - if !matches!(ev, AssetEvent::LoadedWithDependencies { id: _ }) { - continue; - } - - if let Ok(sprite) = atlases.try_make_sprite("agents/spaceAstronauts_005") { - commands.spawn(Sprite { - color: Color::linear_rgb(1.0, 0.0, 0.0), - ..sprite - }); - }; - if let Ok(image_node) = atlases.try_make_image_node("agents/spaceShips_006") { - commands.spawn(image_node); - } + if !ev_asset + .read() + .any(|ev| matches!(ev, AssetEvent::LoadedWithDependencies { id: _ })) + { + return; + } + if let Ok(sprite) = atlases.try_make_sprite("agents/spaceAstronauts_005") { + commands.spawn(sprite); + }; + if let Ok(image_node) = atlases.try_make_image_node("agents/spaceShips_006") { + commands.spawn(image_node); } } diff --git a/crates/rpack_cli/CHANGELOG.md b/crates/rpack_cli/CHANGELOG.md new file mode 100644 index 0000000..3cc4220 --- /dev/null +++ b/crates/rpack_cli/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + +## [0.1.1] - 2025-01-27 + +### Added + +- When no arguments is provided program will look for config files in current directory and generate a tilemap if there is only one config file. +- Better logs. + +## [0.1.0] - 2025-01-14 + +- initial version \ No newline at end of file diff --git a/crates/rpack_cli/Cargo.toml b/crates/rpack_cli/Cargo.toml index 24557c3..bec4c72 100644 --- a/crates/rpack_cli/Cargo.toml +++ b/crates/rpack_cli/Cargo.toml @@ -5,11 +5,11 @@ description = "CLI application for generating rpack atlases" repository = "https://github.com/Leinnan/rpack.git" homepage = "https://github.com/Leinnan/rpack" license = "MIT OR Apache-2.0" -version = "0.2.0" +version = "0.1.1" edition = "2021" [features] -default = ["cli", "dds", "basis"] +default = ["cli", "dds"] cli = ["dep:clap", "dep:glob"] basis = ["dep:basis-universal"] dds = ["dep:image_dds"] diff --git a/crates/rpack_cli/src/lib.rs b/crates/rpack_cli/src/lib.rs index 9c4eeb5..6498813 100644 --- a/crates/rpack_cli/src/lib.rs +++ b/crates/rpack_cli/src/lib.rs @@ -79,6 +79,7 @@ pub enum SaveImageFormat { #[default] Png, Dds, + #[cfg(feature = "basis")] Basis, } @@ -87,6 +88,7 @@ impl Display for SaveImageFormat { match self { SaveImageFormat::Png => f.write_str(".png"), SaveImageFormat::Dds => f.write_str(".dds"), + #[cfg(feature = "basis")] SaveImageFormat::Basis => f.write_str(".basis"), } } @@ -272,17 +274,13 @@ impl TilemapGenerationConfig { } } }; - let working_dir = match std::path::absolute(dir) { - Ok(p) => p, - Err(e) => panic!("DUPA {:?}", e), - }; + let working_dir = std::path::absolute(dir)?; let mut file_paths: Vec = self .asset_patterns .iter() .flat_map(|pattern| { let p = format!("{}/{}", working_dir.to_string_lossy(), pattern); - println!("{}", p); glob::glob(&p).expect("Wrong pattern for assets").flatten() }) .filter(|e| e.is_file()) @@ -345,16 +343,20 @@ impl TilemapGenerationConfig { .image_data .save_with_format(&atlas_image_path, image::ImageFormat::Png)?; } + #[cfg(feature = "basis")] SaveImageFormat::Basis => { - #[cfg(feature = "basis")] spritesheet.save_as_basis(&atlas_image_path)?; - #[cfg(not(feature = "basis"))] panic!("Program is compiled without support for basis. Compile it yourself with feature `basis` enabled."); } } let json = serde_json::to_string_pretty(&spritesheet.atlas_asset_json)?; let mut file = std::fs::File::create(&atlas_config_path)?; file.write_all(json.as_bytes())?; + println!( + "Atlas from {} images saved at: {}", + spritesheet.atlas_asset.frames.len(), + atlas_config_path.display() + ); Ok(()) } diff --git a/crates/rpack_cli/src/main.rs b/crates/rpack_cli/src/main.rs index 5f28942..853053d 100644 --- a/crates/rpack_cli/src/main.rs +++ b/crates/rpack_cli/src/main.rs @@ -16,13 +16,41 @@ struct Args { fn main() -> anyhow::Result<()> { let args_os = std::env::args_os(); - if args_os.len() == 2 { - let arg = format!("{}", args_os.last().expect("msg").to_string_lossy()); - if Path::new(&arg).exists() && arg.ends_with("rpack_gen.json") { - let config = TilemapGenerationConfig::read_from_file(&arg)?; - config.generate()?; - return Ok(()); + match args_os.len() { + 2 => { + let arg = format!("{}", args_os.last().expect("msg").to_string_lossy()); + if Path::new(&arg).exists() && arg.ends_with("rpack_gen.json") { + let config = TilemapGenerationConfig::read_from_file(&arg)?; + config.generate()?; + return Ok(()); + } } + 1 => { + let rpack_files: Vec = glob::glob("./*rpack_gen.json") + .expect("Wrong pattern") + .flatten() + .collect(); + match rpack_files.len() { + 1 => { + println!( + "Generate tilemap from config file: {}", + rpack_files[0].as_path().display() + ); + let config = TilemapGenerationConfig::read_from_file(rpack_files[0].as_path())?; + config.generate()?; + return Ok(()); + } + 0 => {} + nr => { + eprintln!( + "{nr} config files in directory, pass off the filenames as argument to generate a tilemap:\n{:#?}", + rpack_files + ); + return Ok(()); + } + } + } + _ => {} } let args = Args::parse(); diff --git a/crates/rpack_egui/Cargo.toml b/crates/rpack_egui/Cargo.toml index 85c78ae..2358fa5 100644 --- a/crates/rpack_egui/Cargo.toml +++ b/crates/rpack_egui/Cargo.toml @@ -19,7 +19,7 @@ eframe = { version = "0.30", default-features = false, features = [ ] } log = "0.4" egui_json_tree = "0.10" -rpack_cli = { default-features = false, path = "../rpack_cli", version = "0.2" } +rpack_cli = { default-features = false, path = "../rpack_cli", version = "0.1" } # You only need serde if you want app persistence: serde = { version = "1", features = ["derive"] }