Skip to content

Commit

Permalink
fix some feature logic
Browse files Browse the repository at this point in the history
  • Loading branch information
B-Reif committed Jul 27, 2021
1 parent 6c2aa64 commit d575201
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 117 deletions.
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ edition = "2018"
bevy = "0.5"
asefile = { git = "https://github.com/B-Reif/asefile", branch = "main" }
anyhow = "1.0"
benimator = { version = "0.3.0", optional = true }
bevy_ecs_tilemap = { version = "0.4", optional = true }

[dev-dependencies]
strum = { version = "0.20", features = ["derive"] }
# benimator is used in the 'simple' example
benimator = "0.3.0"
bevy_sprite = { version = "^0.5.0", default-features = false }
# bevy_ecs_tilemap is used in the 'tilemap' example
bevy_ecs_tilemap = "0.4"

[profile.dev.package."*"]
opt-level = 2

[[example]]
name = "animated"
required-features = ["benimator"]
22 changes: 6 additions & 16 deletions examples/animated/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use std::{path::Path, time::Duration};
use std::path::Path;

use benimator;
use bevy::{input::system::exit_on_esc_system, prelude::*};
use bevy::{input::system::exit_on_esc_system, prelude::*, sprite::entity::SpriteSheetBundle};
use bevy_ase::{
self,
asset::{Animation, AseAsset},
loader::{self, Loader},
};
use bevy_sprite::entity::SpriteSheetBundle;

