Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ability to provide custom a AssetIo implementation #1037

Merged
merged 2 commits into from
Dec 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ path = "examples/asset/asset_loading.rs"
name = "custom_asset"
path = "examples/asset/custom_asset.rs"

[[example]]
name = "custom_asset_io"
path = "examples/asset/custom_asset_io.rs"

[[example]]
name = "audio"
path = "examples/audio/audio.rs"
Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_asset/src/asset_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ impl Clone for AssetServer {

impl AssetServer {
pub fn new<T: AssetIo>(source_io: T, task_pool: TaskPool) -> Self {
Self::with_boxed_io(Box::new(source_io), task_pool)
}

pub fn with_boxed_io(asset_io: Box<dyn AssetIo>, task_pool: TaskPool) -> Self {
AssetServer {
server: Arc::new(AssetServerInternal {
loaders: Default::default(),
Expand All @@ -69,7 +73,7 @@ impl AssetServer {
handle_to_path: Default::default(),
asset_lifecycles: Default::default(),
task_pool,
asset_io: Box::new(source_io),
asset_io,
}),
}
}
Expand Down
54 changes: 33 additions & 21 deletions crates/bevy_asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,28 +51,41 @@ impl Default for AssetServerSettings {
}
}

/// Create an instance of the platform default `AssetIo`
///
/// This is useful when providing a custom `AssetIo` instance that needs to
/// delegate to the default `AssetIo` for the platform.
pub fn create_platform_default_asset_io(app: &mut AppBuilder) -> Box<dyn AssetIo> {
let settings = app
.resources_mut()
.get_or_insert_with(AssetServerSettings::default);

#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
let source = FileAssetIo::new(&settings.asset_folder);
#[cfg(target_arch = "wasm32")]
let source = WasmAssetIo::new(&settings.asset_folder);
#[cfg(target_os = "android")]
let source = AndroidAssetIo::new(&settings.asset_folder);

Box::new(source)
}

impl Plugin for AssetPlugin {
fn build(&self, app: &mut AppBuilder) {
let task_pool = app
.resources()
.get::<IoTaskPool>()
.expect("`IoTaskPool` resource not found.")
.0
.clone();

let asset_server = {
let settings = app
.resources_mut()
.get_or_insert_with(AssetServerSettings::default);

#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
let source = FileAssetIo::new(&settings.asset_folder);
#[cfg(target_arch = "wasm32")]
let source = WasmAssetIo::new(&settings.asset_folder);
#[cfg(target_os = "android")]
let source = AndroidAssetIo::new(&settings.asset_folder);
AssetServer::new(source, task_pool)
};
if app.resources().get::<AssetServer>().is_none() {
let task_pool = app
.resources()
.get::<IoTaskPool>()
.expect("`IoTaskPool` resource not found.")
.0
.clone();

let source = create_platform_default_asset_io(app);

let asset_server = AssetServer::with_boxed_io(source, task_pool);

app.add_resource(asset_server);
}

app.add_stage_before(
bevy_app::stage::PRE_UPDATE,
Expand All @@ -84,7 +97,6 @@ impl Plugin for AssetPlugin {
stage::ASSET_EVENTS,
SystemStage::parallel(),
)
.add_resource(asset_server)
.register_type::<HandleId>()
.add_system_to_stage(
bevy_app::stage::PRE_UPDATE,
Expand Down
104 changes: 104 additions & 0 deletions examples/asset/custom_asset_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use bevy::{
asset::{AssetIo, AssetIoError},
prelude::*,
utils::BoxedFuture,
};
use std::path::{Path, PathBuf};

/// A custom asset io implementation that simply defers to the platform default
/// implementation.
///
/// This can be used as a starting point for developing a useful implementation
/// that can defer to the default when needed.
struct CustomAssetIo(Box<dyn AssetIo>);

impl AssetIo for CustomAssetIo {
fn load_path<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<Vec<u8>, AssetIoError>> {
println!("load_path({:?})", path);
self.0.load_path(path)
}

fn read_directory(
&self,
path: &Path,
) -> Result<Box<dyn Iterator<Item = PathBuf>>, AssetIoError> {
println!("read_directory({:?})", path);
self.0.read_directory(path)
}

fn is_directory(&self, path: &Path) -> bool {
println!("is_directory({:?})", path);
self.0.is_directory(path)
}

fn watch_path_for_changes(&self, path: &Path) -> Result<(), AssetIoError> {
println!("watch_path_for_changes({:?})", path);
self.0.watch_path_for_changes(path)
}

fn watch_for_changes(&self) -> Result<(), AssetIoError> {
println!("watch_for_changes()");
self.0.watch_for_changes()
}
}

/// A plugin used to execute the override of the asset io
struct CustomAssetIoPlugin;

impl Plugin for CustomAssetIoPlugin {
fn build(&self, app: &mut AppBuilder) {
// must get a hold of the task pool in order to create the asset server

let task_pool = app
.resources()
.get::<bevy::tasks::IoTaskPool>()
.expect("`IoTaskPool` resource not found.")
.0
.clone();

let asset_io = {
// the platform default asset io requires a reference to the app
// builder to find its configuration

let default_io = bevy::asset::create_platform_default_asset_io(app);

// create the custom asset io instance

CustomAssetIo(default_io)
};

// the asset server is constructed and added the resource manager

app.add_resource(AssetServer::new(asset_io, task_pool));
}
}

fn main() {
App::build()
.add_plugins_with(DefaultPlugins, |group| {
// the custom asset io plugin must be inserted in-between the
// `CorePlugin' and `AssetPlugin`. It needs to be after the
// CorePlugin, so that the IO task pool has already been constructed.
// And it must be before the `AssetPlugin` so that the asset plugin
// doesn't create another instance of an assert server. In general,
// the AssetPlugin should still run so that other aspects of the
// asset system are initialized correctly.
group.add_before::<bevy::asset::AssetPlugin, _>(CustomAssetIoPlugin)
})
.add_startup_system(setup)
.run();
}

fn setup(
commands: &mut Commands,
asset_server: Res<AssetServer>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
let texture_handle = asset_server.load("branding/icon.png");
commands
.spawn(Camera2dBundle::default())
.spawn(SpriteBundle {
material: materials.add(texture_handle.into()),
..Default::default()
});
}