2020-10-18 20:48:15 +00:00
|
|
|
use crate::{
|
|
|
|
path::AssetPath, AssetIo, AssetIoError, AssetMeta, AssetServer, Assets, Handle, HandleId,
|
|
|
|
RefChangeChannel,
|
|
|
|
};
|
2020-05-15 23:55:44 +00:00
|
|
|
use anyhow::Result;
|
2021-10-03 19:23:44 +00:00
|
|
|
use bevy_ecs::system::{Res, ResMut};
|
2020-11-28 00:39:59 +00:00
|
|
|
use bevy_reflect::{TypeUuid, TypeUuidDynamic};
|
2020-10-20 00:29:31 +00:00
|
|
|
use bevy_utils::{BoxedFuture, HashMap};
|
2020-10-18 20:48:15 +00:00
|
|
|
use crossbeam_channel::{Receiver, Sender};
|
|
|
|
use downcast_rs::{impl_downcast, Downcast};
|
|
|
|
use std::path::Path;
|
2020-05-15 23:55:44 +00:00
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// A loader for an asset source.
|
|
|
|
///
|
|
|
|
/// Types implementing this trait are used by the asset server to load assets into their respective
|
|
|
|
/// asset storages.
|
2020-10-18 20:48:15 +00:00
|
|
|
pub trait AssetLoader: Send + Sync + 'static {
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Processes the asset in an asynchronous closure.
|
2020-10-20 00:29:31 +00:00
|
|
|
fn load<'a>(
|
|
|
|
&'a self,
|
|
|
|
bytes: &'a [u8],
|
|
|
|
load_context: &'a mut LoadContext,
|
|
|
|
) -> BoxedFuture<'a, Result<(), anyhow::Error>>;
|
2022-07-12 15:44:09 +00:00
|
|
|
|
|
|
|
/// Returns a list of extensions supported by this asset loader, without the preceding dot.
|
2020-05-15 23:55:44 +00:00
|
|
|
fn extensions(&self) -> &[&str];
|
2020-10-18 20:48:15 +00:00
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// An essential piece of data of an application.
|
|
|
|
///
|
|
|
|
/// Assets are the building blocks of games. They can be anything, from images and sounds to scenes
|
|
|
|
/// and scripts. In Bevy, an asset is any struct that has an unique type id, as shown below:
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use bevy_reflect::TypeUuid;
|
|
|
|
/// use serde::Deserialize;
|
|
|
|
///
|
|
|
|
/// #[derive(Debug, Deserialize, TypeUuid)]
|
|
|
|
/// #[uuid = "39cadc56-aa9c-4543-8640-a018b74b5052"]
|
|
|
|
/// pub struct CustomAsset {
|
|
|
|
/// pub value: i32,
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// See the `assets/custom_asset.rs` example in the repository for more details.
|
|
|
|
///
|
|
|
|
/// In order to load assets into your game you must either add them manually to an asset storage
|
|
|
|
/// with [`Assets::add`] or load them from the filesystem with [`AssetServer::load`].
|
2020-10-18 20:48:15 +00:00
|
|
|
pub trait Asset: TypeUuid + AssetDynamic {}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// An untyped version of the [`Asset`] trait.
|
2020-10-18 20:48:15 +00:00
|
|
|
pub trait AssetDynamic: Downcast + TypeUuidDynamic + Send + Sync + 'static {}
|
|
|
|
impl_downcast!(AssetDynamic);
|
|
|
|
|
|
|
|
impl<T> Asset for T where T: TypeUuid + AssetDynamic + TypeUuidDynamic {}
|
|
|
|
|
|
|
|
impl<T> AssetDynamic for T where T: Send + Sync + 'static + TypeUuidDynamic {}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// A complete asset processed in an [`AssetLoader`].
|
2021-01-31 21:54:15 +00:00
|
|
|
pub struct LoadedAsset<T: Asset> {
|
|
|
|
pub(crate) value: Option<T>,
|
2020-10-18 20:48:15 +00:00
|
|
|
pub(crate) dependencies: Vec<AssetPath<'static>>,
|
|
|
|
}
|
|
|
|
|
2021-01-31 21:54:15 +00:00
|
|
|
impl<T: Asset> LoadedAsset<T> {
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Creates a new loaded asset.
|
2021-01-31 21:54:15 +00:00
|
|
|
pub fn new(value: T) -> Self {
|
2020-10-18 20:48:15 +00:00
|
|
|
Self {
|
2021-01-31 21:54:15 +00:00
|
|
|
value: Some(value),
|
2020-10-18 20:48:15 +00:00
|
|
|
dependencies: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Adds a dependency on another asset at the provided path.
|
Shader Imports. Decouple Mesh logic from PBR (#3137)
## Shader Imports
This adds "whole file" shader imports. These come in two flavors:
### Asset Path Imports
```rust
// /assets/shaders/custom.wgsl
#import "shaders/custom_material.wgsl"
[[stage(fragment)]]
fn fragment() -> [[location(0)]] vec4<f32> {
return get_color();
}
```
```rust
// /assets/shaders/custom_material.wgsl
[[block]]
struct CustomMaterial {
color: vec4<f32>;
};
[[group(1), binding(0)]]
var<uniform> material: CustomMaterial;
```
### Custom Path Imports
Enables defining custom import paths. These are intended to be used by crates to export shader functionality:
```rust
// bevy_pbr2/src/render/pbr.wgsl
#import bevy_pbr::mesh_view_bind_group
#import bevy_pbr::mesh_bind_group
[[block]]
struct StandardMaterial {
base_color: vec4<f32>;
emissive: vec4<f32>;
perceptual_roughness: f32;
metallic: f32;
reflectance: f32;
flags: u32;
};
/* rest of PBR fragment shader here */
```
```rust
impl Plugin for MeshRenderPlugin {
fn build(&self, app: &mut bevy_app::App) {
let mut shaders = app.world.get_resource_mut::<Assets<Shader>>().unwrap();
shaders.set_untracked(
MESH_BIND_GROUP_HANDLE,
Shader::from_wgsl(include_str!("mesh_bind_group.wgsl"))
.with_import_path("bevy_pbr::mesh_bind_group"),
);
shaders.set_untracked(
MESH_VIEW_BIND_GROUP_HANDLE,
Shader::from_wgsl(include_str!("mesh_view_bind_group.wgsl"))
.with_import_path("bevy_pbr::mesh_view_bind_group"),
);
```
By convention these should use rust-style module paths that start with the crate name. Ultimately we might enforce this convention.
Note that this feature implements _run time_ import resolution. Ultimately we should move the import logic into an asset preprocessor once Bevy gets support for that.
## Decouple Mesh Logic from PBR Logic via MeshRenderPlugin
This breaks out mesh rendering code from PBR material code, which improves the legibility of the code, decouples mesh logic from PBR logic, and opens the door for a future `MaterialPlugin<T: Material>` that handles all of the pipeline setup for arbitrary shader materials.
## Removed `RenderAsset<Shader>` in favor of extracting shaders into RenderPipelineCache
This simplifies the shader import implementation and removes the need to pass around `RenderAssets<Shader>`.
## RenderCommands are now fallible
This allows us to cleanly handle pipelines+shaders not being ready yet. We can abort a render command early in these cases, preventing bevy from trying to bind group / do draw calls for pipelines that couldn't be bound. This could also be used in the future for things like "components not existing on entities yet".
# Next Steps
* Investigate using Naga for "partial typed imports" (ex: `#import bevy_pbr::material::StandardMaterial`, which would import only the StandardMaterial struct)
* Implement `MaterialPlugin<T: Material>` for low-boilerplate custom material shaders
* Move shader import logic into the asset preprocessor once bevy gets support for that.
Fixes #3132
2021-11-18 03:45:02 +00:00
|
|
|
pub fn add_dependency(&mut self, asset_path: AssetPath) {
|
2020-10-18 20:48:15 +00:00
|
|
|
self.dependencies.push(asset_path.to_owned());
|
Shader Imports. Decouple Mesh logic from PBR (#3137)
## Shader Imports
This adds "whole file" shader imports. These come in two flavors:
### Asset Path Imports
```rust
// /assets/shaders/custom.wgsl
#import "shaders/custom_material.wgsl"
[[stage(fragment)]]
fn fragment() -> [[location(0)]] vec4<f32> {
return get_color();
}
```
```rust
// /assets/shaders/custom_material.wgsl
[[block]]
struct CustomMaterial {
color: vec4<f32>;
};
[[group(1), binding(0)]]
var<uniform> material: CustomMaterial;
```
### Custom Path Imports
Enables defining custom import paths. These are intended to be used by crates to export shader functionality:
```rust
// bevy_pbr2/src/render/pbr.wgsl
#import bevy_pbr::mesh_view_bind_group
#import bevy_pbr::mesh_bind_group
[[block]]
struct StandardMaterial {
base_color: vec4<f32>;
emissive: vec4<f32>;
perceptual_roughness: f32;
metallic: f32;
reflectance: f32;
flags: u32;
};
/* rest of PBR fragment shader here */
```
```rust
impl Plugin for MeshRenderPlugin {
fn build(&self, app: &mut bevy_app::App) {
let mut shaders = app.world.get_resource_mut::<Assets<Shader>>().unwrap();
shaders.set_untracked(
MESH_BIND_GROUP_HANDLE,
Shader::from_wgsl(include_str!("mesh_bind_group.wgsl"))
.with_import_path("bevy_pbr::mesh_bind_group"),
);
shaders.set_untracked(
MESH_VIEW_BIND_GROUP_HANDLE,
Shader::from_wgsl(include_str!("mesh_view_bind_group.wgsl"))
.with_import_path("bevy_pbr::mesh_view_bind_group"),
);
```
By convention these should use rust-style module paths that start with the crate name. Ultimately we might enforce this convention.
Note that this feature implements _run time_ import resolution. Ultimately we should move the import logic into an asset preprocessor once Bevy gets support for that.
## Decouple Mesh Logic from PBR Logic via MeshRenderPlugin
This breaks out mesh rendering code from PBR material code, which improves the legibility of the code, decouples mesh logic from PBR logic, and opens the door for a future `MaterialPlugin<T: Material>` that handles all of the pipeline setup for arbitrary shader materials.
## Removed `RenderAsset<Shader>` in favor of extracting shaders into RenderPipelineCache
This simplifies the shader import implementation and removes the need to pass around `RenderAssets<Shader>`.
## RenderCommands are now fallible
This allows us to cleanly handle pipelines+shaders not being ready yet. We can abort a render command early in these cases, preventing bevy from trying to bind group / do draw calls for pipelines that couldn't be bound. This could also be used in the future for things like "components not existing on entities yet".
# Next Steps
* Investigate using Naga for "partial typed imports" (ex: `#import bevy_pbr::material::StandardMaterial`, which would import only the StandardMaterial struct)
* Implement `MaterialPlugin<T: Material>` for low-boilerplate custom material shaders
* Move shader import logic into the asset preprocessor once bevy gets support for that.
Fixes #3132
2021-11-18 03:45:02 +00:00
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Adds a dependency on another asset at the provided path.
|
2022-02-13 22:33:55 +00:00
|
|
|
#[must_use]
|
Shader Imports. Decouple Mesh logic from PBR (#3137)
## Shader Imports
This adds "whole file" shader imports. These come in two flavors:
### Asset Path Imports
```rust
// /assets/shaders/custom.wgsl
#import "shaders/custom_material.wgsl"
[[stage(fragment)]]
fn fragment() -> [[location(0)]] vec4<f32> {
return get_color();
}
```
```rust
// /assets/shaders/custom_material.wgsl
[[block]]
struct CustomMaterial {
color: vec4<f32>;
};
[[group(1), binding(0)]]
var<uniform> material: CustomMaterial;
```
### Custom Path Imports
Enables defining custom import paths. These are intended to be used by crates to export shader functionality:
```rust
// bevy_pbr2/src/render/pbr.wgsl
#import bevy_pbr::mesh_view_bind_group
#import bevy_pbr::mesh_bind_group
[[block]]
struct StandardMaterial {
base_color: vec4<f32>;
emissive: vec4<f32>;
perceptual_roughness: f32;
metallic: f32;
reflectance: f32;
flags: u32;
};
/* rest of PBR fragment shader here */
```
```rust
impl Plugin for MeshRenderPlugin {
fn build(&self, app: &mut bevy_app::App) {
let mut shaders = app.world.get_resource_mut::<Assets<Shader>>().unwrap();
shaders.set_untracked(
MESH_BIND_GROUP_HANDLE,
Shader::from_wgsl(include_str!("mesh_bind_group.wgsl"))
.with_import_path("bevy_pbr::mesh_bind_group"),
);
shaders.set_untracked(
MESH_VIEW_BIND_GROUP_HANDLE,
Shader::from_wgsl(include_str!("mesh_view_bind_group.wgsl"))
.with_import_path("bevy_pbr::mesh_view_bind_group"),
);
```
By convention these should use rust-style module paths that start with the crate name. Ultimately we might enforce this convention.
Note that this feature implements _run time_ import resolution. Ultimately we should move the import logic into an asset preprocessor once Bevy gets support for that.
## Decouple Mesh Logic from PBR Logic via MeshRenderPlugin
This breaks out mesh rendering code from PBR material code, which improves the legibility of the code, decouples mesh logic from PBR logic, and opens the door for a future `MaterialPlugin<T: Material>` that handles all of the pipeline setup for arbitrary shader materials.
## Removed `RenderAsset<Shader>` in favor of extracting shaders into RenderPipelineCache
This simplifies the shader import implementation and removes the need to pass around `RenderAssets<Shader>`.
## RenderCommands are now fallible
This allows us to cleanly handle pipelines+shaders not being ready yet. We can abort a render command early in these cases, preventing bevy from trying to bind group / do draw calls for pipelines that couldn't be bound. This could also be used in the future for things like "components not existing on entities yet".
# Next Steps
* Investigate using Naga for "partial typed imports" (ex: `#import bevy_pbr::material::StandardMaterial`, which would import only the StandardMaterial struct)
* Implement `MaterialPlugin<T: Material>` for low-boilerplate custom material shaders
* Move shader import logic into the asset preprocessor once bevy gets support for that.
Fixes #3132
2021-11-18 03:45:02 +00:00
|
|
|
pub fn with_dependency(mut self, asset_path: AssetPath) -> Self {
|
|
|
|
self.add_dependency(asset_path);
|
2020-10-18 20:48:15 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Adds dependencies on other assets at the provided paths.
|
2022-02-13 22:33:55 +00:00
|
|
|
#[must_use]
|
Shader Imports. Decouple Mesh logic from PBR (#3137)
## Shader Imports
This adds "whole file" shader imports. These come in two flavors:
### Asset Path Imports
```rust
// /assets/shaders/custom.wgsl
#import "shaders/custom_material.wgsl"
[[stage(fragment)]]
fn fragment() -> [[location(0)]] vec4<f32> {
return get_color();
}
```
```rust
// /assets/shaders/custom_material.wgsl
[[block]]
struct CustomMaterial {
color: vec4<f32>;
};
[[group(1), binding(0)]]
var<uniform> material: CustomMaterial;
```
### Custom Path Imports
Enables defining custom import paths. These are intended to be used by crates to export shader functionality:
```rust
// bevy_pbr2/src/render/pbr.wgsl
#import bevy_pbr::mesh_view_bind_group
#import bevy_pbr::mesh_bind_group
[[block]]
struct StandardMaterial {
base_color: vec4<f32>;
emissive: vec4<f32>;
perceptual_roughness: f32;
metallic: f32;
reflectance: f32;
flags: u32;
};
/* rest of PBR fragment shader here */
```
```rust
impl Plugin for MeshRenderPlugin {
fn build(&self, app: &mut bevy_app::App) {
let mut shaders = app.world.get_resource_mut::<Assets<Shader>>().unwrap();
shaders.set_untracked(
MESH_BIND_GROUP_HANDLE,
Shader::from_wgsl(include_str!("mesh_bind_group.wgsl"))
.with_import_path("bevy_pbr::mesh_bind_group"),
);
shaders.set_untracked(
MESH_VIEW_BIND_GROUP_HANDLE,
Shader::from_wgsl(include_str!("mesh_view_bind_group.wgsl"))
.with_import_path("bevy_pbr::mesh_view_bind_group"),
);
```
By convention these should use rust-style module paths that start with the crate name. Ultimately we might enforce this convention.
Note that this feature implements _run time_ import resolution. Ultimately we should move the import logic into an asset preprocessor once Bevy gets support for that.
## Decouple Mesh Logic from PBR Logic via MeshRenderPlugin
This breaks out mesh rendering code from PBR material code, which improves the legibility of the code, decouples mesh logic from PBR logic, and opens the door for a future `MaterialPlugin<T: Material>` that handles all of the pipeline setup for arbitrary shader materials.
## Removed `RenderAsset<Shader>` in favor of extracting shaders into RenderPipelineCache
This simplifies the shader import implementation and removes the need to pass around `RenderAssets<Shader>`.
## RenderCommands are now fallible
This allows us to cleanly handle pipelines+shaders not being ready yet. We can abort a render command early in these cases, preventing bevy from trying to bind group / do draw calls for pipelines that couldn't be bound. This could also be used in the future for things like "components not existing on entities yet".
# Next Steps
* Investigate using Naga for "partial typed imports" (ex: `#import bevy_pbr::material::StandardMaterial`, which would import only the StandardMaterial struct)
* Implement `MaterialPlugin<T: Material>` for low-boilerplate custom material shaders
* Move shader import logic into the asset preprocessor once bevy gets support for that.
Fixes #3132
2021-11-18 03:45:02 +00:00
|
|
|
pub fn with_dependencies(mut self, mut asset_paths: Vec<AssetPath<'static>>) -> Self {
|
|
|
|
for asset_path in asset_paths.drain(..) {
|
|
|
|
self.add_dependency(asset_path);
|
|
|
|
}
|
2020-10-18 20:48:15 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-31 21:54:15 +00:00
|
|
|
pub(crate) struct BoxedLoadedAsset {
|
|
|
|
pub(crate) value: Option<Box<dyn AssetDynamic>>,
|
|
|
|
pub(crate) dependencies: Vec<AssetPath<'static>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Asset> From<LoadedAsset<T>> for BoxedLoadedAsset {
|
|
|
|
fn from(asset: LoadedAsset<T>) -> Self {
|
|
|
|
BoxedLoadedAsset {
|
2021-03-25 20:48:18 +00:00
|
|
|
value: asset
|
|
|
|
.value
|
|
|
|
.map(|value| Box::new(value) as Box<dyn AssetDynamic>),
|
2021-01-31 21:54:15 +00:00
|
|
|
dependencies: asset.dependencies,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// An asynchronous context where an [`Asset`] is processed.
|
|
|
|
///
|
|
|
|
/// The load context is created by the [`AssetServer`] to process an asset source after loading its
|
|
|
|
/// contents into memory. It is then passed to the appropriate [`AssetLoader`] based on the file
|
|
|
|
/// extension of the asset's path.
|
|
|
|
///
|
|
|
|
/// An asset source can define one or more assets from a single source path. The main asset is set
|
|
|
|
/// using [`LoadContext::set_default_asset`] and sub-assets are defined with
|
|
|
|
/// [`LoadContext::set_labeled_asset`].
|
2020-10-18 20:48:15 +00:00
|
|
|
pub struct LoadContext<'a> {
|
|
|
|
pub(crate) ref_change_channel: &'a RefChangeChannel,
|
|
|
|
pub(crate) asset_io: &'a dyn AssetIo,
|
2021-01-31 21:54:15 +00:00
|
|
|
pub(crate) labeled_assets: HashMap<Option<String>, BoxedLoadedAsset>,
|
2020-10-18 20:48:15 +00:00
|
|
|
pub(crate) path: &'a Path,
|
|
|
|
pub(crate) version: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> LoadContext<'a> {
|
|
|
|
pub(crate) fn new(
|
|
|
|
path: &'a Path,
|
|
|
|
ref_change_channel: &'a RefChangeChannel,
|
|
|
|
asset_io: &'a dyn AssetIo,
|
|
|
|
version: usize,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
ref_change_channel,
|
|
|
|
asset_io,
|
|
|
|
labeled_assets: Default::default(),
|
|
|
|
version,
|
|
|
|
path,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Gets the source path for this load context.
|
2020-10-18 20:48:15 +00:00
|
|
|
pub fn path(&self) -> &Path {
|
2021-07-29 20:52:15 +00:00
|
|
|
self.path
|
2020-10-18 20:48:15 +00:00
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Returns `true` if the load context contains an asset with the specified label.
|
2020-10-18 20:48:15 +00:00
|
|
|
pub fn has_labeled_asset(&self, label: &str) -> bool {
|
|
|
|
self.labeled_assets.contains_key(&Some(label.to_string()))
|
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Sets the primary asset loaded from the asset source.
|
2021-01-31 21:54:15 +00:00
|
|
|
pub fn set_default_asset<T: Asset>(&mut self, asset: LoadedAsset<T>) {
|
|
|
|
self.labeled_assets.insert(None, asset.into());
|
2020-10-18 20:48:15 +00:00
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Sets a secondary asset loaded from the asset source.
|
2021-01-31 21:54:15 +00:00
|
|
|
pub fn set_labeled_asset<T: Asset>(&mut self, label: &str, asset: LoadedAsset<T>) -> Handle<T> {
|
2020-10-18 20:48:15 +00:00
|
|
|
assert!(!label.is_empty());
|
2021-01-31 21:54:15 +00:00
|
|
|
self.labeled_assets
|
|
|
|
.insert(Some(label.to_string()), asset.into());
|
2020-12-31 20:57:15 +00:00
|
|
|
self.get_handle(AssetPath::new_ref(self.path(), Some(label)))
|
2020-10-18 20:48:15 +00:00
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Gets a handle to an asset of type `T` from its id.
|
2020-10-18 20:48:15 +00:00
|
|
|
pub fn get_handle<I: Into<HandleId>, T: Asset>(&self, id: I) -> Handle<T> {
|
|
|
|
Handle::strong(id.into(), self.ref_change_channel.sender.clone())
|
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Reads the contents of the file at the specified path through the [`AssetIo`] associated
|
|
|
|
/// with this context.
|
2020-10-20 00:29:31 +00:00
|
|
|
pub async fn read_asset_bytes<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>, AssetIoError> {
|
|
|
|
self.asset_io.load_path(path.as_ref()).await
|
2020-10-18 20:48:15 +00:00
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Generates metadata for the assets managed by this load context.
|
2020-10-18 20:48:15 +00:00
|
|
|
pub fn get_asset_metas(&self) -> Vec<AssetMeta> {
|
|
|
|
let mut asset_metas = Vec::new();
|
2022-02-13 22:33:55 +00:00
|
|
|
for (label, asset) in &self.labeled_assets {
|
2020-10-18 20:48:15 +00:00
|
|
|
asset_metas.push(AssetMeta {
|
|
|
|
dependencies: asset.dependencies.clone(),
|
|
|
|
label: label.clone(),
|
|
|
|
type_uuid: asset.value.as_ref().unwrap().type_uuid(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
asset_metas
|
2020-05-15 23:55:44 +00:00
|
|
|
}
|
2021-04-30 20:12:50 +00:00
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Gets the asset I/O associated with this load context.
|
2022-05-02 18:04:47 +00:00
|
|
|
pub fn asset_io(&self) -> &dyn AssetIo {
|
|
|
|
self.asset_io
|
|
|
|
}
|
2020-05-15 23:55:44 +00:00
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// The result of loading an asset of type `T`.
|
2020-10-08 18:43:01 +00:00
|
|
|
#[derive(Debug)]
|
2021-10-03 19:23:44 +00:00
|
|
|
pub struct AssetResult<T> {
|
2022-07-12 15:44:09 +00:00
|
|
|
/// The asset itself.
|
2021-04-24 18:14:04 +00:00
|
|
|
pub asset: Box<T>,
|
2022-07-12 15:44:09 +00:00
|
|
|
/// The unique id of the asset.
|
2020-10-18 20:48:15 +00:00
|
|
|
pub id: HandleId,
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Change version.
|
2020-10-18 20:48:15 +00:00
|
|
|
pub version: usize,
|
2020-05-15 23:55:44 +00:00
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// An event channel used by asset server to update the asset storage of a `T` asset.
|
2020-10-08 18:43:01 +00:00
|
|
|
#[derive(Debug)]
|
2021-10-03 19:23:44 +00:00
|
|
|
pub struct AssetLifecycleChannel<T> {
|
2022-07-12 15:44:09 +00:00
|
|
|
/// The sender endpoint of the channel.
|
2020-10-18 20:48:15 +00:00
|
|
|
pub sender: Sender<AssetLifecycleEvent<T>>,
|
2022-07-12 15:44:09 +00:00
|
|
|
/// The receiver endpoint of the channel.
|
2020-10-18 20:48:15 +00:00
|
|
|
pub receiver: Receiver<AssetLifecycleEvent<T>>,
|
2020-05-15 23:55:44 +00:00
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Events for the [`AssetLifecycleChannel`].
|
2021-10-03 19:23:44 +00:00
|
|
|
pub enum AssetLifecycleEvent<T> {
|
2022-07-12 15:44:09 +00:00
|
|
|
/// An asset was created.
|
2020-10-18 20:48:15 +00:00
|
|
|
Create(AssetResult<T>),
|
2022-07-12 15:44:09 +00:00
|
|
|
/// An asset was freed.
|
2020-10-18 20:48:15 +00:00
|
|
|
Free(HandleId),
|
|
|
|
}
|
|
|
|
|
2022-07-12 15:44:09 +00:00
|
|
|
/// A trait for sending lifecycle notifications from assets in the asset server.
|
2020-10-18 20:48:15 +00:00
|
|
|
pub trait AssetLifecycle: Downcast + Send + Sync + 'static {
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Notifies the asset server that a new asset was created.
|
2020-10-18 20:48:15 +00:00
|
|
|
fn create_asset(&self, id: HandleId, asset: Box<dyn AssetDynamic>, version: usize);
|
2022-07-12 15:44:09 +00:00
|
|
|
/// Notifies the asset server that an asset was freed.
|
2020-10-18 20:48:15 +00:00
|
|
|
fn free_asset(&self, id: HandleId);
|
|
|
|
}
|
|
|
|
impl_downcast!(AssetLifecycle);
|
|
|
|
|
|
|
|
impl<T: AssetDynamic> AssetLifecycle for AssetLifecycleChannel<T> {
|
|
|
|
fn create_asset(&self, id: HandleId, asset: Box<dyn AssetDynamic>, version: usize) {
|
|
|
|
if let Ok(asset) = asset.downcast::<T>() {
|
|
|
|
self.sender
|
|
|
|
.send(AssetLifecycleEvent::Create(AssetResult {
|
2021-04-24 18:14:04 +00:00
|
|
|
asset,
|
2021-05-06 23:25:16 +00:00
|
|
|
id,
|
2020-10-18 20:48:15 +00:00
|
|
|
version,
|
|
|
|
}))
|
2022-02-13 22:33:55 +00:00
|
|
|
.unwrap();
|
2020-10-18 20:48:15 +00:00
|
|
|
} else {
|
2020-12-02 19:31:16 +00:00
|
|
|
panic!(
|
|
|
|
"Failed to downcast asset to {}.",
|
|
|
|
std::any::type_name::<T>()
|
|
|
|
);
|
2020-10-18 20:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn free_asset(&self, id: HandleId) {
|
|
|
|
self.sender.send(AssetLifecycleEvent::Free(id)).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-03 19:23:44 +00:00
|
|
|
impl<T> Default for AssetLifecycleChannel<T> {
|
2020-10-18 20:48:15 +00:00
|
|
|
fn default() -> Self {
|
2020-05-15 23:55:44 +00:00
|
|
|
let (sender, receiver) = crossbeam_channel::unbounded();
|
2020-10-18 20:48:15 +00:00
|
|
|
AssetLifecycleChannel { sender, receiver }
|
2020-05-15 23:55:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-29 17:52:39 +00:00
|
|
|
/// Updates the [`Assets`] collection according to the changes queued up by [`AssetServer`].
|
2020-10-18 20:48:15 +00:00
|
|
|
pub fn update_asset_storage_system<T: Asset + AssetDynamic>(
|
2020-06-06 00:32:32 +00:00
|
|
|
asset_server: Res<AssetServer>,
|
2021-06-02 02:30:14 +00:00
|
|
|
assets: ResMut<Assets<T>>,
|
2020-05-15 23:55:44 +00:00
|
|
|
) {
|
2021-06-02 02:30:14 +00:00
|
|
|
asset_server.update_asset_storage(assets);
|
2020-05-15 23:55:44 +00:00
|
|
|
}
|