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