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}