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
use std::sync::Arc; use specs::UnprotectedStorage; use {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 { /// 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<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<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<Source>, options: Self::Options, create_reload: bool, ) -> Result<FormatValue<A>> { 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<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)) } } }