zengine_asset/
assets.rs

1use crossbeam_channel::Sender;
2use downcast_rs::{impl_downcast, Downcast};
3use rustc_hash::FxHashMap;
4use std::{ffi::OsStr, path::PathBuf};
5use zengine_macro::Resource;
6
7use crate::{
8    handle::{HandleId, HandleRef},
9    Handle,
10};
11
12/// An Asset rappresent any kind of external data like
13/// images, sound, text file etc..
14///
15/// To load and asset into you game you have to load from
16/// the filesystem with [AssetManager::load](crate::AssetManager::load)
17///
18/// You should avoid to implement the Asset trait manually and use instead
19/// the [asset derive macro](zengine_macro::Asset)
20pub trait Asset: Downcast + Send + Sync + std::fmt::Debug + 'static {
21    /// Returns an unique asset id.
22    ///
23    /// Normally it's used the asset path as unique id but in case
24    /// this is not possible (eg: get multiple id for the same asset path)
25    /// the engine will call this function.
26    ///
27    /// The [asset derive macro](zengine_macro::Asset) implement this function
28    /// in the following way
29    ///
30    /// ```ignore
31    /// static ASSETTEST_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
32    /// impl Asset for AssetTest {
33    ///    fn next_counter() -> u64
34    ///    where
35    ///        Self: Sized,
36    ///    {
37    ///        ASSETTEST_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
38    ///    }
39    ///}
40    /// ```
41    fn next_counter() -> u64
42    where
43        Self: Sized;
44}
45impl_downcast!(Asset);
46
47/// A [Resource](zengine_ecs::Resource) that stores Assets of a given type
48///
49/// Each asset is mapped by an unique [`HandleId`], allowing any [`Handle`] with the same
50/// [`HandleId`] to access it.
51/// One asset remain loaded as long as a Strong handle to that asset exists.
52///
53/// To get a reference to an asset without forcing it to stay loadid you can use a Weak handle
54#[derive(Resource, Debug)]
55pub struct Assets<T: Asset> {
56    assets: FxHashMap<HandleId, T>,
57    pub(crate) sender: Sender<HandleRef>,
58}
59
60impl<T: Asset> Assets<T> {
61    pub(crate) fn new(sender: Sender<HandleRef>) -> Self {
62        Self {
63            assets: FxHashMap::default(),
64            sender,
65        }
66    }
67
68    /// Checks if an asset exists for the given handle
69    pub fn contains(&self, handle: &Handle<T>) -> bool {
70        self.assets.contains_key(&handle.id)
71    }
72
73    /// Get an asset reference for the given handle
74    pub fn get(&self, handle: &Handle<T>) -> Option<&T> {
75        self.assets.get(&handle.id)
76    }
77
78    /// Get a mutable asset reference for the given handle
79    pub fn get_mut(&mut self, handle: &Handle<T>) -> Option<&mut T> {
80        self.assets.get_mut(&handle.id)
81    }
82
83    /// Gets an iterator over the assets in the storage
84    pub fn iter(&self) -> impl Iterator<Item = (&HandleId, &T)> {
85        self.assets.iter()
86    }
87
88    /// Gets a mutable iterator over the assets in the storage
89    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&HandleId, &mut T)> {
90        self.assets.iter_mut()
91    }
92
93    /// Add an asset to the storage returning a strong handle to that asset
94    pub fn add(&mut self, asset: T) -> Handle<T> {
95        let handle = Handle::strong(
96            HandleId::new_from_u64::<T>(T::next_counter()),
97            self.sender.clone(),
98        );
99
100        self.set_untracked(handle.id, asset);
101
102        handle
103    }
104
105    /// Add/Replace the asset pointed by the given handle
106    /// returning a strong handle to that asset
107    pub fn set(&mut self, handle: Handle<T>, asset: T) -> Handle<T> {
108        let id = handle.id;
109        self.set_untracked(id, asset);
110
111        Handle::strong(id, self.sender.clone())
112    }
113
114    /// Add/Replace the asset pointed by the given handle
115    pub fn set_untracked(&mut self, handle_id: HandleId, asset: T) {
116        self.assets.insert(handle_id, asset);
117    }
118
119    /// Remove the asset pointed by the given handle from the storage
120    ///
121    /// The asset is returned
122    pub fn remove<H: Into<HandleId>>(&mut self, handle: H) -> Option<T> {
123        let id = handle.into();
124        self.assets.remove(&id)
125    }
126
127    /// Gets the number of assets in the storage
128    pub fn len(&self) -> usize {
129        self.assets.len()
130    }
131
132    /// Returns `true` if there are no stored assets
133    pub fn is_empty(&self) -> bool {
134        self.assets.is_empty()
135    }
136}
137
138/// Represents a path to an asset in the file system
139#[derive(Debug)]
140pub struct AssetPath {
141    pub(crate) path: PathBuf,
142    pub(crate) extension: String,
143}
144
145impl From<&str> for AssetPath {
146    fn from(file_path: &str) -> Self {
147        let path = std::path::Path::new(file_path);
148
149        let extension = path
150            .extension()
151            .and_then(OsStr::to_str)
152            .unwrap_or("")
153            .to_owned();
154
155        AssetPath {
156            path: path.into(),
157            extension,
158        }
159    }
160}