Skip to main content

amethyst_assets/
asset.rs

1use crate::{storage::ProcessingState, FormatRegisteredData, Handle, Reload, SingleFile, Source};
2use amethyst_core::ecs::storage::UnprotectedStorage;
3use amethyst_error::{Error, ResultExt};
4use std::{fmt::Debug, ops::Deref, sync::Arc};
5
6#[cfg(feature = "profiler")]
7use thread_profiler::profile_scope;
8
9/// One of the three core traits of this crate.
10///
11/// You want to implement this for every type of asset like
12///
13/// * `Mesh`
14/// * `Texture`
15/// * `Terrain`
16///
17/// and so on. Now, an asset may be available in different formats.
18/// That's why we have the `Data` associated type here. You can specify
19/// an intermediate format here, like the vertex data for a mesh or the samples
20/// for audio data.
21///
22/// This data is then generated by the `Format` trait.
23pub trait Asset: Send + Sync + 'static {
24    /// An identifier for this asset used for debugging.
25    const NAME: &'static str;
26
27    /// The `Data` type the asset can be created from.
28    type Data: Send + Sync + 'static;
29
30    /// The ECS storage type to be used. You'll want to use `DenseVecStorage` in most cases.
31    type HandleStorage: UnprotectedStorage<Handle<Self>> + Send + Sync;
32}
33
34/// Defines a way to process asset's data into the asset. This allows
35/// using default `Processor` system to process assets that implement that type.
36pub trait ProcessableAsset: Asset + Sized {
37    /// Processes asset data into asset during loading.
38    fn process(data: Self::Data) -> Result<ProcessingState<Self>, Error>;
39}
40
41impl<T: Asset<Data = T>> ProcessableAsset for T {
42    fn process(data: Self::Data) -> Result<ProcessingState<Self>, Error> {
43        Ok(ProcessingState::Loaded(data))
44    }
45}
46
47/// A format, providing a conversion from bytes to asset data, which is then
48/// in turn accepted by `Asset::from_data`. Examples for formats are
49/// `Png`, `Obj` and `Wave`.
50///
51/// The format type itself represents loading options, which are passed to `import`.
52/// E.g. for textures this would be stuff like mipmap levels and
53/// sampler info.
54pub trait Format<D: 'static>: objekt::Clone + Debug + Send + Sync + 'static {
55    /// A unique identifier for this format.
56    fn name(&self) -> &'static str;
57
58    /// Produces asset data from given bytes.
59    /// This method is a simplified version of `format`.
60    /// This format assumes that the asset name is the full path and the asset is only
61    /// contained in one file.
62    ///
63    /// If you are implementing `format` yourself, this method will never be used
64    /// and can be left unimplemented.
65    ///
66    fn import_simple(&self, _bytes: Vec<u8>) -> Result<D, Error> {
67        unimplemented!("You must implement either `import_simple` or `import`.")
68    }
69
70    /// Reads the given bytes and produces asset data.
71    ///
72    /// You can implement `import_simple` instead of simpler formats.
73    ///
74    /// ## Reload
75    ///
76    /// The reload structure has metadata which allows the asset management
77    /// to reload assets if necessary (for hot reloading).
78    /// You should only create `Reload` when `create_reload` is `Some`.
79    /// Also, the parameter is just a request, which means it's optional either way.
80    fn import(
81        &self,
82        name: String,
83        source: Arc<dyn Source>,
84        create_reload: Option<Box<dyn Format<D>>>,
85    ) -> Result<FormatValue<D>, Error> {
86        #[cfg(feature = "profiler")]
87        profile_scope!("import_asset");
88        if let Some(boxed_format) = create_reload {
89            let (b, m) = source
90                .load_with_metadata(&name)
91                .with_context(|_| crate::error::Error::Source)?;
92            Ok(FormatValue {
93                data: self.import_simple(b)?,
94                reload: Some(Box::new(SingleFile::new(boxed_format, m, name, source))),
95            })
96        } else {
97            let b = source
98                .load(&name)
99                .with_context(|_| crate::error::Error::Source)?;
100            Ok(FormatValue::data(self.import_simple(b)?))
101        }
102    }
103}
104
105objekt::clone_trait_object!(<D> Format<D>);
106
107/// SerializableFormat is a marker trait which is required for Format types that are supposed
108/// to be serialized. This trait implies both `Serialize` and `Deserialize` implementation.
109///
110/// **Note:** This trait should never be implemented manually.
111/// Use the `register_format` macro to register it correctly.
112/// See [FormatRegisteredData](trait.FormatRegisteredData.html) for the full example.
113pub trait SerializableFormat<D: FormatRegisteredData + 'static>:
114    Format<D> + erased_serde::Serialize + 'static
115{
116    // Empty.
117}
118
119objekt::clone_trait_object!(<D> SerializableFormat<D>);
120
121// Allow using dynamic types on sites that accept format as generic.
122impl<D: 'static> Format<D> for Box<dyn Format<D>> {
123    fn name(&self) -> &'static str {
124        self.deref().name()
125    }
126    fn import_simple(&self, bytes: Vec<u8>) -> Result<D, Error> {
127        self.deref().import_simple(bytes)
128    }
129
130    fn import(
131        &self,
132        name: String,
133        source: Arc<dyn Source>,
134        create_reload: Option<Box<dyn Format<D>>>,
135    ) -> Result<FormatValue<D>, Error> {
136        self.deref().import(name, source, create_reload)
137    }
138}
139
140impl<D: 'static> Format<D> for Box<dyn SerializableFormat<D>> {
141    fn name(&self) -> &'static str {
142        self.deref().name()
143    }
144    fn import_simple(&self, bytes: Vec<u8>) -> Result<D, Error> {
145        self.deref().import_simple(bytes)
146    }
147
148    fn import(
149        &self,
150        name: String,
151        source: Arc<dyn Source>,
152        create_reload: Option<Box<dyn Format<D>>>,
153    ) -> Result<FormatValue<D>, Error> {
154        self.deref().import(name, source, create_reload)
155    }
156}
157
158impl<D: FormatRegisteredData + 'static> SerializableFormat<D> for Box<dyn SerializableFormat<D>> {}
159
160/// The `Ok` return value of `Format::import` for a given asset type `A`.
161pub struct FormatValue<D> {
162    /// The format data.
163    pub data: D,
164    /// An optional reload structure
165    pub reload: Option<Box<dyn Reload<D>>>,
166}
167
168impl<D> FormatValue<D> {
169    /// Creates a `FormatValue` from only the data (setting `reload` to `None`).
170    pub fn data(data: D) -> Self {
171        FormatValue { data, reload: None }
172    }
173}