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}