fn main() {
App::build()
Expand Down Expand Up @@ -51,7 +49,6 @@ pub fn spawn_sprites(
animations: Res<Assets<Animation>>,
mut sprite_sheet_animations: ResMut<Assets<benimator::SpriteSheetAnimation>>,
) {
//commands.spawn_bundle(OrthographicCameraBundle::new_2d());
commands.spawn_bundle({
let mut b = OrthographicCameraBundle::new_2d();
b.orthographic_projection.scale = 1.0 / 3.0; // scale to 3x
Expand All @@ -60,17 +57,10 @@ pub fn spawn_sprites(

let anims = animations.iter().enumerate();
for (idx, (_id, anim)) in anims {
let b_frames = anim
.frames()
.iter()
.map(|f| benimator::Frame {
duration: Duration::from_millis(f.duration_ms as u64),
index: f.sprite.atlas_index,
})
.collect();
let b_anim = benimator::SpriteSheetAnimation::from_frames(b_frames);
let b_handle = sprite_sheet_animations.add(b_anim);
let texture_atlas = anim.atlas();
// The "benimator" feature provides a From implementation to convert animations.
let anim: benimator::SpriteSheetAnimation = anim.into();
let anim_handle = sprite_sheet_animations.add(anim);
let x_position = idx as f32 * 50.0;

commands
Expand All @@ -79,7 +69,7 @@ pub fn spawn_sprites(
transform: Transform::from_xyz(x_position, 0.0, 0.0),
..Default::default()
})
.insert(b_handle)
.insert(anim_handle)
.insert(benimator::Play);
}
}
2 changes: 1 addition & 1 deletion src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ pub(crate) mod ase;
pub mod slice;
pub(crate) mod tileset;

pub use animation::{Animation, Frame};
pub use animation::{Animation, Frame, Sprite};
pub use ase::{AseAsset, AseId};
pub use tileset::{TileSize, Tileset, TilesetAseKey, TilesetId};
43 changes: 40 additions & 3 deletions src/asset/animation.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use crate::sprite::Sprite;
use asefile::{AsepriteFile, Tag};
use bevy::prelude::*;
use bevy::{reflect::TypeUuid, sprite::TextureAtlas};
use bevy::{
prelude::*,
reflect::TypeUuid,
render::texture::{Extent3d, TextureDimension, TextureFormat},
sprite::TextureAtlas,
};
use std::path::PathBuf;

/// A sprite-based animation.
Expand All @@ -28,6 +31,40 @@ impl Animation {
}
}

/// The sprite of an animation frame. Refers to an item in a sprite atlas.
#[derive(Debug)]
pub struct Sprite {
/// The index into the TextureAtlas for this sprite.
pub atlas_index: u32,
}

pub(crate) struct SpriteData<T> {
pub(crate) frame: u32,
pub(crate) texture: T,
pub(crate) duration: u32,
}
impl SpriteData<Texture> {
pub(crate) fn new(ase: &AsepriteFile, frame: u32) -> Self {
let img = ase.frame(frame).image();
let size = Extent3d {
width: ase.width() as u32,
height: ase.height() as u32,
depth: 1,
};
let texture = Texture::new_fill(
size,
TextureDimension::D2,
img.as_raw(),
TextureFormat::Rgba8UnormSrgb,
);
Self {
frame,
texture,
duration: ase.frame(frame).duration(),
}
}
}

/// A single frame in an [Animation].
#[derive(Debug)]
pub struct Frame {
Expand Down
11 changes: 6 additions & 5 deletions src/asset/ase.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
use std::{fmt, path::PathBuf};

use asefile::AsepriteFile;
use bevy::reflect::TypeUuid;
use bevy::utils::HashMap;
use std::{fmt, path::PathBuf};

/// Handle type for ase assets.
///
/// The [Loader] processes [AseAsset] instances and stores their data
/// in bevy's Assets resources (as Texture, [Animation], etc).
/// [crate::loader::Loader] processes [AseAsset] instances and stores their data
/// as various other data types in bevy's Assets resources.
///
/// Once an AseAsset has been processed into other resource types, its data is dropped.
///
/// # Examples
///
/// ```
/// use bevy::prelude::*;
/// use bevy_ase::loader::AseAsset;
/// use bevy_ase::asset::AseAsset;
///
/// // Convert an untyped handle into an AseAsset handle.
/// pub fn to_typed(handle: HandleUntyped) -> Handle<AseAsset> {
Expand Down
17 changes: 17 additions & 0 deletions src/benimator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use crate::asset::{Animation, Frame};
use std::time::Duration;

impl From<&Frame> for benimator::Frame {
fn from(f: &Frame) -> Self {
benimator::Frame {
duration: Duration::from_millis(f.duration_ms as u64),
index: f.sprite.atlas_index,
}
}
}
impl From<&Animation> for benimator::SpriteSheetAnimation {
fn from(a: &Animation) -> Self {
let frames = a.frames().iter().map(|f| f.into()).collect();
benimator::SpriteSheetAnimation::from_frames(frames)
}
}
93 changes: 85 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,106 @@
#![warn(missing_docs)]
//! Utilities for loading [`Aseprite`] files directly into a [`Bevy`] application.
//! Utilities for loading [`Aseprite`] files into a [`Bevy`] application.
//!
//! Provides an [`AssetLoader`] which reads .aseprite files directly into memory,
//! without an intermediate import step. The loader adds [`Resources`] generated
//! by the files' data. The added resources include several types:
//! Provides [Loader](loader::Loader), an [`AssetLoader`] struct which directly
//! reads .aseprite files without an intermediate import step.
//! The loader adds [`Resources`] generated by the files' data.
//! Systems can invoke the loader's methods to start loading files,
//! or query the loading state.
//!
//! # Resources
//!
//! This library creates several types of resources:
//!
//! - [`Texture`] data, which contains the file's images.
//! - [Animation] data.
//! - [Tileset] data (from files created in Aseprite v1.3 beta).
//! - [`TextureAtlas`] data, which contains mapping information for each sprite in a spritesheet.
//! - [Animation](asset::Animation) data.
//! - [Slice](asset::slice::Slice) data.
//! - [Tileset](asset::Tileset) data (from files created in Aseprite v1.3 beta).
//!
//! # Configuration
//!
//! This library exposes [a plugin](loader::AseLoaderDefaultPlugin) with default settings.
//! This plugin initializes all of the above resources as Asset types,
//! adds [Loader](loader::Loader) and [AseAssetLoader](loader::AseAssetLoader) resources,
//! and adds an [importer system function](loader::ase_importer) to process loaded ase data.
//! For a custom configuration, import the constituent parts and add them to AppBuilder directly.
//! The Texture resource is required to be initialized. Other asset types are optional.
//!
//! # Examples
//!
//! ```
//! use bevy::prelude::*;
//! use bevy_ase::asset::AseAsset;
//! use bevy_ase::loader::{AseLoaderDefaultPlugin, Loader};
//!
//! // Initialize and run a bevy app with the default bevy_ase configuration.
//! fn main() {
//! App::build()
//! .add_plugins(DefaultPlugins)
//! .add_plugin(AseLoaderDefaultPlugin)
//! .add_system(load_sprites.system());
//! }
//!
//! // Get an aseprite asset and send it to the loader.
//! pub fn load_sprites(asset_server: Res<AssetServer>, mut loader: ResMut<Loader>) {
//! let h: Handle<AseAsset> = asset_server.load(std::path::Path::new("sprites/hello.aseprite"));
//! loader.add(h.clone());
//! }
//! ```
//!
//! # Optional Features
//!
//! ## Benimator
//!
//! When compiled with the "benimator" feature, this library includes a From implementation
//! to convert [Animation](asset::Animation) assets into benimator [`SpriteSheetAnimation`] assets.
//!
//! ### Example
//!
//! ```
//! use bevy::prelude::*;
//! use bevy_ase;
//! #[cfg(feature = "benimator")]
//! use benimator;
//!
//! // Creates a benimator animation asset whenever a bevy_ase animation asset is created.
//! #[cfg(feature = "benimator")]
//! pub fn convert_animation(
//! mut event_reader: EventReader<AssetEvent<bevy_ase::asset::Animation>>,
//! animations: Res<Assets<bevy_ase::asset::Animation>>,
//! mut sprite_sheet_animations: ResMut<Assets<benimator::SpriteSheetAnimation>>,
//! ) {
//! for evt in event_reader.iter() {
//! if let AssetEvent::Created { handle } = evt {
//! // Unwrap: Responding to Asset Created event, so asset exists
//! let anim = animations.get(handle).unwrap();
//! let converted_animation = anim.into();
//! sprite_sheet_animations.add(converted_animation);
//! }
//! }
//! }
//! ```
//!
//! [`Texture`]: https://docs.rs/bevy/0.5.0/bevy/render/texture/index.html
//! [`TextureAtlas`]: https://docs.rs/bevy/0.5.0/bevy/sprite/struct.TextureAtlas.html
//! [`AssetLoader`]: https://docs.rs/bevy/0.5.0/bevy/asset/trait.AssetLoader.html
//! [`Bevy`]: https://bevyengine.org/
//! [`Aseprite`]: https://www.aseprite.org/
//! [`Resources`]: https://bevyengine.org/learn/book/getting-started/resources/
//! [`SpriteSheetAnimation`]: https://docs.rs/benimator/0.3.0/benimator/struct.SpriteSheetAnimation.html
/// Provides asset types for working with Aseprite data.
pub mod asset;
/// Implements conversions from bevy_ase assets into benimator assets.
#[cfg(feature = "benimator")]
pub mod benimator;

/// Provides systems and resources for loading Aseprite files.
///
/// The default loader configuration provided by [AseLoaderDefaultPlugin] contains
/// The default loader configuration provided by [loader::AseLoaderDefaultPlugin] contains
/// asset types and processing for all Aseprite data types provided by this library.
pub mod loader;
mod processing;
mod sprite;
#[cfg(test)]
mod tests;

Expand Down
23 changes: 14 additions & 9 deletions src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub struct AseLoaderDefaultPlugin;
impl Plugin for AseLoaderDefaultPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_asset::<AseAsset>()
.add_asset::<Texture>()
.add_asset::<TextureAtlas>()
.add_asset::<Animation>()
.add_asset::<Tileset>()
.add_asset::<Slice>()
Expand Down Expand Up @@ -114,15 +116,17 @@ impl AssetLoader for AseAssetLoader {
}
/// Provides methods for loading [AseAsset].
///
/// The [AseLoaderPlugin] adds this as a resource by default.
/// The [AseLoaderDefaultPlugin] adds this as a resource by default.
/// To load Aseprite files, or check their loading status, a system can accept the [Loader] as a parameter.
///
/// # Examples
///
/// ```
/// use bevy::prelude::*;
/// use bevy_ase::loader::Loader;
/// // Adds a Loader instance to the app's resources.
/// // The AseLoaderPlugin already does this by default.
/// fn build(&self, app: &mut AppBuilder) {
/// // The AseLoaderDefaultPlugin already does this by default.
/// fn build(app: &mut AppBuilder) {
/// app.init_resource::<Loader>();
/// }
/// ```
Expand Down Expand Up @@ -150,12 +154,13 @@ impl Loader {
///
/// ```
/// use bevy::prelude::*;
/// use bevy_ase::loader::{AseAsset, Loader};
/// use bevy_ase::asset::AseAsset;
/// use bevy_ase::loader::Loader;
/// use std::path::Path;
///
/// // System function which sends ase assets in the "sprites" folder to the loader.
/// pub fn load_sprites(asset_server: Res<AssetServer>, mut aseloader: ResMut<Loader>) {
/// let handles = asset_server.load_folder(Path::new("sprites")).unwrap();
/// let handles = asset_server.load_folder(std::path::Path::new("sprites")).unwrap();
/// for h in &handles {
/// aseloader.add(h.clone().typed::<AseAsset>());
/// }
Expand Down Expand Up @@ -258,8 +263,8 @@ impl Loader {
}

pub(crate) struct AseAssetResources<'a> {
pub textures: &'a mut Assets<Texture>,
pub animations: Option<&'a mut Assets<Animation>>,
pub textures: Option<&'a mut Assets<Texture>>,
pub atlases: Option<&'a mut Assets<TextureAtlas>>,
pub tilesets: Option<&'a mut Assets<Tileset>>,
pub slices: Option<&'a mut Assets<Slice>>,
Expand All @@ -284,7 +289,7 @@ pub fn ase_importer(
task_pool: ResMut<AsyncComputeTaskPool>,
mut aseassets: ResMut<Assets<AseAsset>>,
asset_server: Res<AssetServer>,
mut textures: Option<ResMut<Assets<Texture>>>,
mut textures: ResMut<Assets<Texture>>,
mut atlases: Option<ResMut<Assets<TextureAtlas>>>,
mut animations: Option<ResMut<Assets<Animation>>>,
mut tilesets: Option<ResMut<Assets<Tileset>>>,
Expand All @@ -297,14 +302,14 @@ pub fn ase_importer(
if loader.all_todo_handles_ready(&asset_server) {
loader.spawn_tasks(&task_pool, &mut aseassets);
}
let textures = textures.as_mut().map(DerefMut::deref_mut);
let textures = textures.deref_mut();
let atlases = atlases.as_mut().map(DerefMut::deref_mut);
let animations = animations.as_mut().map(DerefMut::deref_mut);
let tilesets = tilesets.as_mut().map(DerefMut::deref_mut);
let slices = slices.as_mut().map(DerefMut::deref_mut);
let resources = AseAssetResources {
animations,
textures,
animations,
atlases,
tilesets,
slices,
Expand Down
Loading

0 comments on commit d575201

Please sign in to comment.