mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
parent
03bc5d7fdd
commit
f88cfabdde
19 changed files with 410 additions and 264 deletions
|
@ -37,3 +37,10 @@ log = { version = "0.4", features = ["release_max_level_info"] }
|
|||
notify = { version = "5.0.0-pre.2", optional = true }
|
||||
parking_lot = "0.11.0"
|
||||
rand = "0.7.3"
|
||||
async-trait = "0.1.40"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
wasm-bindgen = { version = "0.2" }
|
||||
web-sys = { version = "0.3", features = ["Request", "Window", "Response"]}
|
||||
wasm-bindgen-futures = "0.4"
|
||||
js-sys = "0.3"
|
|
@ -1,8 +1,8 @@
|
|||
use crate::{
|
||||
path::{AssetPath, AssetPathId, SourcePathId},
|
||||
Asset, AssetIo, AssetIoError, AssetLifecycle, AssetLifecycleChannel, AssetLifecycleEvent,
|
||||
AssetLoader, Assets, FileAssetIo, Handle, HandleId, HandleUntyped, LabelId, LoadContext,
|
||||
LoadState, RefChange, RefChangeChannel, SourceInfo, SourceMeta,
|
||||
AssetLoader, Assets, Handle, HandleId, HandleUntyped, LabelId, LoadContext, LoadState,
|
||||
RefChange, RefChangeChannel, SourceInfo, SourceMeta,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use bevy_ecs::Res;
|
||||
|
@ -35,8 +35,8 @@ pub(crate) struct AssetRefCounter {
|
|||
pub(crate) ref_counts: Arc<RwLock<HashMap<HandleId, usize>>>,
|
||||
}
|
||||
|
||||
pub struct AssetServerInternal<TAssetIo: AssetIo = FileAssetIo> {
|
||||
pub(crate) asset_io: TAssetIo,
|
||||
pub struct AssetServerInternal {
|
||||
pub(crate) asset_io: Box<dyn AssetIo>,
|
||||
pub(crate) asset_ref_counter: AssetRefCounter,
|
||||
pub(crate) asset_sources: Arc<RwLock<HashMap<SourcePathId, SourceInfo>>>,
|
||||
pub(crate) asset_lifecycles: Arc<RwLock<HashMap<Uuid, Box<dyn AssetLifecycle>>>>,
|
||||
|
@ -47,11 +47,11 @@ pub struct AssetServerInternal<TAssetIo: AssetIo = FileAssetIo> {
|
|||
}
|
||||
|
||||
/// Loads assets from the filesystem on background threads
|
||||
pub struct AssetServer<TAssetIo: AssetIo = FileAssetIo> {
|
||||
pub(crate) server: Arc<AssetServerInternal<TAssetIo>>,
|
||||
pub struct AssetServer {
|
||||
pub(crate) server: Arc<AssetServerInternal>,
|
||||
}
|
||||
|
||||
impl<TAssetIo: AssetIo> Clone for AssetServer<TAssetIo> {
|
||||
impl Clone for AssetServer {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
server: self.server.clone(),
|
||||
|
@ -59,8 +59,8 @@ impl<TAssetIo: AssetIo> Clone for AssetServer<TAssetIo> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
|
||||
pub fn new(source_io: TAssetIo, task_pool: TaskPool) -> Self {
|
||||
impl AssetServer {
|
||||
pub fn new<T: AssetIo>(source_io: T, task_pool: TaskPool) -> Self {
|
||||
AssetServer {
|
||||
server: Arc::new(AssetServerInternal {
|
||||
loaders: Default::default(),
|
||||
|
@ -70,7 +70,7 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
|
|||
handle_to_path: Default::default(),
|
||||
asset_lifecycles: Default::default(),
|
||||
task_pool,
|
||||
asset_io: source_io,
|
||||
asset_io: Box::new(source_io),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
|
|||
}
|
||||
|
||||
// TODO: properly set failed LoadState in all failure cases
|
||||
fn load_sync<'a, P: Into<AssetPath<'a>>>(
|
||||
async fn load_async<'a, P: Into<AssetPath<'a>>>(
|
||||
&self,
|
||||
path: P,
|
||||
force: bool,
|
||||
|
@ -221,17 +221,18 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
|
|||
};
|
||||
|
||||
// load the asset bytes
|
||||
let bytes = self.server.asset_io.load_path(asset_path.path())?;
|
||||
let bytes = self.server.asset_io.load_path(asset_path.path()).await?;
|
||||
|
||||
// load the asset source using the corresponding AssetLoader
|
||||
let mut load_context = LoadContext::new(
|
||||
asset_path.path(),
|
||||
&self.server.asset_ref_counter.channel,
|
||||
&self.server.asset_io,
|
||||
&*self.server.asset_io,
|
||||
version,
|
||||
);
|
||||
asset_loader
|
||||
.load(&bytes, &mut load_context)
|
||||
.await
|
||||
.map_err(AssetServerError::AssetLoaderError)?;
|
||||
|
||||
// if version has changed since we loaded and grabbed a lock, return. theres is a newer version being loaded
|
||||
|
@ -291,7 +292,7 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
|
|||
self.server
|
||||
.task_pool
|
||||
.spawn(async move {
|
||||
server.load_sync(owned_path, force).unwrap();
|
||||
server.load_async(owned_path, force).await.unwrap();
|
||||
})
|
||||
.detach();
|
||||
asset_path.into()
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::{filesystem_watcher::FilesystemWatcher, AssetIo, AssetIoError, AssetServer};
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use bevy_ecs::Res;
|
||||
use bevy_utils::HashSet;
|
||||
use crossbeam_channel::TryRecvError;
|
||||
|
@ -10,33 +12,6 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{filesystem_watcher::FilesystemWatcher, AssetServer};
|
||||
|
||||
/// Errors that occur while loading assets
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AssetIoError {
|
||||
#[error("Path not found")]
|
||||
NotFound(PathBuf),
|
||||
#[error("Encountered an io error while loading asset.")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("Failed to watch path")]
|
||||
PathWatchError(PathBuf),
|
||||
}
|
||||
|
||||
/// Handles load requests from an AssetServer
|
||||
pub trait AssetIo: Send + Sync + 'static {
|
||||
fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError>;
|
||||
fn save_path(&self, path: &Path, bytes: &[u8]) -> Result<(), AssetIoError>;
|
||||
fn read_directory(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> Result<Box<dyn Iterator<Item = PathBuf>>, AssetIoError>;
|
||||
fn is_directory(&self, path: &Path) -> bool;
|
||||
fn watch_path_for_changes(&self, path: &Path) -> Result<(), AssetIoError>;
|
||||
fn watch_for_changes(&self) -> Result<(), AssetIoError>;
|
||||
}
|
||||
|
||||
pub struct FileAssetIo {
|
||||
root_path: PathBuf,
|
||||
|
@ -67,8 +42,9 @@ impl FileAssetIo {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AssetIo for FileAssetIo {
|
||||
fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError> {
|
||||
async fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError> {
|
||||
let mut bytes = Vec::new();
|
||||
match File::open(self.root_path.join(path)) {
|
||||
Ok(mut file) => {
|
||||
|
@ -98,15 +74,6 @@ impl AssetIo for FileAssetIo {
|
|||
)))
|
||||
}
|
||||
|
||||
fn save_path(&self, path: &Path, bytes: &[u8]) -> Result<(), AssetIoError> {
|
||||
let path = self.root_path.join(path);
|
||||
if let Some(parent_path) = path.parent() {
|
||||
fs::create_dir_all(parent_path)?;
|
||||
}
|
||||
|
||||
Ok(fs::write(self.root_path.join(path), bytes)?)
|
||||
}
|
||||
|
||||
fn watch_path_for_changes(&self, path: &Path) -> Result<(), AssetIoError> {
|
||||
#[cfg(feature = "filesystem_watcher")]
|
||||
{
|
||||
|
@ -139,7 +106,13 @@ impl AssetIo for FileAssetIo {
|
|||
#[cfg(feature = "filesystem_watcher")]
|
||||
pub fn filesystem_watcher_system(asset_server: Res<AssetServer>) {
|
||||
let mut changed = HashSet::default();
|
||||
let watcher = asset_server.server.asset_io.filesystem_watcher.read();
|
||||
let asset_io =
|
||||
if let Some(asset_io) = asset_server.server.asset_io.downcast_ref::<FileAssetIo>() {
|
||||
asset_io
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let watcher = asset_io.filesystem_watcher.read();
|
||||
if let Some(ref watcher) = *watcher {
|
||||
loop {
|
||||
let event = match watcher.receiver.try_recv() {
|
||||
|
@ -155,9 +128,7 @@ pub fn filesystem_watcher_system(asset_server: Res<AssetServer>) {
|
|||
{
|
||||
for path in paths.iter() {
|
||||
if !changed.contains(path) {
|
||||
let relative_path = path
|
||||
.strip_prefix(&asset_server.server.asset_io.root_path)
|
||||
.unwrap();
|
||||
let relative_path = path.strip_prefix(&asset_io.root_path).unwrap();
|
||||
let _ = asset_server.load_untracked(relative_path, true);
|
||||
}
|
||||
}
|
45
crates/bevy_asset/src/io/mod.rs
Normal file
45
crates/bevy_asset/src/io/mod.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod file_asset_io;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod wasm_asset_io;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use file_asset_io::*;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use wasm_asset_io::*;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
use std::{
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that occur while loading assets
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AssetIoError {
|
||||
#[error("Path not found")]
|
||||
NotFound(PathBuf),
|
||||
#[error("Encountered an io error while loading asset.")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("Failed to watch path")]
|
||||
PathWatchError(PathBuf),
|
||||
}
|
||||
|
||||
/// Handles load requests from an AssetServer
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait AssetIo: Downcast + Send + Sync + 'static {
|
||||
async fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError>;
|
||||
fn read_directory(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> Result<Box<dyn Iterator<Item = PathBuf>>, AssetIoError>;
|
||||
fn is_directory(&self, path: &Path) -> bool;
|
||||
fn watch_path_for_changes(&self, path: &Path) -> Result<(), AssetIoError>;
|
||||
fn watch_for_changes(&self) -> Result<(), AssetIoError>;
|
||||
}
|
||||
|
||||
impl_downcast!(AssetIo);
|
54
crates/bevy_asset/src/io/wasm_asset_io.rs
Normal file
54
crates/bevy_asset/src/io/wasm_asset_io.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
use crate::{AssetIo, AssetIoError};
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use js_sys::Uint8Array;
|
||||
use std::path::{Path, PathBuf};
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::Response;
|
||||
|
||||
pub struct WasmAssetIo {
|
||||
root_path: PathBuf,
|
||||
}
|
||||
|
||||
impl WasmAssetIo {
|
||||
pub fn new<P: AsRef<Path>>(path: P) -> Self {
|
||||
WasmAssetIo {
|
||||
root_path: path.as_ref().to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl AssetIo for WasmAssetIo {
|
||||
async fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError> {
|
||||
let path = self.root_path.join(path);
|
||||
let window = web_sys::window().unwrap();
|
||||
let resp_value = JsFuture::from(window.fetch_with_str(path.to_str().unwrap()))
|
||||
.await
|
||||
.unwrap();
|
||||
let resp: Response = resp_value.dyn_into().unwrap();
|
||||
let data = JsFuture::from(resp.array_buffer().unwrap()).await.unwrap();
|
||||
let bytes = Uint8Array::new(&data).to_vec();
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
fn read_directory(
|
||||
&self,
|
||||
_path: &Path,
|
||||
) -> Result<Box<dyn Iterator<Item = PathBuf>>, AssetIoError> {
|
||||
Ok(Box::new(std::iter::empty::<PathBuf>()))
|
||||
}
|
||||
|
||||
fn watch_path_for_changes(&self, _path: &Path) -> Result<(), AssetIoError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn watch_for_changes(&self) -> Result<(), AssetIoError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_directory(&self, path: &Path) -> bool {
|
||||
self.root_path.join(path).is_dir()
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
mod asset_server;
|
||||
mod assets;
|
||||
#[cfg(feature = "filesystem_watcher")]
|
||||
#[cfg(all(feature = "filesystem_watcher", not(target_arch = "wasm32")))]
|
||||
mod filesystem_watcher;
|
||||
mod handle;
|
||||
mod info;
|
||||
|
@ -37,7 +37,7 @@ use bevy_type_registry::RegisterType;
|
|||
pub struct AssetPlugin;
|
||||
|
||||
pub struct AssetServerSettings {
|
||||
asset_folder: String,
|
||||
pub asset_folder: String,
|
||||
}
|
||||
|
||||
impl Default for AssetServerSettings {
|
||||
|
@ -61,7 +61,11 @@ impl Plugin for AssetPlugin {
|
|||
let settings = app
|
||||
.resources_mut()
|
||||
.get_or_insert_with(AssetServerSettings::default);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let source = FileAssetIo::new(&settings.asset_folder);
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let source = WasmAssetIo::new(&settings.asset_folder);
|
||||
AssetServer::new(source, task_pool)
|
||||
};
|
||||
|
||||
|
@ -74,7 +78,7 @@ impl Plugin for AssetPlugin {
|
|||
asset_server::free_unused_assets_system.system(),
|
||||
);
|
||||
|
||||
#[cfg(feature = "filesystem_watcher")]
|
||||
#[cfg(all(feature = "filesystem_watcher", not(target_arch = "wasm32")))]
|
||||
app.add_system_to_stage(stage::LOAD_ASSETS, io::filesystem_watcher_system.system());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,18 @@ use crate::{
|
|||
use anyhow::Result;
|
||||
use bevy_ecs::{Res, ResMut, Resource};
|
||||
use bevy_type_registry::{TypeUuid, TypeUuidDynamic};
|
||||
use bevy_utils::HashMap;
|
||||
use bevy_utils::{BoxedFuture, HashMap};
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
use std::path::Path;
|
||||
|
||||
/// A loader for an asset source
|
||||
pub trait AssetLoader: Send + Sync + 'static {
|
||||
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<(), anyhow::Error>;
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
bytes: &'a [u8],
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<(), anyhow::Error>>;
|
||||
fn extensions(&self) -> &[&str];
|
||||
}
|
||||
|
||||
|
@ -94,8 +98,8 @@ impl<'a> LoadContext<'a> {
|
|||
Handle::strong(id.into(), self.ref_change_channel.sender.clone())
|
||||
}
|
||||
|
||||
pub fn read_asset_bytes<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>, AssetIoError> {
|
||||
self.asset_io.load_path(path.as_ref())
|
||||
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
|
||||
}
|
||||
|
||||
pub fn get_asset_metas(&self) -> Vec<AssetMeta> {
|
||||
|
|
|
@ -18,6 +18,7 @@ bevy_app = { path = "../bevy_app", version = "0.2.1" }
|
|||
bevy_asset = { path = "../bevy_asset", version = "0.2.1" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.2.1" }
|
||||
bevy_type_registry = { path = "../bevy_type_registry", version = "0.2.1" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.2.1" }
|
||||
|
||||
# other
|
||||
anyhow = "1.0"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use anyhow::Result;
|
||||
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
||||
use bevy_type_registry::TypeUuid;
|
||||
use bevy_utils::BoxedFuture;
|
||||
use std::{io::Cursor, sync::Arc};
|
||||
|
||||
/// A source of audio data
|
||||
|
@ -21,11 +22,11 @@ impl AsRef<[u8]> for AudioSource {
|
|||
pub struct Mp3Loader;
|
||||
|
||||
impl AssetLoader for Mp3Loader {
|
||||
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<()> {
|
||||
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> BoxedFuture<Result<()>> {
|
||||
load_context.set_default_asset(LoadedAsset::new(AudioSource {
|
||||
bytes: bytes.into(),
|
||||
}));
|
||||
Ok(())
|
||||
Box::pin(async move { Ok(()) })
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &[&str] {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use anyhow::Result;
|
||||
use bevy_asset::{AssetIoError, AssetLoader, AssetPath, LoadContext, LoadedAsset};
|
||||
use bevy_ecs::{World, WorldBuilderSource};
|
||||
use bevy_ecs::{bevy_utils::BoxedFuture, World, WorldBuilderSource};
|
||||
use bevy_math::Mat4;
|
||||
use bevy_pbr::prelude::{PbrComponents, StandardMaterial};
|
||||
use bevy_render::{
|
||||
|
@ -47,8 +47,12 @@ pub enum GltfError {
|
|||
pub struct GltfLoader;
|
||||
|
||||
impl AssetLoader for GltfLoader {
|
||||
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<()> {
|
||||
Ok(load_gltf(bytes, load_context)?)
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
bytes: &'a [u8],
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<()>> {
|
||||
Box::pin(async move { Ok(load_gltf(bytes, load_context).await?) })
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &[&str] {
|
||||
|
@ -57,10 +61,13 @@ impl AssetLoader for GltfLoader {
|
|||
}
|
||||
}
|
||||
|
||||
fn load_gltf(bytes: &[u8], load_context: &mut LoadContext) -> Result<(), GltfError> {
|
||||
async fn load_gltf<'a, 'b>(
|
||||
bytes: &'a [u8],
|
||||
load_context: &'a mut LoadContext<'b>,
|
||||
) -> Result<(), GltfError> {
|
||||
let gltf = gltf::Gltf::from_slice(bytes)?;
|
||||
let mut world = World::default();
|
||||
let buffer_data = load_buffers(&gltf, load_context, load_context.path())?;
|
||||
let buffer_data = load_buffers(&gltf, load_context, load_context.path()).await?;
|
||||
|
||||
let world_builder = &mut world.build();
|
||||
|
||||
|
@ -267,9 +274,9 @@ fn get_primitive_topology(mode: Mode) -> Result<PrimitiveTopology, GltfError> {
|
|||
}
|
||||
}
|
||||
|
||||
fn load_buffers(
|
||||
async fn load_buffers(
|
||||
gltf: &gltf::Gltf,
|
||||
load_context: &LoadContext,
|
||||
load_context: &LoadContext<'_>,
|
||||
asset_path: &Path,
|
||||
) -> Result<Vec<Vec<u8>>, GltfError> {
|
||||
const OCTET_STREAM_URI: &str = "data:application/octet-stream;base64,";
|
||||
|
@ -287,7 +294,7 @@ fn load_buffers(
|
|||
} else {
|
||||
// TODO: Remove this and add dep
|
||||
let buffer_path = asset_path.parent().unwrap().join(uri);
|
||||
let buffer_bytes = load_context.read_asset_bytes(buffer_path)?;
|
||||
let buffer_bytes = load_context.read_asset_bytes(buffer_path).await?;
|
||||
buffer_data.push(buffer_bytes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,42 +2,49 @@ use super::{Texture, TextureFormat};
|
|||
use anyhow::Result;
|
||||
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
||||
use bevy_math::Vec2;
|
||||
use bevy_utils::BoxedFuture;
|
||||
|
||||
/// Loads HDR textures as Texture assets
|
||||
#[derive(Clone, Default)]
|
||||
pub struct HdrTextureLoader;
|
||||
|
||||
impl AssetLoader for HdrTextureLoader {
|
||||
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<()> {
|
||||
let format = TextureFormat::Rgba32Float;
|
||||
debug_assert_eq!(
|
||||
format.pixel_size(),
|
||||
4 * 4,
|
||||
"Format should have 32bit x 4 size"
|
||||
);
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
bytes: &'a [u8],
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<()>> {
|
||||
Box::pin(async move {
|
||||
let format = TextureFormat::Rgba32Float;
|
||||
debug_assert_eq!(
|
||||
format.pixel_size(),
|
||||
4 * 4,
|
||||
"Format should have 32bit x 4 size"
|
||||
);
|
||||
|
||||
let decoder = image::hdr::HdrDecoder::new(bytes)?;
|
||||
let info = decoder.metadata();
|
||||
let rgb_data = decoder.read_image_hdr()?;
|
||||
let mut rgba_data = Vec::with_capacity(rgb_data.len() * format.pixel_size());
|
||||
let decoder = image::hdr::HdrDecoder::new(bytes)?;
|
||||
let info = decoder.metadata();
|
||||
let rgb_data = decoder.read_image_hdr()?;
|
||||
let mut rgba_data = Vec::with_capacity(rgb_data.len() * format.pixel_size());
|
||||
|
||||
for rgb in rgb_data {
|
||||
let alpha = 1.0f32;
|
||||
for rgb in rgb_data {
|
||||
let alpha = 1.0f32;
|
||||
|
||||
rgba_data.extend_from_slice(&rgb.0[0].to_ne_bytes());
|
||||
rgba_data.extend_from_slice(&rgb.0[1].to_ne_bytes());
|
||||
rgba_data.extend_from_slice(&rgb.0[2].to_ne_bytes());
|
||||
rgba_data.extend_from_slice(&alpha.to_ne_bytes());
|
||||
}
|
||||
rgba_data.extend_from_slice(&rgb.0[0].to_ne_bytes());
|
||||
rgba_data.extend_from_slice(&rgb.0[1].to_ne_bytes());
|
||||
rgba_data.extend_from_slice(&rgb.0[2].to_ne_bytes());
|
||||
rgba_data.extend_from_slice(&alpha.to_ne_bytes());
|
||||
}
|
||||
|
||||
let texture = Texture::new(
|
||||
Vec2::new(info.width as f32, info.height as f32),
|
||||
rgba_data,
|
||||
format,
|
||||
);
|
||||
let texture = Texture::new(
|
||||
Vec2::new(info.width as f32, info.height as f32),
|
||||
rgba_data,
|
||||
format,
|
||||
);
|
||||
|
||||
load_context.set_default_asset(LoadedAsset::new(texture));
|
||||
Ok(())
|
||||
load_context.set_default_asset(LoadedAsset::new(texture));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &[&str] {
|
||||
|
|
|
@ -2,6 +2,7 @@ use super::{Texture, TextureFormat};
|
|||
use anyhow::Result;
|
||||
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
||||
use bevy_math::Vec2;
|
||||
use bevy_utils::BoxedFuture;
|
||||
|
||||
/// Loader for images that can be read by the `image` crate.
|
||||
///
|
||||
|
@ -10,141 +11,147 @@ use bevy_math::Vec2;
|
|||
pub struct ImageTextureLoader;
|
||||
|
||||
impl AssetLoader for ImageTextureLoader {
|
||||
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<()> {
|
||||
use bevy_core::AsBytes;
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
bytes: &'a [u8],
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<()>> {
|
||||
Box::pin(async move {
|
||||
use bevy_core::AsBytes;
|
||||
|
||||
// Find the image type we expect. A file with the extension "png" should
|
||||
// probably load as a PNG.
|
||||
// Find the image type we expect. A file with the extension "png" should
|
||||
// probably load as a PNG.
|
||||
|
||||
let ext = load_context.path().extension().unwrap().to_str().unwrap();
|
||||
let ext = load_context.path().extension().unwrap().to_str().unwrap();
|
||||
|
||||
// NOTE: If more formats are added they can be added here.
|
||||
let img_format = if ext.eq_ignore_ascii_case("png") {
|
||||
image::ImageFormat::Png
|
||||
} else {
|
||||
panic!(
|
||||
"Unexpected image format {:?} for file {}, this is an error in `bevy_render`.",
|
||||
ext,
|
||||
load_context.path().display()
|
||||
)
|
||||
};
|
||||
// NOTE: If more formats are added they can be added here.
|
||||
let img_format = if ext.eq_ignore_ascii_case("png") {
|
||||
image::ImageFormat::Png
|
||||
} else {
|
||||
panic!(
|
||||
"Unexpected image format {:?} for file {}, this is an error in `bevy_render`.",
|
||||
ext,
|
||||
load_context.path().display()
|
||||
)
|
||||
};
|
||||
|
||||
// Load the image in the expected format.
|
||||
// Some formats like PNG allow for R or RG textures too, so the texture
|
||||
// format needs to be determined. For RGB textures an alpha channel
|
||||
// needs to be added, so the image data needs to be converted in those
|
||||
// cases.
|
||||
// Load the image in the expected format.
|
||||
// Some formats like PNG allow for R or RG textures too, so the texture
|
||||
// format needs to be determined. For RGB textures an alpha channel
|
||||
// needs to be added, so the image data needs to be converted in those
|
||||
// cases.
|
||||
|
||||
let dyn_img = image::load_from_memory_with_format(bytes, img_format)?;
|
||||
let dyn_img = image::load_from_memory_with_format(bytes, img_format)?;
|
||||
|
||||
let width;
|
||||
let height;
|
||||
let width;
|
||||
let height;
|
||||
|
||||
let data: Vec<u8>;
|
||||
let format: TextureFormat;
|
||||
let data: Vec<u8>;
|
||||
let format: TextureFormat;
|
||||
|
||||
match dyn_img {
|
||||
image::DynamicImage::ImageLuma8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::R8Unorm;
|
||||
match dyn_img {
|
||||
image::DynamicImage::ImageLuma8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::R8Unorm;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageLumaA8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rg8Unorm;
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageLumaA8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rg8Unorm;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageRgb8(i) => {
|
||||
let i = image::DynamicImage::ImageRgb8(i).into_rgba();
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rgba8UnormSrgb;
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageRgb8(i) => {
|
||||
let i = image::DynamicImage::ImageRgb8(i).into_rgba();
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rgba8UnormSrgb;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageRgba8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rgba8UnormSrgb;
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageRgba8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rgba8UnormSrgb;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageBgr8(i) => {
|
||||
let i = image::DynamicImage::ImageBgr8(i).into_bgra();
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageBgr8(i) => {
|
||||
let i = image::DynamicImage::ImageBgr8(i).into_bgra();
|
||||
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Bgra8UnormSrgb;
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Bgra8UnormSrgb;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageBgra8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Bgra8UnormSrgb;
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageBgra8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Bgra8UnormSrgb;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageLuma16(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::R16Uint;
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageLuma16(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::R16Uint;
|
||||
|
||||
let raw_data = i.into_raw();
|
||||
let raw_data = i.into_raw();
|
||||
|
||||
data = raw_data.as_slice().as_bytes().to_owned();
|
||||
}
|
||||
image::DynamicImage::ImageLumaA16(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rg16Uint;
|
||||
data = raw_data.as_slice().as_bytes().to_owned();
|
||||
}
|
||||
image::DynamicImage::ImageLumaA16(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rg16Uint;
|
||||
|
||||
let raw_data = i.into_raw();
|
||||
let raw_data = i.into_raw();
|
||||
|
||||
data = raw_data.as_slice().as_bytes().to_owned();
|
||||
}
|
||||
|
||||
image::DynamicImage::ImageRgb16(image) => {
|
||||
width = image.width();
|
||||
height = image.height();
|
||||
format = TextureFormat::Rgba16Uint;
|
||||
|
||||
let mut local_data =
|
||||
Vec::with_capacity(width as usize * height as usize * format.pixel_size());
|
||||
|
||||
for pixel in image.into_raw().chunks_exact(3) {
|
||||
// TODO unsafe_get in release builds?
|
||||
let r = pixel[0];
|
||||
let g = pixel[1];
|
||||
let b = pixel[2];
|
||||
let a = u16::max_value();
|
||||
|
||||
local_data.extend_from_slice(&r.to_ne_bytes());
|
||||
local_data.extend_from_slice(&g.to_ne_bytes());
|
||||
local_data.extend_from_slice(&b.to_ne_bytes());
|
||||
local_data.extend_from_slice(&a.to_ne_bytes());
|
||||
data = raw_data.as_slice().as_bytes().to_owned();
|
||||
}
|
||||
|
||||
data = local_data;
|
||||
image::DynamicImage::ImageRgb16(image) => {
|
||||
width = image.width();
|
||||
height = image.height();
|
||||
format = TextureFormat::Rgba16Uint;
|
||||
|
||||
let mut local_data =
|
||||
Vec::with_capacity(width as usize * height as usize * format.pixel_size());
|
||||
|
||||
for pixel in image.into_raw().chunks_exact(3) {
|
||||
// TODO unsafe_get in release builds?
|
||||
let r = pixel[0];
|
||||
let g = pixel[1];
|
||||
let b = pixel[2];
|
||||
let a = u16::max_value();
|
||||
|
||||
local_data.extend_from_slice(&r.to_ne_bytes());
|
||||
local_data.extend_from_slice(&g.to_ne_bytes());
|
||||
local_data.extend_from_slice(&b.to_ne_bytes());
|
||||
local_data.extend_from_slice(&a.to_ne_bytes());
|
||||
}
|
||||
|
||||
data = local_data;
|
||||
}
|
||||
image::DynamicImage::ImageRgba16(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rgba16Uint;
|
||||
|
||||
let raw_data = i.into_raw();
|
||||
|
||||
data = raw_data.as_slice().as_bytes().to_owned();
|
||||
}
|
||||
}
|
||||
image::DynamicImage::ImageRgba16(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rgba16Uint;
|
||||
|
||||
let raw_data = i.into_raw();
|
||||
|
||||
data = raw_data.as_slice().as_bytes().to_owned();
|
||||
}
|
||||
}
|
||||
|
||||
let texture = Texture::new(Vec2::new(width as f32, height as f32), data, format);
|
||||
load_context.set_default_asset(LoadedAsset::new(texture));
|
||||
Ok(())
|
||||
let texture = Texture::new(Vec2::new(width as f32, height as f32), data, format);
|
||||
load_context.set_default_asset(LoadedAsset::new(texture));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &[&str] {
|
||||
|
|
|
@ -4,6 +4,7 @@ use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
|||
use bevy_ecs::{FromResources, Resources};
|
||||
use bevy_property::PropertyTypeRegistry;
|
||||
use bevy_type_registry::TypeRegistry;
|
||||
use bevy_utils::BoxedFuture;
|
||||
use parking_lot::RwLock;
|
||||
use serde::de::DeserializeSeed;
|
||||
use std::sync::Arc;
|
||||
|
@ -23,15 +24,21 @@ impl FromResources for SceneLoader {
|
|||
}
|
||||
|
||||
impl AssetLoader for SceneLoader {
|
||||
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<()> {
|
||||
let registry = self.property_type_registry.read();
|
||||
let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?;
|
||||
let scene_deserializer = SceneDeserializer {
|
||||
property_type_registry: ®istry,
|
||||
};
|
||||
let scene = scene_deserializer.deserialize(&mut deserializer)?;
|
||||
load_context.set_default_asset(LoadedAsset::new(scene));
|
||||
Ok(())
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
bytes: &'a [u8],
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<()>> {
|
||||
Box::pin(async move {
|
||||
let registry = self.property_type_registry.read();
|
||||
let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?;
|
||||
let scene_deserializer = SceneDeserializer {
|
||||
property_type_registry: ®istry,
|
||||
};
|
||||
let scene = scene_deserializer.deserialize(&mut deserializer)?;
|
||||
load_context.set_default_asset(LoadedAsset::new(scene));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &[&str] {
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
use crate::Font;
|
||||
use anyhow::Result;
|
||||
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
||||
use bevy_utils::BoxedFuture;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FontLoader;
|
||||
|
||||
impl AssetLoader for FontLoader {
|
||||
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<()> {
|
||||
let font = Font::try_from_bytes(bytes.into())?;
|
||||
load_context.set_default_asset(LoadedAsset::new(font));
|
||||
Ok(())
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
bytes: &'a [u8],
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<()>> {
|
||||
Box::pin(async move {
|
||||
let font = Font::try_from_bytes(bytes.into())?;
|
||||
load_context.set_default_asset(LoadedAsset::new(font));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &[&str] {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
pub use ahash::AHasher;
|
||||
use ahash::RandomState;
|
||||
use std::{future::Future, pin::Pin};
|
||||
|
||||
pub use ahash::AHasher;
|
||||
|
||||
pub type BoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
|
||||
pub type HashMap<K, V> = std::collections::HashMap<K, V, RandomState>;
|
||||
pub type HashSet<K> = std::collections::HashSet<K, RandomState>;
|
||||
|
||||
|
|
|
@ -71,9 +71,12 @@ impl WinitWindows {
|
|||
|
||||
let winit_window = winit_window_builder.build(&event_loop).unwrap();
|
||||
|
||||
winit_window
|
||||
.set_cursor_grab(window.cursor_locked())
|
||||
.unwrap();
|
||||
match winit_window.set_cursor_grab(window.cursor_locked()) {
|
||||
Ok(_) => {}
|
||||
Err(winit::error::ExternalError::NotSupported(_)) => {}
|
||||
Err(err) => Err(err).unwrap(),
|
||||
}
|
||||
|
||||
winit_window.set_cursor_visible(window.cursor_visible());
|
||||
|
||||
self.window_id_to_winit
|
||||
|
|
|
@ -2,6 +2,7 @@ use bevy::{
|
|||
asset::{AssetLoader, LoadContext, LoadedAsset},
|
||||
prelude::*,
|
||||
type_registry::TypeUuid,
|
||||
utils::BoxedFuture,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
|
@ -15,10 +16,16 @@ pub struct CustomAsset {
|
|||
pub struct CustomAssetLoader;
|
||||
|
||||
impl AssetLoader for CustomAssetLoader {
|
||||
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<(), anyhow::Error> {
|
||||
let custom_asset = ron::de::from_bytes::<CustomAsset>(bytes)?;
|
||||
load_context.set_default_asset(LoadedAsset::new(custom_asset));
|
||||
Ok(())
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
bytes: &'a [u8],
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<(), anyhow::Error>> {
|
||||
Box::pin(async move {
|
||||
let custom_asset = ron::de::from_bytes::<CustomAsset>(bytes)?;
|
||||
load_context.set_default_asset(LoadedAsset::new(custom_asset));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &[&str] {
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
#[cfg(target_arch = "wasm32")]
|
||||
extern crate console_error_panic_hook;
|
||||
|
||||
use bevy::{asset::AssetLoader, prelude::*, type_registry::TypeUuid};
|
||||
use bevy_asset::{LoadContext, LoadedAsset};
|
||||
use bevy::{
|
||||
asset::{AssetLoader, AssetServerSettings, LoadContext, LoadedAsset},
|
||||
prelude::*,
|
||||
type_registry::TypeUuid,
|
||||
utils::BoxedFuture,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
@ -12,17 +16,38 @@ fn main() {
|
|||
}
|
||||
|
||||
App::build()
|
||||
.add_resource(AssetServerSettings {
|
||||
asset_folder: "/".to_string(),
|
||||
})
|
||||
.add_default_plugins()
|
||||
.add_asset::<RustSourceCode>()
|
||||
.init_asset_loader::<RustSourceCodeLoader>()
|
||||
.add_startup_system(asset_system.system())
|
||||
.add_system(asset_events.system())
|
||||
.add_startup_system(load_asset.system())
|
||||
.add_system(print_asset.system())
|
||||
.run();
|
||||
}
|
||||
|
||||
fn asset_system(asset_server: Res<AssetServer>) {
|
||||
asset_server.load::<RustSourceCode, _>("assets_wasm.rs");
|
||||
log::info!("hello wasm");
|
||||
struct State {
|
||||
handle: Handle<RustSourceCode>,
|
||||
printed: bool,
|
||||
}
|
||||
|
||||
fn load_asset(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.insert_resource(State {
|
||||
handle: asset_server.load("assets_wasm.rs"),
|
||||
printed: false,
|
||||
});
|
||||
}
|
||||
|
||||
fn print_asset(mut state: ResMut<State>, rust_sources: Res<Assets<RustSourceCode>>) {
|
||||
if state.printed {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(code) = rust_sources.get(&state.handle) {
|
||||
log::info!("code: {}", code.0);
|
||||
state.printed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, TypeUuid)]
|
||||
|
@ -33,11 +58,17 @@ pub struct RustSourceCode(pub String);
|
|||
pub struct RustSourceCodeLoader;
|
||||
|
||||
impl AssetLoader for RustSourceCodeLoader {
|
||||
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<(), anyhow::Error> {
|
||||
load_context.set_default_asset(LoadedAsset::new(RustSourceCode(String::from_utf8(
|
||||
bytes.into(),
|
||||
)?)));
|
||||
Ok(())
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
bytes: &'a [u8],
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<(), anyhow::Error>> {
|
||||
Box::pin(async move {
|
||||
load_context.set_default_asset(LoadedAsset::new(RustSourceCode(String::from_utf8(
|
||||
bytes.into(),
|
||||
)?)));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &[&str] {
|
||||
|
@ -45,25 +76,3 @@ impl AssetLoader for RustSourceCodeLoader {
|
|||
EXT
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AssetEventsState {
|
||||
reader: EventReader<AssetEvent<RustSourceCode>>,
|
||||
}
|
||||
|
||||
pub fn asset_events(
|
||||
mut state: Local<AssetEventsState>,
|
||||
rust_sources: Res<Assets<RustSourceCode>>,
|
||||
events: Res<Events<AssetEvent<RustSourceCode>>>,
|
||||
) {
|
||||
for event in state.reader.iter(&events) {
|
||||
match event {
|
||||
AssetEvent::Created { handle } => {
|
||||
if let Some(code) = rust_sources.get(handle) {
|
||||
log::info!("code: {}", code.0);
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ pub use bevy_scene as scene;
|
|||
pub use bevy_tasks as tasks;
|
||||
pub use bevy_transform as transform;
|
||||
pub use bevy_type_registry as type_registry;
|
||||
pub use bevy_utils as utils;
|
||||
pub use bevy_window as window;
|
||||
|
||||
#[cfg(feature = "bevy_audio")]
|
||||
|
|
Loading…
Reference in a new issue