Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_asset/src/transformer.rs
6598 views
1
use crate::{meta::Settings, Asset, ErasedLoadedAsset, Handle, LabeledAsset, UntypedHandle};
2
use alloc::boxed::Box;
3
use atomicow::CowArc;
4
use bevy_platform::collections::HashMap;
5
use bevy_tasks::ConditionalSendFuture;
6
use core::{
7
borrow::Borrow,
8
convert::Infallible,
9
hash::Hash,
10
marker::PhantomData,
11
ops::{Deref, DerefMut},
12
};
13
use serde::{Deserialize, Serialize};
14
15
/// Transforms an [`Asset`] of a given [`AssetTransformer::AssetInput`] type to an [`Asset`] of [`AssetTransformer::AssetOutput`] type.
16
///
17
/// This trait is commonly used in association with [`LoadTransformAndSave`](crate::processor::LoadTransformAndSave) to accomplish common asset pipeline workflows.
18
pub trait AssetTransformer: Send + Sync + 'static {
19
/// The [`Asset`] type which this [`AssetTransformer`] takes as and input.
20
type AssetInput: Asset;
21
/// The [`Asset`] type which this [`AssetTransformer`] outputs.
22
type AssetOutput: Asset;
23
/// The settings type used by this [`AssetTransformer`].
24
type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
25
/// The type of [error](`std::error::Error`) which could be encountered by this transformer.
26
type Error: Into<Box<dyn core::error::Error + Send + Sync + 'static>>;
27
28
/// Transforms the given [`TransformedAsset`] to [`AssetTransformer::AssetOutput`].
29
/// The [`TransformedAsset`]'s `labeled_assets` can be altered to add new Labeled Sub-Assets
30
/// The passed in `settings` can influence how the `asset` is transformed
31
fn transform<'a>(
32
&'a self,
33
asset: TransformedAsset<Self::AssetInput>,
34
settings: &'a Self::Settings,
35
) -> impl ConditionalSendFuture<Output = Result<TransformedAsset<Self::AssetOutput>, Self::Error>>;
36
}
37
38
/// An [`Asset`] (and any "sub assets") intended to be transformed
39
pub struct TransformedAsset<A: Asset> {
40
pub(crate) value: A,
41
pub(crate) labeled_assets: HashMap<CowArc<'static, str>, LabeledAsset>,
42
}
43
44
impl<A: Asset> Deref for TransformedAsset<A> {
45
type Target = A;
46
fn deref(&self) -> &Self::Target {
47
&self.value
48
}
49
}
50
51
impl<A: Asset> DerefMut for TransformedAsset<A> {
52
fn deref_mut(&mut self) -> &mut Self::Target {
53
&mut self.value
54
}
55
}
56
57
impl<A: Asset> TransformedAsset<A> {
58
/// Creates a new [`TransformedAsset`] from `asset` if its internal value matches `A`.
59
pub fn from_loaded(asset: ErasedLoadedAsset) -> Option<Self> {
60
if let Ok(value) = asset.value.downcast::<A>() {
61
return Some(TransformedAsset {
62
value: *value,
63
labeled_assets: asset.labeled_assets,
64
});
65
}
66
None
67
}
68
/// Creates a new [`TransformedAsset`] from `asset`, transferring the `labeled_assets` from this [`TransformedAsset`] to the new one
69
pub fn replace_asset<B: Asset>(self, asset: B) -> TransformedAsset<B> {
70
TransformedAsset {
71
value: asset,
72
labeled_assets: self.labeled_assets,
73
}
74
}
75
/// Takes the labeled assets from `labeled_source` and places them in this [`TransformedAsset`]
76
pub fn take_labeled_assets<B: Asset>(&mut self, labeled_source: TransformedAsset<B>) {
77
self.labeled_assets = labeled_source.labeled_assets;
78
}
79
/// Retrieves the value of this asset.
80
#[inline]
81
pub fn get(&self) -> &A {
82
&self.value
83
}
84
/// Mutably retrieves the value of this asset.
85
#[inline]
86
pub fn get_mut(&mut self) -> &mut A {
87
&mut self.value
88
}
89
/// Returns the labeled asset, if it exists and matches this type.
90
pub fn get_labeled<B: Asset, Q>(&mut self, label: &Q) -> Option<TransformedSubAsset<'_, B>>
91
where
92
CowArc<'static, str>: Borrow<Q>,
93
Q: ?Sized + Hash + Eq,
94
{
95
let labeled = self.labeled_assets.get_mut(label)?;
96
let value = labeled.asset.value.downcast_mut::<B>()?;
97
Some(TransformedSubAsset {
98
value,
99
labeled_assets: &mut labeled.asset.labeled_assets,
100
})
101
}
102
/// Returns the type-erased labeled asset, if it exists and matches this type.
103
pub fn get_erased_labeled<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
104
where
105
CowArc<'static, str>: Borrow<Q>,
106
Q: ?Sized + Hash + Eq,
107
{
108
let labeled = self.labeled_assets.get(label)?;
109
Some(&labeled.asset)
110
}
111
/// Returns the [`UntypedHandle`] of the labeled asset with the provided 'label', if it exists.
112
pub fn get_untyped_handle<Q>(&self, label: &Q) -> Option<UntypedHandle>
113
where
114
CowArc<'static, str>: Borrow<Q>,
115
Q: ?Sized + Hash + Eq,
116
{
117
let labeled = self.labeled_assets.get(label)?;
118
Some(labeled.handle.clone())
119
}
120
/// Returns the [`Handle`] of the labeled asset with the provided 'label', if it exists and is an asset of type `B`
121
pub fn get_handle<Q, B: Asset>(&self, label: &Q) -> Option<Handle<B>>
122
where
123
CowArc<'static, str>: Borrow<Q>,
124
Q: ?Sized + Hash + Eq,
125
{
126
let labeled = self.labeled_assets.get(label)?;
127
if let Ok(handle) = labeled.handle.clone().try_typed::<B>() {
128
return Some(handle);
129
}
130
None
131
}
132
/// Adds `asset` as a labeled sub asset using `label` and `handle`
133
pub fn insert_labeled(
134
&mut self,
135
label: impl Into<CowArc<'static, str>>,
136
handle: impl Into<UntypedHandle>,
137
asset: impl Into<ErasedLoadedAsset>,
138
) {
139
let labeled = LabeledAsset {
140
asset: asset.into(),
141
handle: handle.into(),
142
};
143
self.labeled_assets.insert(label.into(), labeled);
144
}
145
/// Iterate over all labels for "labeled assets" in the loaded asset
146
pub fn iter_labels(&self) -> impl Iterator<Item = &str> {
147
self.labeled_assets.keys().map(|s| &**s)
148
}
149
}
150
151
/// A labeled sub-asset of [`TransformedAsset`]
152
pub struct TransformedSubAsset<'a, A: Asset> {
153
value: &'a mut A,
154
labeled_assets: &'a mut HashMap<CowArc<'static, str>, LabeledAsset>,
155
}
156
157
impl<'a, A: Asset> Deref for TransformedSubAsset<'a, A> {
158
type Target = A;
159
fn deref(&self) -> &Self::Target {
160
self.value
161
}
162
}
163
164
impl<'a, A: Asset> DerefMut for TransformedSubAsset<'a, A> {
165
fn deref_mut(&mut self) -> &mut Self::Target {
166
self.value
167
}
168
}
169
170
impl<'a, A: Asset> TransformedSubAsset<'a, A> {
171
/// Creates a new [`TransformedSubAsset`] from `asset` if its internal value matches `A`.
172
pub fn from_loaded(asset: &'a mut ErasedLoadedAsset) -> Option<Self> {
173
let value = asset.value.downcast_mut::<A>()?;
174
Some(TransformedSubAsset {
175
value,
176
labeled_assets: &mut asset.labeled_assets,
177
})
178
}
179
/// Retrieves the value of this asset.
180
#[inline]
181
pub fn get(&self) -> &A {
182
self.value
183
}
184
/// Mutably retrieves the value of this asset.
185
#[inline]
186
pub fn get_mut(&mut self) -> &mut A {
187
self.value
188
}
189
/// Returns the labeled asset, if it exists and matches this type.
190
pub fn get_labeled<B: Asset, Q>(&mut self, label: &Q) -> Option<TransformedSubAsset<'_, B>>
191
where
192
CowArc<'static, str>: Borrow<Q>,
193
Q: ?Sized + Hash + Eq,
194
{
195
let labeled = self.labeled_assets.get_mut(label)?;
196
let value = labeled.asset.value.downcast_mut::<B>()?;
197
Some(TransformedSubAsset {
198
value,
199
labeled_assets: &mut labeled.asset.labeled_assets,
200
})
201
}
202
/// Returns the type-erased labeled asset, if it exists and matches this type.
203
pub fn get_erased_labeled<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
204
where
205
CowArc<'static, str>: Borrow<Q>,
206
Q: ?Sized + Hash + Eq,
207
{
208
let labeled = self.labeled_assets.get(label)?;
209
Some(&labeled.asset)
210
}
211
/// Returns the [`UntypedHandle`] of the labeled asset with the provided 'label', if it exists.
212
pub fn get_untyped_handle<Q>(&self, label: &Q) -> Option<UntypedHandle>
213
where
214
CowArc<'static, str>: Borrow<Q>,
215
Q: ?Sized + Hash + Eq,
216
{
217
let labeled = self.labeled_assets.get(label)?;
218
Some(labeled.handle.clone())
219
}
220
/// Returns the [`Handle`] of the labeled asset with the provided 'label', if it exists and is an asset of type `B`
221
pub fn get_handle<Q, B: Asset>(&self, label: &Q) -> Option<Handle<B>>
222
where
223
CowArc<'static, str>: Borrow<Q>,
224
Q: ?Sized + Hash + Eq,
225
{
226
let labeled = self.labeled_assets.get(label)?;
227
if let Ok(handle) = labeled.handle.clone().try_typed::<B>() {
228
return Some(handle);
229
}
230
None
231
}
232
/// Adds `asset` as a labeled sub asset using `label` and `handle`
233
pub fn insert_labeled(
234
&mut self,
235
label: impl Into<CowArc<'static, str>>,
236
handle: impl Into<UntypedHandle>,
237
asset: impl Into<ErasedLoadedAsset>,
238
) {
239
let labeled = LabeledAsset {
240
asset: asset.into(),
241
handle: handle.into(),
242
};
243
self.labeled_assets.insert(label.into(), labeled);
244
}
245
/// Iterate over all labels for "labeled assets" in the loaded asset
246
pub fn iter_labels(&self) -> impl Iterator<Item = &str> {
247
self.labeled_assets.keys().map(|s| &**s)
248
}
249
}
250
251
/// An identity [`AssetTransformer`] which infallibly returns the input [`Asset`] on transformation.]
252
pub struct IdentityAssetTransformer<A: Asset> {
253
_phantom: PhantomData<fn(A) -> A>,
254
}
255
256
impl<A: Asset> IdentityAssetTransformer<A> {
257
/// Creates a new [`IdentityAssetTransformer`] with the correct internal [`PhantomData`] field.
258
pub const fn new() -> Self {
259
Self {
260
_phantom: PhantomData,
261
}
262
}
263
}
264
265
impl<A: Asset> Default for IdentityAssetTransformer<A> {
266
fn default() -> Self {
267
Self::new()
268
}
269
}
270
271
impl<A: Asset> AssetTransformer for IdentityAssetTransformer<A> {
272
type AssetInput = A;
273
type AssetOutput = A;
274
type Settings = ();
275
type Error = Infallible;
276
277
async fn transform<'a>(
278
&'a self,
279
asset: TransformedAsset<Self::AssetInput>,
280
_settings: &'a Self::Settings,
281
) -> Result<TransformedAsset<Self::AssetOutput>, Self::Error> {
282
Ok(asset)
283
}
284
}
285
286