1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
use std::sync::Arc;
use amethyst_core::specs::storage::UnprotectedStorage;
use crate::{ErrorKind, Handle, Reload, Result, ResultExt, SingleFile, Source};
/// One of the three core traits of this crate.
///
/// You want to implement this for every type of asset like
///
/// * `Mesh`
/// * `Texture`
/// * `Terrain`
///
/// and so on. Now, an asset may be available in different formats.
/// That's why we have the `Data` associated type here. You can specify
/// an intermediate format here, like the vertex data for a mesh or the samples
/// for audio data.
///
/// This data is then generated by the `Format` trait.
pub trait Asset: Send + Sync + 'static {
/// An identifier for this asset used for debugging.
const NAME: &'static str;
/// The `Data` type the asset can be created from.
type Data: Send + Sync + 'static;
/// The ECS storage type to be used. You'll want to use `VecStorage` in most cases.
type HandleStorage: UnprotectedStorage<Handle<Self>> + Send + Sync;
}
/// A format, providing a conversion from bytes to asset data, which is then
/// in turn accepted by `Asset::from_data`. Examples for formats are
/// `Png`, `Obj` and `Wave`.
pub trait Format<A: Asset>: Send + 'static {
/// A unique identifier for this format.
const NAME: &'static str;
/// Options specific to the format, which are passed to `import`.
/// E.g. for textures this would be stuff like mipmap levels and
/// sampler info.
type Options: Send + 'static;
/// Reads the given bytes and produces asset data.
///
/// ## Reload
///
/// The reload structure has metadata which allows the asset management
/// to reload assets if necessary (for hot reloading).
/// You should only create this if `create_reload` is `true`.
/// Also, the parameter is just a request, which means you can also return `None`.
fn import(
&self,
name: String,
source: Arc<dyn Source>,
options: Self::Options,
create_reload: bool,
) -> Result<FormatValue<A>>;
}
/// The `Ok` return value of `Format::import` for a given asset type `A`.
pub struct FormatValue<A: Asset> {
/// The format data.
pub data: A::Data,
/// An optional reload structure
pub reload: Option<Box<dyn Reload<A>>>,
}
impl<A: Asset> FormatValue<A> {
/// Creates a `FormatValue` from only the data (setting `reload` to `None`).
pub fn data(data: A::Data) -> Self {
FormatValue { data, reload: None }
}
}
/// This is a simplified version of `Format`, which doesn't give you as much freedom,
/// but in return is simpler to implement.
/// All `SimpleFormat` types automatically implement `Format`.
/// This format assumes that the asset name is the full path and the asset is only
/// contained in one file.
pub trait SimpleFormat<A: Asset> {
/// A unique identifier for this format.
const NAME: &'static str;
/// Options specific to the format, which are passed to `import`.
/// E.g. for textures this would be stuff like mipmap levels and
/// sampler info.
type Options: Clone + Send + Sync + 'static;
/// Produces asset data from given bytes.
fn import(&self, bytes: Vec<u8>, options: Self::Options) -> Result<A::Data>;
}
impl<A, T> Format<A> for T
where
A: Asset,
T: SimpleFormat<A> + Clone + Send + Sync + 'static,
{
const NAME: &'static str = T::NAME;
type Options = T::Options;
fn import(
&self,
name: String,
source: Arc<dyn Source>,
options: Self::Options,
create_reload: bool,
) -> Result<FormatValue<A>> {
#[cfg(feature = "profiler")]
profile_scope!("import_asset");
if create_reload {
let (b, m) = source
.load_with_metadata(&name)
.chain_err(|| ErrorKind::Source)?;
let data = T::import(&self, b, options.clone())?;
let reload = SingleFile::new(self.clone(), m, options, name, source);
let reload = Some(Box::new(reload) as Box<dyn Reload<A>>);
Ok(FormatValue { data, reload })
} else {
let b = source.load(&name).chain_err(|| ErrorKind::Source)?;
let data = T::import(&self, b, options)?;
Ok(FormatValue::data(data))
}
}
}