Skip to main content

coffee/graphics/texture_array/
loader.rs

1use std::collections::VecDeque;
2use std::path::PathBuf;
3
4use super::{Builder, Index, TextureArray};
5use crate::load::Task;
6use crate::{Error, Result};
7
8/// A [`TextureArray`] builder that produces a [`Task`].
9///
10/// You should use [`add`] to get an index [`Key`] per texture so you can
11/// retrieve each [`Index`] from the provided [`Indices`] on [`finish`].
12///
13/// For example, let's say that we want to use a [`TextureArray`] for our
14/// entities. We could write in our `entity` module:
15///
16/// ```
17/// use coffee::load::Task;
18/// use coffee::graphics::texture_array::{TextureArray, Index, Loader};
19///
20/// pub struct Assets {
21///     player: Index,
22///     enemy: Index,
23///     building: Index,
24///     items: Index,
25///     texture: TextureArray,
26/// }
27///
28/// impl Assets {
29///     pub fn load() -> Task<Assets> {
30///         let mut loader = Loader::new(2048, 2048);
31///
32///         let player = loader.add("player.png");
33///         let enemy = loader.add("enemy.png");
34///         let building = loader.add("building.png");
35///         let items = loader.add("items.png");
36///
37///         loader.finish(move |texture, indices| Ok(Assets {
38///             player: indices.get(player)?,
39///             enemy: indices.get(enemy)?,
40///             building: indices.get(building)?,
41///             items: indices.get(items)?,
42///             texture,
43///         }))
44///     }
45/// }
46/// ```
47///
48/// [`TextureArray`]: struct.TextureArray.html
49/// [`Task`]: ../../load/struct.Task.html
50/// [`add`]: #method.add
51/// [`Key`]: struct.Key.html
52/// [`Index`]: struct.Index.html
53/// [`Indices`]: struct.Indices.html
54/// [`finish`]: #method.finish
55#[derive(Debug)]
56pub struct Loader {
57    width: u16,
58    height: u16,
59    paths: Vec<PathBuf>,
60}
61
62impl Loader {
63    /// Creates a new [`Loader`] that produces a [`TextureArray`] of the given
64    /// size.
65    ///
66    /// [`Loader`]: struct.Loader.html
67    /// [`TextureArray`]: struct.TextureArray.html
68    pub fn new(width: u16, height: u16) -> Loader {
69        Loader {
70            width,
71            height,
72            paths: Vec::new(),
73        }
74    }
75
76    /// Queues an image to be added to the produced [`TextureArray`] and obtain
77    /// a [`Key`] to its [`Index`].
78    ///
79    /// [`TextureArray`]: struct.TextureArray.html
80    /// [`Key`]: struct.Key.html
81    /// [`Index`]: struct.Index.html
82    pub fn add<P: Into<PathBuf>>(&mut self, path: P) -> Key {
83        self.paths.push(path.into());
84        Key(self.paths.len() - 1)
85    }
86
87    /// Finishes the [`Loader`] definition and obtain a [`Task`] that produces
88    /// a value from the loaded [`TextureArray`] and its [`Indices`].
89    ///
90    /// [`Loader`]: struct.Loader.html
91    /// [`Task`]: ../../load/struct.Task.html
92    /// [`TextureArray`]: struct.TextureArray.html
93    /// [`Indices`]: struct.Indices.html
94    pub fn finish<F, T>(self, on_completion: F) -> Task<T>
95    where
96        F: 'static + Fn(TextureArray, Indices) -> Result<T>,
97    {
98        let total_work = self.paths.len() as u32 + 1;
99
100        Task::sequence(total_work, move |task| {
101            let mut builder = Builder::new(self.width, self.height);
102            let mut work_todo = VecDeque::from(self.paths.clone());
103            let mut indices = Vec::new();
104
105            while let Some(next) = work_todo.pop_front() {
106                let index = builder.add(next)?;
107                indices.push(index);
108
109                task.notify_progress(1);
110            }
111
112            let result =
113                on_completion(builder.build(task.gpu()), Indices(indices))?;
114
115            task.notify_progress(1);
116
117            Ok(result)
118        })
119    }
120}
121
122/// A key used to obtain an [`Index`] from [`Indices`] once a [`TextureArray`]
123/// is loaded using a [`Loader`].
124///
125/// [`Key`]: struct.Key.html
126/// [`Index`]: struct.Index.html
127/// [`Indices`]: struct.Indices.html
128/// [`TextureArray`]: struct.TextureArray.html
129/// [`Loader`]: struct.Loader.html
130#[derive(Clone, Copy, Eq, PartialEq, Debug)]
131pub struct Key(usize);
132
133/// A set of loaded indices obtained when using a [`Loader`].
134///
135/// [`Loader`]: struct.Loader.html
136#[derive(Clone, PartialEq, Debug)]
137pub struct Indices(Vec<Index>);
138
139impl Indices {
140    /// Get an [`Index`] for the given [`Key`].
141    ///
142    /// [`Key`]: struct.Key.html
143    /// [`Index`]: struct.Index.html
144    pub fn get(&self, key: Key) -> Result<Index> {
145        self.0
146            .get(key.0)
147            .cloned()
148            .ok_or(Error::TextureArray(super::Error::KeyNotFound(key.0)))
149    }
150}