Skip to content

Commit

Permalink
Merge pull request #1 from MrGVSV/bevy-0.6
Browse files Browse the repository at this point in the history
Add Support for Bevy 0.6
  • Loading branch information
MrGVSV authored Jan 8, 2022
2 parents 829b392 + b5580cf commit dfd66b3
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 65 deletions.
23 changes: 16 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "bevy_tile_atlas"
version = "0.1.4"
edition = "2018"
version = "0.2.0"
edition = "2021"
authors = ["Gino Valente <[email protected]>"]
description = "A TextureAtlas builder for ordered tilesets"
repository = "https://github.com/MrGVSV/bevy_tile_atlas"
Expand All @@ -10,14 +10,23 @@ keywords = ["bevy", "tile", "tileset", "texture", "ordered"]
readme = "README.md"
exclude = ["assets/**/*", ".github/**/*"]

[badges]
maintenance = { status = "as-is" }


[dependencies]
bevy = "0.5"
bevy_asset = { version = "0.6", default-features = false }
bevy_ecs = { version = "0.6", default-features = false }
bevy_log = { version = "0.6", default-features = false, optional = true }
bevy_math = { version = "0.6", default-features = false }
bevy_render = { version = "0.6", default-features = false }
bevy_sprite = { version = "0.6", default-features = false }
thiserror = "1.0.30"

[dev-dependencies]
bevy = "0.6"

[features]
default = ["debug"]
# Enables logging (specifically for warnings, errors, or automatic texture format conversions)
debug = ["bevy_log"]

[[example]]
name = "atlas"
path = "examples/atlas.rs"
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ This crate is essentially an augmentation of Bevy's own `TextureAtlasBuilder`.
Add to your `[dependencies]` list in `Cargo.toml`:

```toml
bevy_tile_atlas = "0.1.4"
bevy_tile_atlas = "0.2.0"
```

