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

Support multiple different AssetLoaders with the same extension #367

Closed
skairunner opened this issue Aug 27, 2020 · 1 comment · Fixed by #10153
Closed

Support multiple different AssetLoaders with the same extension #367

skairunner opened this issue Aug 27, 2020 · 1 comment · Fixed by #10153
Labels
A-Assets Load files from disk to use for things like images, models, and sounds C-Enhancement A new feature

Comments

@skairunner
Copy link

Currently, the AssetServer stores its references to handlers as a dictionary of string to box. But the file extension to asset type relationship is not one-to-one -- with GLTF, the file can contain anything from Meshes and Materials to Textures to Skins (aka bones).

In a language with reflection, it would be possible to select handlers depending on the type. While Rust doesn't have true reflection, it does have TypeId which would allow us to match on both AssetLoader type and file extension. This might be a good solution that would also let us preserve the existing API. This could also solve the TODO in the asset server file that mentions that load() needs to be strongly typed.

Another solution that is slightly less ergonomic is to create a load_a method that takes a String specifying the type of the asset we want to load as well as the path.

If we can determine a good path to take, I may be able to implement it, since I am working on something that directly requires this support. (No extra credit for guessing that it's GLTF :D )

@skairunner skairunner changed the title Supporting multiple different AssetLoaders with the same extension Support multiple different AssetLoaders with the same extension Aug 27, 2020
@karroffel karroffel added A-Assets Load files from disk to use for things like images, models, and sounds C-Enhancement A new feature labels Aug 27, 2020
@blaind
Copy link
Contributor

blaind commented Aug 27, 2022

What about allowing multiple loaders, where additional loaders for the extension could use set_labeled_asset instead of set_default_asset?

impl AssetLoader for FontLoader {
    fn load<'a>(
        &'a self,
        bytes: &'a [u8],
        load_context: &'a mut LoadContext,
    ) -> BoxedFuture<'a, Result<()>> {
        Box::pin(async move {
            load_context.set_labeled_asset("mesh", LoadedAsset::new(...));
            Ok(())
        })
    }

    fn extensions(&self) -> &[&str] {
        &["ttf"]
    }
}

Although, then the user needs to know the label when loading

let asset = asset_server.load("fonts/FiraMono-Medium.ttf#mesh");

github-merge-queue bot pushed a commit that referenced this issue Jan 31, 2024
# Objective

- Addresses **Support processing and loading files without extensions**
from #9714
- Addresses **More runtime loading configuration** from #9714
- Fixes #367
- Fixes #10703

## Solution

`AssetServer::load::<A>` and `AssetServer::load_with_settings::<A>` can
now use the `Asset` type parameter `A` to select a registered
`AssetLoader` without inspecting the provided `AssetPath`. This change
cascades onto `LoadContext::load` and `LoadContext::load_with_settings`.
This allows the loading of assets which have incorrect or ambiguous file
extensions.

```rust
// Allow the type to be inferred by context
let handle = asset_server.load("data/asset_no_extension");

// Hint the type through the handle
let handle: Handle<CustomAsset> = asset_server.load("data/asset_no_extension");

// Explicit through turbofish
let handle = asset_server.load::<CustomAsset>("data/asset_no_extension");
```

Since a single `AssetPath` no longer maps 1:1 with an `Asset`, I've also
modified how assets are loaded to permit multiple asset types to be
loaded from a single path. This allows for two different `AssetLoaders`
(which return different types of assets) to both load a single path (if
requested).

```rust
// Uses GltfLoader
let model = asset_server.load::<Gltf>("cube.gltf");

// Hypothetical Blob loader for data transmission (for example)
let blob = asset_server.load::<Blob>("cube.gltf");
```

As these changes are reflected in the `LoadContext` as well as the
`AssetServer`, custom `AssetLoaders` can also take advantage of this
behaviour to create more complex assets.

---

## Change Log

- Updated `custom_asset` example to demonstrate extension-less assets.
- Added `AssetServer::get_handles_untyped` and Added
`AssetServer::get_path_ids`

## Notes

As a part of that refactor, I chose to store `AssetLoader`s (within
`AssetLoaders`) using a `HashMap<TypeId, ...>` instead of a `Vec<...>`.
My reasoning for this was I needed to add a relationship between `Asset`
`TypeId`s and the `AssetLoader`, so instead of having a `Vec` and a
`HashMap`, I combined the two, removing the `usize` index from the
adjacent maps.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
tjamaan pushed a commit to tjamaan/bevy that referenced this issue Feb 6, 2024
# Objective

- Addresses **Support processing and loading files without extensions**
from bevyengine#9714
- Addresses **More runtime loading configuration** from bevyengine#9714
- Fixes bevyengine#367
- Fixes bevyengine#10703

## Solution

`AssetServer::load::<A>` and `AssetServer::load_with_settings::<A>` can
now use the `Asset` type parameter `A` to select a registered
`AssetLoader` without inspecting the provided `AssetPath`. This change
cascades onto `LoadContext::load` and `LoadContext::load_with_settings`.
This allows the loading of assets which have incorrect or ambiguous file
extensions.

```rust
// Allow the type to be inferred by context
let handle = asset_server.load("data/asset_no_extension");

// Hint the type through the handle
let handle: Handle<CustomAsset> = asset_server.load("data/asset_no_extension");

// Explicit through turbofish
let handle = asset_server.load::<CustomAsset>("data/asset_no_extension");
```

Since a single `AssetPath` no longer maps 1:1 with an `Asset`, I've also
modified how assets are loaded to permit multiple asset types to be
loaded from a single path. This allows for two different `AssetLoaders`
(which return different types of assets) to both load a single path (if
requested).

```rust
// Uses GltfLoader
let model = asset_server.load::<Gltf>("cube.gltf");

// Hypothetical Blob loader for data transmission (for example)
let blob = asset_server.load::<Blob>("cube.gltf");
```

As these changes are reflected in the `LoadContext` as well as the
`AssetServer`, custom `AssetLoaders` can also take advantage of this
behaviour to create more complex assets.

---

## Change Log

- Updated `custom_asset` example to demonstrate extension-less assets.
- Added `AssetServer::get_handles_untyped` and Added
`AssetServer::get_path_ids`

## Notes

As a part of that refactor, I chose to store `AssetLoader`s (within
`AssetLoaders`) using a `HashMap<TypeId, ...>` instead of a `Vec<...>`.
My reasoning for this was I needed to add a relationship between `Asset`
`TypeId`s and the `AssetLoader`, so instead of having a `Vec` and a
`HashMap`, I combined the two, removing the `usize` index from the
adjacent maps.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Assets Load files from disk to use for things like images, models, and sounds C-Enhancement A new feature
Projects
Status: Wishlist
Development

Successfully merging a pull request may close this issue.

3 participants