1use crate::{
8 meta::Settings, Asset, AssetId, ErasedLoadedAsset, Handle, LabeledAsset, UntypedAssetId,
9 UntypedHandle,
10};
11use alloc::{boxed::Box, vec::Vec};
12use atomicow::CowArc;
13use bevy_platform::collections::{hash_map::Entry, HashMap};
14use bevy_reflect::TypePath;
15use bevy_tasks::ConditionalSendFuture;
16use core::{
17 borrow::Borrow,
18 convert::Infallible,
19 hash::Hash,
20 marker::PhantomData,
21 ops::{Deref, DerefMut},
22};
23use serde::{Deserialize, Serialize};
24
25pub trait AssetTransformer: TypePath + Send + Sync + 'static {
29 type AssetInput: Asset;
31 type AssetOutput: Asset;
33 type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
35 type Error: Into<Box<dyn core::error::Error + Send + Sync + 'static>>;
37
38 fn transform<'a>(
42 &'a self,
43 asset: TransformedAsset<Self::AssetInput>,
44 settings: &'a Self::Settings,
45 ) -> impl ConditionalSendFuture<Output = Result<TransformedAsset<Self::AssetOutput>, Self::Error>>;
46}
47
48pub struct TransformedAsset<A: Asset> {
50 pub(crate) value: A,
51 pub(crate) labeled_assets: Vec<LabeledAsset>,
52 pub(crate) label_to_asset_index: HashMap<CowArc<'static, str>, usize>,
53 pub(crate) asset_id_to_asset_index: HashMap<UntypedAssetId, usize>,
58}
59
60impl<A: Asset> Deref for TransformedAsset<A> {
61 type Target = A;
62 fn deref(&self) -> &Self::Target {
63 &self.value
64 }
65}
66
67impl<A: Asset> DerefMut for TransformedAsset<A> {
68 fn deref_mut(&mut self) -> &mut Self::Target {
69 &mut self.value
70 }
71}
72
73impl<A: Asset> TransformedAsset<A> {
74 pub fn from_loaded(asset: ErasedLoadedAsset) -> Option<Self> {
76 if let Ok(value) = asset.value.downcast::<A>() {
77 return Some(TransformedAsset {
78 value: *value,
79 labeled_assets: asset.labeled_assets,
80 label_to_asset_index: asset.label_to_asset_index,
81 asset_id_to_asset_index: asset.asset_id_to_asset_index,
82 });
83 }
84 None
85 }
86
87 pub fn replace_asset<B: Asset>(self, asset: B) -> TransformedAsset<B> {
89 TransformedAsset {
90 value: asset,
91 labeled_assets: self.labeled_assets,
92 label_to_asset_index: self.label_to_asset_index,
93 asset_id_to_asset_index: self.asset_id_to_asset_index,
94 }
95 }
96
97 pub fn take_labeled_assets<B: Asset>(&mut self, labeled_source: TransformedAsset<B>) {
99 self.labeled_assets = labeled_source.labeled_assets;
100 self.label_to_asset_index = labeled_source.label_to_asset_index;
101 self.asset_id_to_asset_index = labeled_source.asset_id_to_asset_index;
102 }
103
104 #[inline]
106 pub fn get(&self) -> &A {
107 &self.value
108 }
109
110 #[inline]
112 pub fn get_mut(&mut self) -> &mut A {
113 &mut self.value
114 }
115
116 pub fn get_labeled<B: Asset, Q>(&mut self, label: &Q) -> Option<TransformedSubAsset<'_, B>>
118 where
119 CowArc<'static, str>: Borrow<Q>,
120 Q: ?Sized + Hash + Eq,
121 {
122 let index = self.label_to_asset_index.get(label)?;
123 let labeled = &mut self.labeled_assets[*index];
124 let value = labeled.asset.value.downcast_mut::<B>()?;
125 Some(TransformedSubAsset {
126 value,
127 labeled_assets: &mut labeled.asset.labeled_assets,
128 label_to_asset_index: &mut labeled.asset.label_to_asset_index,
129 asset_id_to_asset_index: &mut labeled.asset.asset_id_to_asset_index,
130 })
131 }
132
133 pub fn get_erased_labeled<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
135 where
136 CowArc<'static, str>: Borrow<Q>,
137 Q: ?Sized + Hash + Eq,
138 {
139 let index = self.label_to_asset_index.get(label)?;
140 let labeled = &self.labeled_assets[*index];
141 Some(&labeled.asset)
142 }
143
144 pub fn get_labeled_by_id<B: Asset>(
149 &mut self,
150 id: impl Into<AssetId<B>>,
151 ) -> Option<TransformedSubAsset<'_, B>> {
152 let index = self.asset_id_to_asset_index.get(&id.into().untyped())?;
153 let labeled = &mut self.labeled_assets[*index];
154 let value = labeled.asset.value.downcast_mut::<B>()?;
155 Some(TransformedSubAsset {
156 value,
157 labeled_assets: &mut labeled.asset.labeled_assets,
158 label_to_asset_index: &mut labeled.asset.label_to_asset_index,
159 asset_id_to_asset_index: &mut labeled.asset.asset_id_to_asset_index,
160 })
161 }
162
163 pub fn get_erased_labeled_by_id(
168 &self,
169 id: impl Into<UntypedAssetId>,
170 ) -> Option<&ErasedLoadedAsset> {
171 let index = self.asset_id_to_asset_index.get(&id.into())?;
172 let labeled = &self.labeled_assets[*index];
173 Some(&labeled.asset)
174 }
175
176 pub fn get_untyped_handle<Q>(&self, label: &Q) -> Option<UntypedHandle>
178 where
179 CowArc<'static, str>: Borrow<Q>,
180 Q: ?Sized + Hash + Eq,
181 {
182 let index = self.label_to_asset_index.get(label)?;
183 let labeled = &self.labeled_assets[*index];
184 Some(labeled.handle.clone())
185 }
186
187 pub fn get_handle<Q, B: Asset>(&self, label: &Q) -> Option<Handle<B>>
189 where
190 CowArc<'static, str>: Borrow<Q>,
191 Q: ?Sized + Hash + Eq,
192 {
193 let index = self.label_to_asset_index.get(label)?;
194 let labeled = &self.labeled_assets[*index];
195 if let Ok(handle) = labeled.handle.clone().try_typed::<B>() {
196 return Some(handle);
197 }
198 None
199 }
200
201 pub fn insert_labeled(
203 &mut self,
204 label: impl Into<CowArc<'static, str>>,
205 handle: impl Into<UntypedHandle>,
206 asset: impl Into<ErasedLoadedAsset>,
207 ) {
208 let labeled = LabeledAsset {
209 asset: asset.into(),
210 handle: handle.into(),
211 };
212 match self.label_to_asset_index.entry(label.into()) {
213 Entry::Occupied(entry) => {
214 let labeled_entry = &mut self.labeled_assets[*entry.get()];
215 if labeled.handle != labeled_entry.handle {
216 self.asset_id_to_asset_index
217 .remove(&labeled_entry.handle.id());
218 self.asset_id_to_asset_index
219 .insert(labeled.handle.id(), *entry.get());
220 }
221 *labeled_entry = labeled;
222 }
223 Entry::Vacant(entry) => {
224 entry.insert(self.labeled_assets.len());
225 self.asset_id_to_asset_index
226 .insert(labeled.handle.id(), self.labeled_assets.len());
227 self.labeled_assets.push(labeled);
228 }
229 }
230 }
231
232 pub fn iter_labels(&self) -> impl Iterator<Item = &str> {
234 self.label_to_asset_index.keys().map(|s| &**s)
235 }
236}
237
238pub struct TransformedSubAsset<'a, A: Asset> {
240 value: &'a mut A,
241 labeled_assets: &'a mut Vec<LabeledAsset>,
242 label_to_asset_index: &'a mut HashMap<CowArc<'static, str>, usize>,
243 asset_id_to_asset_index: &'a mut HashMap<UntypedAssetId, usize>,
248}
249
250impl<'a, A: Asset> Deref for TransformedSubAsset<'a, A> {
251 type Target = A;
252 fn deref(&self) -> &Self::Target {
253 self.value
254 }
255}
256
257impl<'a, A: Asset> DerefMut for TransformedSubAsset<'a, A> {
258 fn deref_mut(&mut self) -> &mut Self::Target {
259 self.value
260 }
261}
262
263impl<'a, A: Asset> TransformedSubAsset<'a, A> {
264 pub fn from_loaded(asset: &'a mut ErasedLoadedAsset) -> Option<Self> {
266 let value = asset.value.downcast_mut::<A>()?;
267 Some(TransformedSubAsset {
268 value,
269 labeled_assets: &mut asset.labeled_assets,
270 label_to_asset_index: &mut asset.label_to_asset_index,
271 asset_id_to_asset_index: &mut asset.asset_id_to_asset_index,
272 })
273 }
274
275 #[inline]
277 pub fn get(&self) -> &A {
278 self.value
279 }
280
281 #[inline]
283 pub fn get_mut(&mut self) -> &mut A {
284 self.value
285 }
286
287 pub fn get_labeled<B: Asset, Q>(&mut self, label: &Q) -> Option<TransformedSubAsset<'_, B>>
289 where
290 CowArc<'static, str>: Borrow<Q>,
291 Q: ?Sized + Hash + Eq,
292 {
293 let index = self.label_to_asset_index.get(label)?;
294 let labeled = &mut self.labeled_assets[*index];
295 let value = labeled.asset.value.downcast_mut::<B>()?;
296 Some(TransformedSubAsset {
297 value,
298 labeled_assets: &mut labeled.asset.labeled_assets,
299 label_to_asset_index: &mut labeled.asset.label_to_asset_index,
300 asset_id_to_asset_index: &mut labeled.asset.asset_id_to_asset_index,
301 })
302 }
303
304 pub fn get_erased_labeled<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
306 where
307 CowArc<'static, str>: Borrow<Q>,
308 Q: ?Sized + Hash + Eq,
309 {
310 let index = self.label_to_asset_index.get(label)?;
311 let labeled = &self.labeled_assets[*index];
312 Some(&labeled.asset)
313 }
314
315 pub fn get_labeled_by_id<B: Asset>(
320 &mut self,
321 id: impl Into<AssetId<B>>,
322 ) -> Option<TransformedSubAsset<'_, B>> {
323 let index = self.asset_id_to_asset_index.get(&id.into().untyped())?;
324 let labeled = &mut self.labeled_assets[*index];
325 let value = labeled.asset.value.downcast_mut::<B>()?;
326 Some(TransformedSubAsset {
327 value,
328 labeled_assets: &mut labeled.asset.labeled_assets,
329 label_to_asset_index: &mut labeled.asset.label_to_asset_index,
330 asset_id_to_asset_index: &mut labeled.asset.asset_id_to_asset_index,
331 })
332 }
333
334 pub fn get_erased_labeled_by_id(
339 &self,
340 id: impl Into<UntypedAssetId>,
341 ) -> Option<&ErasedLoadedAsset> {
342 let index = self.asset_id_to_asset_index.get(&id.into())?;
343 let labeled = &self.labeled_assets[*index];
344 Some(&labeled.asset)
345 }
346
347 pub fn get_untyped_handle<Q>(&self, label: &Q) -> Option<UntypedHandle>
349 where
350 CowArc<'static, str>: Borrow<Q>,
351 Q: ?Sized + Hash + Eq,
352 {
353 let index = self.label_to_asset_index.get(label)?;
354 let labeled = &self.labeled_assets[*index];
355 Some(labeled.handle.clone())
356 }
357
358 pub fn get_handle<Q, B: Asset>(&self, label: &Q) -> Option<Handle<B>>
360 where
361 CowArc<'static, str>: Borrow<Q>,
362 Q: ?Sized + Hash + Eq,
363 {
364 let index = self.label_to_asset_index.get(label)?;
365 let labeled = &self.labeled_assets[*index];
366 if let Ok(handle) = labeled.handle.clone().try_typed::<B>() {
367 return Some(handle);
368 }
369 None
370 }
371
372 pub fn insert_labeled(
374 &mut self,
375 label: impl Into<CowArc<'static, str>>,
376 handle: impl Into<UntypedHandle>,
377 asset: impl Into<ErasedLoadedAsset>,
378 ) {
379 let labeled = LabeledAsset {
380 asset: asset.into(),
381 handle: handle.into(),
382 };
383 match self.label_to_asset_index.entry(label.into()) {
384 Entry::Occupied(entry) => {
385 let labeled_entry = &mut self.labeled_assets[*entry.get()];
386 if labeled.handle != labeled_entry.handle {
387 self.asset_id_to_asset_index
388 .remove(&labeled_entry.handle.id());
389 self.asset_id_to_asset_index
390 .insert(labeled.handle.id(), *entry.get());
391 }
392 *labeled_entry = labeled;
393 }
394 Entry::Vacant(entry) => {
395 entry.insert(self.labeled_assets.len());
396 self.asset_id_to_asset_index
397 .insert(labeled.handle.id(), self.labeled_assets.len());
398 self.labeled_assets.push(labeled);
399 }
400 }
401 }
402 pub fn iter_labels(&self) -> impl Iterator<Item = &str> {
404 self.label_to_asset_index.keys().map(|s| &**s)
405 }
406}
407
408#[derive(TypePath)]
410pub struct IdentityAssetTransformer<A: Asset> {
411 _phantom: PhantomData<fn(A) -> A>,
412}
413
414impl<A: Asset> IdentityAssetTransformer<A> {
415 pub const fn new() -> Self {
417 Self {
418 _phantom: PhantomData,
419 }
420 }
421}
422
423impl<A: Asset> Default for IdentityAssetTransformer<A> {
424 fn default() -> Self {
425 Self::new()
426 }
427}
428
429impl<A: Asset> AssetTransformer for IdentityAssetTransformer<A> {
430 type AssetInput = A;
431 type AssetOutput = A;
432 type Settings = ();
433 type Error = Infallible;
434
435 async fn transform<'a>(
436 &'a self,
437 asset: TransformedAsset<Self::AssetInput>,
438 _settings: &'a Self::Settings,
439 ) -> Result<TransformedAsset<Self::AssetOutput>, Self::Error> {
440 Ok(asset)
441 }
442}