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