use std::{
path::{MAIN_SEPARATOR, PathBuf},
str::FromStr as _,
};
use phf::Map;
use super::{Id, loader::Loader};
use crate::graphics::atlas::TextureRef;
static RUNTIME_EMBEDDED_ASSETS: &[EmbeddedRawAsset] = &[];
static RUNTIME_EMBEDDED_ATLAS: &EmbeddedRawStaticAtlas = &EmbeddedRawStaticAtlas {
diced_atlas_png_bytes: &[],
width: 0,
height: 0,
textures: &phf::Map::new(),
};
pub struct AssetSource {
runtime_asset_dir: Option<PathBuf>,
embedded_assets: &'static [EmbeddedRawAsset],
embedded_atlas: &'static EmbeddedRawStaticAtlas,
#[cfg(not(target_arch = "wasm32"))]
hot_reload_folder_watcher:
Option<notify_debouncer_mini::Debouncer<notify_debouncer_mini::notify::RecommendedWatcher>>,
}
impl AssetSource {
#[inline(always)]
#[must_use]
pub fn new() -> Self {
let runtime_asset_dir = None;
let embedded_assets = RUNTIME_EMBEDDED_ASSETS;
let embedded_atlas = RUNTIME_EMBEDDED_ATLAS;
Self {
runtime_asset_dir,
embedded_assets,
embedded_atlas,
#[cfg(not(target_arch = "wasm32"))]
hot_reload_folder_watcher: None,
}
}
#[inline(always)]
#[must_use]
pub fn with_runtime_dir(mut self, runtime_asset_dir: &str) -> Self {
self.runtime_asset_dir = Some(PathBuf::from_str(runtime_asset_dir).unwrap());
#[cfg(not(target_arch = "wasm32"))]
{
self.hot_reload_folder_watcher =
Some(super::hot_reload::watch_assets_folder(runtime_asset_dir));
}
self
}
#[inline(always)]
#[must_use]
pub const fn with_embedded_assets(
mut self,
embedded_assets: &'static [EmbeddedRawAsset],
) -> Self {
self.embedded_assets = embedded_assets;
self
}
#[inline(always)]
#[must_use]
pub const fn with_embedded_atlas(
mut self,
embedded_astlas: &'static EmbeddedRawStaticAtlas,
) -> Self {
self.embedded_atlas = embedded_astlas;
self
}
#[inline]
#[must_use]
pub fn load_if_exists<L, T>(&self, id: &Id) -> Option<T>
where
L: Loader<T>,
{
if let Some(bytes) = self.embedded_assets.iter().find_map(|raw_asset| {
(raw_asset.id == id && raw_asset.extension == L::EXTENSION).then_some(raw_asset.bytes)
}) {
return Some(L::load(bytes, id));
}
if let Some(runtime_asset_dir) = &self.runtime_asset_dir {
let file_path = runtime_asset_dir.join(format!(
"{}.{}",
id.replace('.', std::str::from_utf8(&[MAIN_SEPARATOR as u8]).unwrap()),
L::EXTENSION
));
let bytes = std::fs::read(file_path).ok()?;
Some(L::load(&bytes, id))
} else {
None
}
}
#[must_use]
#[inline]
pub(crate) fn embedded_texture(&self, id: &Id) -> Option<&EmbeddedTexture> {
self.embedded_atlas.textures.get(id)
}
#[must_use]
#[inline]
pub(crate) const fn embedded_atlas(&self) -> &EmbeddedRawStaticAtlas {
self.embedded_atlas
}
}
impl Default for AssetSource {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[allow(clippy::exhaustive_structs)]
pub struct EmbeddedRawAsset {
pub id: &'static str,
pub extension: &'static str,
pub bytes: &'static [u8],
}
#[allow(clippy::exhaustive_structs)]
#[derive(Debug)]
pub struct EmbeddedTexture {
pub width: u16,
pub height: u16,
pub reference: TextureRef,
pub diced: &'static [EmbeddedTextureDiceMapping],
}
#[allow(clippy::exhaustive_structs)]
#[derive(Debug)]
pub struct EmbeddedTextureDiceMapping {
pub diced_u: u16,
pub diced_v: u16,
pub texture_u: u16,
pub texture_v: u16,
pub width: u16,
pub height: u16,
}
#[allow(clippy::exhaustive_structs)]
pub struct EmbeddedRawStaticAtlas {
pub diced_atlas_png_bytes: &'static [u8],
pub width: u16,
pub height: u16,
pub textures: &'static Map<&'static str, EmbeddedTexture>,
}