## Usage
Expand All @@ -40,7 +40,7 @@ use bevy_tile_atlas::TileAtlasBuilder;
/// Creates a tile-based, ordered `TextureAtlas`
///
/// Assumes that the given handles are all loaded and in their desired order
fn build_tileset(handles: Vec<Handle<Texture>>, textures: &mut Assets<Texture>) -> TextureAtlas {
fn build_tileset(handles: Vec<Handle<Image>>, textures: &mut Assets<Image>) -> TextureAtlas {
let mut builder = TileAtlasBuilder::default();

for handle in handles {
Expand All @@ -54,6 +54,13 @@ fn build_tileset(handles: Vec<Handle<Texture>>, textures: &mut Assets<Texture>)

> **Note:** Duplicate textures can be added. This is helpful for when tiles need to be at multiple indices at once.
## Bevy Compatibility

| bevy | bevy_tile_atlas |
| ---- | --------------- |
| 0.6 | 0.2.0 |
| 0.5 | 0.1.4 |

## FAQ

**If this was made for `bevy_ecs_tilemap`, why did you not submit it as a PR?**
Expand All @@ -66,4 +73,4 @@ I didn't have to enforce the tile restriction, but I didn't see this being used

**Is the order guaranteed for whole folders of textures?**

If you load from a whole folder, the order of insertion will depend on how Bevy chooses to load the files (which I think can vary, though I'm not sure). Therefore, it's recommended to either manually place the tile handles into a `Vec` or array, or use some other mechanism to automatically order them (i.e. a config file).
If you load from a whole folder, the order of insertion will depend on how Bevy chooses to load the files (which I think can vary, though I'm not sure). Therefore, it's recommended to either manually place the tile handles into a `Vec` or array, or use some other mechanism to automatically order them (i.e. a [config file).](https://github.com/MrGVSV/bevy_tileset)
19 changes: 7 additions & 12 deletions examples/atlas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,14 @@ use bevy::prelude::*;
use bevy_tile_atlas::TileAtlasBuilder;

fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
.init_resource::<TileHandles>()
.init_resource::<MyAtlas>()
.add_state(AppState::LoadTileset)
.add_system_set(SystemSet::on_enter(AppState::LoadTileset).with_system(load_tiles.system()))
.add_system_set(
SystemSet::on_update(AppState::CreateTileset).with_system(create_atlas.system()),
)
.add_system_set(
SystemSet::on_enter(AppState::DisplayTileset).with_system(display_atlas.system()),
)
.add_system_set(SystemSet::on_enter(AppState::LoadTileset).with_system(load_tiles))
.add_system_set(SystemSet::on_update(AppState::CreateTileset).with_system(create_atlas))
.add_system_set(SystemSet::on_enter(AppState::DisplayTileset).with_system(display_atlas))
.run();
}

Expand Down Expand Up @@ -63,7 +59,7 @@ fn load_tiles(

fn create_atlas(
mut atlas: ResMut<MyAtlas>,
mut textures: ResMut<Assets<Texture>>,
mut textures: ResMut<Assets<Image>>,
mut state: ResMut<State<AppState>>,
handles: Res<TileHandles>,
asset_server: Res<AssetServer>,
Expand All @@ -79,7 +75,7 @@ fn create_atlas(

for handle in &handles.0 {
if let Some(texture) = textures.get(handle) {
if let Ok(index) = builder.add_texture(handle.clone().typed::<Texture>(), texture) {
if let Ok(index) = builder.add_texture(handle.clone().typed::<Image>(), texture) {
println!("Added texture at index: {}", index);
}
}
Expand All @@ -100,7 +96,6 @@ fn create_atlas(
fn display_atlas(
mut atlas_res: ResMut<MyAtlas>,
mut commands: Commands,
mut materials: ResMut<Assets<ColorMaterial>>,
mut atlases: ResMut<Assets<TextureAtlas>>,
) {
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
Expand All @@ -122,7 +117,7 @@ fn display_atlas(

// Display the whole tileset
commands.spawn_bundle(SpriteBundle {
material: materials.add(handle.into()),
texture: handle,
..Default::default()
});

Expand Down
28 changes: 28 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
//! bevy_tile_atlas is a `TextureAtlas` builder for ordered tilesets.
//!
//! In other words, this crate is used to generate a `TileAtlas` that respects the order of insertion,
//! allowing its sub-textures to exist at known indices. This is helpful for texture animations, where
//! the frames are designated by a range of indices to loop through. It can also be helpful for retrieving
//! a sub-texture without needing access to its handle (i.e., "get texture at index at index 7" instead of
//! storing/passing around a `Handle<Image>`).
//!
//! ## Example
//! ```
//! # use bevy::prelude::*;
//! # use bevy_tile_atlas::TileAtlasBuilder;
//!
//! /// Creates a tile-based, ordered `TextureAtlas`
//! ///
//! /// Assumes that the given handles are all loaded and in their desired order
//! fn build_tileset(handles: Vec<Handle<Image>>, textures: &mut Assets<Image>) -> TextureAtlas {
//! let mut builder = TileAtlasBuilder::default();
//!
//! for handle in handles {
//! let texture = textures.get(&handle).unwrap();
//! builder.add_texture(handle, texture);
//! }
//!
//! builder.finish(textures).unwrap()
//! }
//! ```
mod store;
mod tile_atlas;

Expand Down
31 changes: 20 additions & 11 deletions src/store.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
use bevy::asset::{Assets, Handle, HandleId};
use bevy::ecs::component::Component;
use bevy::prelude::{ResMut, Texture};
//! Defines the `TextureStore` trait which is used by `TileAtlasBuilder` to manage its textures
use bevy_asset::{Assets, Handle, HandleId};
use bevy_ecs::system::{ResMut, Resource};
use bevy_render::texture::Image;
use std::ops::{Deref, DerefMut};

/// Trait used in the [`TileAtlasBuilder::finish`](crate::TileAtlasBuilder::finish) method to get and
/// add textures.
///
/// The reason for such a trait and not simply using `Assets<Image>` is to allow the builder to be used
/// in places where `Assets<Image>` might not be available (such as within a custom `AssetLoader`).
pub trait TextureStore {
fn add(&mut self, asset: Texture) -> Handle<Texture>;
fn get<H: Into<HandleId>>(&self, handle: H) -> Option<&Texture>;
/// Add a texture to the store
fn add(&mut self, asset: Image) -> Handle<Image>;
/// Get a texture from the store
fn get<H: Into<HandleId>>(&self, handle: H) -> Option<&Image>;
}

impl TextureStore for Assets<Texture> {
fn add(&mut self, asset: Texture) -> Handle<Texture> {
impl TextureStore for Assets<Image> {
fn add(&mut self, asset: Image) -> Handle<Image> {
self.add(asset)
}

fn get<H: Into<HandleId>>(&self, handle: H) -> Option<&Texture> {
fn get<H: Into<HandleId>>(&self, handle: H) -> Option<&Image> {
self.get(handle)
}
}

impl<'w, T: TextureStore + Component> TextureStore for ResMut<'w, T> {
fn add(&mut self, asset: Texture) -> Handle<Texture> {
impl<'w, T: TextureStore + Resource> TextureStore for ResMut<'w, T> {
fn add(&mut self, asset: Image) -> Handle<Image> {
self.deref_mut().add(asset)
}

fn get<H: Into<HandleId>>(&self, handle: H) -> Option<&Texture> {
fn get<H: Into<HandleId>>(&self, handle: H) -> Option<&Image> {
self.deref().get(handle)
}
}
85 changes: 53 additions & 32 deletions src/tile_atlas.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
//! Contains the atlas builder and its associated structs
use crate::TextureStore;
use bevy::log::{debug, error, warn};
use bevy::prelude::{Handle, Texture, TextureAtlas, Vec2};
use bevy::render::texture::{Extent3d, TextureDimension, TextureFormat};
use bevy::sprite::{Rect, TextureAtlasBuilderError};
use bevy_asset::Handle;
use bevy_math::Vec2;
use bevy_render::render_resource::{Extent3d, TextureDimension, TextureFormat};
use bevy_render::texture::{Image, TextureFormatPixelInfo};
use bevy_sprite::{Rect, TextureAtlas, TextureAtlasBuilderError};
use std::collections::HashMap;
use thiserror::Error;

Expand All @@ -28,7 +31,7 @@ pub struct TileAtlasBuilder {
/// If `None`, then no wrapping (i.e. single row)
max_columns: Option<usize>,
/// The ordered collection of texture handles in this atlas
handles: Vec<Handle<Texture>>,
handles: Vec<Handle<Image>>,
/// The texture format for the textures that will be loaded in the atlas.
format: TextureFormat,
/// Enable automatic format conversion for textures if they are not in the atlas format.
Expand Down Expand Up @@ -152,21 +155,30 @@ impl TileAtlasBuilder {
///
pub fn add_texture(
&mut self,
texture_handle: Handle<Texture>,
texture: &Texture,
texture_handle: Handle<Image>,
texture: &Image,
) -> Result<usize, TileAtlasBuilderError> {
if let Some(size) = self.tile_size {
if texture.size.width > size.x as u32 || texture.size.height > size.y as u32 {
if texture.texture_descriptor.size.width > size.x as u32
|| texture.texture_descriptor.size.height > size.y as u32
{
let expected = size;
let found = texture.size.as_vec3().truncate();
warn!(
let found = Vec2::new(
texture.texture_descriptor.size.width as f32,
texture.texture_descriptor.size.height as f32,
);
#[cfg(feature = "debug")]
bevy_log::warn!(
"The given texture does not fit into specified tile size (expected: {:?}, found: {:?}). Skipping...",
expected, found,
);
return Err(TileAtlasBuilderError::InvalidTileSize { expected, found });
}
} else {
let new_size = texture.size.as_vec3().truncate();
let new_size = Vec2::new(
texture.texture_descriptor.size.width as f32,
texture.texture_descriptor.size.height as f32,
);
self.tile_size = Some(new_size);
};

Expand All @@ -188,12 +200,12 @@ impl TileAtlasBuilder {

let total_rows = ((total as f32) / self.get_max_columns() as f32).ceil() as usize;

let mut atlas_texture = Texture::new_fill(
Extent3d::new(
(self.get_max_columns() as f32 * tile_size.x) as u32,
((total_rows as f32) * tile_size.y) as u32,
1,
),
let mut atlas_texture = Image::new_fill(
Extent3d {
width: (self.get_max_columns() as f32 * tile_size.x) as u32,
height: ((total_rows as f32) * tile_size.y) as u32,
depth_or_array_layers: 1,
},
TextureDimension::D2,
&[0, 0, 0, 0],
self.format,
Expand All @@ -212,10 +224,12 @@ impl TileAtlasBuilder {

texture_handles.insert(handle.clone_weak(), index);
texture_rects.push(Rect { min, max });
if texture.format != self.format && !self.auto_format_conversion {
warn!(
if texture.texture_descriptor.format != self.format && !self.auto_format_conversion {
#[cfg(feature = "debug")]
bevy_log::warn!(
"Loading a texture of format '{:?}' in an atlas with format '{:?}'",
texture.format, self.format
texture.texture_descriptor.format,
self.format
);
return Err(TileAtlasBuilderError::Internal(
TextureAtlasBuilderError::WrongFormat,
Expand All @@ -232,7 +246,10 @@ impl TileAtlasBuilder {
}

Ok(TextureAtlas {
size: atlas_texture.size.as_vec3().truncate(),
size: Vec2::new(
atlas_texture.texture_descriptor.size.width as f32,
atlas_texture.texture_descriptor.size.height as f32,
),
texture: textures.add(atlas_texture),
textures: texture_rects,
texture_handles: Some(texture_handles),
Expand All @@ -241,31 +258,35 @@ impl TileAtlasBuilder {

fn copy_converted_texture(
&self,
atlas_texture: &mut Texture,
texture: &Texture,
atlas_texture: &mut Image,
texture: &Image,
column_index: usize,
row_index: usize,
) {
if self.format == texture.format {
if self.format == texture.texture_descriptor.format {
self.copy_texture_to_atlas(atlas_texture, texture, column_index, row_index);
} else if let Some(converted_texture) = texture.convert(self.format) {
debug!(
#[cfg(feature = "debug")]
bevy_log::debug!(
"Converting texture from '{:?}' to '{:?}'",
texture.format, self.format
texture.texture_descriptor.format,
self.format
);
self.copy_texture_to_atlas(atlas_texture, &converted_texture, column_index, row_index);
} else {
error!(
#[cfg(feature = "debug")]
bevy_log::error!(
"Error converting texture from '{:?}' to '{:?}', ignoring",
texture.format, self.format
texture.texture_descriptor.format,
self.format
);
}
}

fn copy_texture_to_atlas(
&self,
atlas_texture: &mut Texture,
texture: &Texture,
atlas_texture: &mut Image,
texture: &Image,
column_index: usize,
row_index: usize,
) {
Expand All @@ -276,8 +297,8 @@ impl TileAtlasBuilder {
let rect_height = tile_size.y as usize;
let rect_x = column_index * tile_size.x as usize;
let rect_y = row_index * tile_size.y as usize;
let atlas_width = atlas_texture.size.width as usize;
let format_size = atlas_texture.format.pixel_size();
let atlas_width = atlas_texture.texture_descriptor.size.width as usize;
let format_size = atlas_texture.texture_descriptor.format.pixel_size();

for (texture_y, bound_y) in (rect_y..rect_y + rect_height).enumerate() {
let begin = (bound_y * atlas_width + rect_x) * format_size;
Expand Down

0 comments on commit dfd66b3

Please sign in to comment.