mirage_engine/mesh/slot.rs
1use crate::assets::Unresolved;
2use crate::{Material, ReliefData, ShadingData, TextureData};
3
4/// What one part of a mesh draws with: a material and its maps, over some
5/// of the mesh's triangles.
6///
7/// A slot is a length, not a range.
8#[derive(Clone, Debug)]
9pub struct Slot {
10 index_count: u32,
11 material: Material,
12 texture: Option<TextureData>,
13 relief: Option<ReliefData>,
14 shading: Option<ShadingData>,
15 emissive: Option<TextureData>,
16 /// The index of the part naming this slot, absent while it is
17 /// anonymous.
18 part: Option<u32>,
19}
20
21impl Slot {
22 /// A slot drawn with `material` over a white default, for the next
23 /// `index_count` indices.
24 pub fn new(index_count: u32, material: Material) -> Self {
25 Self {
26 index_count,
27 material,
28 texture: None,
29 relief: None,
30 shading: None,
31 emissive: None,
32 part: None,
33 }
34 }
35
36 /// Samples this slot from `texture`, in place of the white default.
37 #[must_use]
38 pub fn textured(mut self, texture: TextureData) -> Self {
39 self.texture = Some(texture);
40 self
41 }
42
43 /// The relief is a texture map: `RGB` holds each texel's normal, `+Y` up
44 /// the map, and `A` its depth where the relief declares one, `1.0` one
45 /// sprite width, half in front of the sprite and half behind.
46 ///
47 /// Required if you want a surface lit by normals its own corners do not
48 /// hold. A billboarded or upright draw reads the normal in the axes the
49 /// camera turned; every other draw reads it through the derivative
50 /// basis — the basis its own position and UV derivatives span — and
51 /// ignores the depth. The relief is windowed like the color texture, so
52 /// each sheet cell lines up with its own relief cell. A depth also
53 /// decides what a billboarded or upright draw casts: a texel with no
54 /// depth casts nothing, and depth under the sprite's own texels, down to
55 /// its foot, produces a shadow that starts at the ground it is drawn on.
56 #[must_use]
57 pub fn relief(mut self, relief: ReliefData) -> Self {
58 self.relief = Some(relief);
59 self
60 }
61
62 /// The shading map holds each texel's occlusion in `R`, its roughness in
63 /// `G` and its metallic in `B`. The roughness and metallic each scale the
64 /// slot's own lane; the occlusion instead darkens what the texel takes
65 /// of the frame's sky.
66 ///
67 /// Required if you want the roughness, the metallic or the sky's own
68 /// light on one material to differ across the surface. Every light of
69 /// the frame lands whole, whatever the occlusion holds.
70 #[must_use]
71 pub fn shading(mut self, shading: ShadingData) -> Self {
72 self.shading = Some(shading);
73 self
74 }
75
76 /// The emissive map is the light this slot casts per texel, scaled by the
77 /// material's emissive color.
78 ///
79 /// Required if you want part of a surface to cast light while the rest of
80 /// it does not. A material casts none by default, so the map alone casts
81 /// nothing.
82 #[must_use]
83 pub fn emissive_map(mut self, emissive: TextureData) -> Self {
84 self.emissive = Some(emissive);
85 self
86 }
87
88 /// The number of the mesh's indices this slot covers.
89 pub fn index_count(&self) -> u32 {
90 self.index_count
91 }
92
93 /// The material used unless a draw overrides this slot.
94 pub fn material(&self) -> Material {
95 self.material
96 }
97
98 /// The pixels this slot samples, absent while it is drawn over white.
99 pub fn texture(&self) -> Option<&TextureData> {
100 self.texture.as_ref()
101 }
102
103 /// The same slot named by the part at `part`.
104 pub(crate) fn named(mut self, part: u32) -> Self {
105 self.part = Some(part);
106 self
107 }
108
109 /// The index of the part naming this slot, absent while it is
110 /// anonymous.
111 pub(crate) fn part(&self) -> Option<u32> {
112 self.part
113 }
114
115 /// The relief this slot reads, absent where it has none.
116 pub(crate) fn relief_map(&self) -> Option<&ReliefData> {
117 self.relief.as_ref()
118 }
119
120 /// The shading map this slot reads, absent where it has none.
121 pub(crate) fn shading_map(&self) -> Option<&ShadingData> {
122 self.shading.as_ref()
123 }
124
125 /// The emissive map this slot reads, absent where it has none.
126 pub(crate) fn emissive(&self) -> Option<&TextureData> {
127 self.emissive.as_ref()
128 }
129
130 /// Sets the material, in place of the one it has.
131 pub(crate) fn set_material(&mut self, material: Material) {
132 self.material = material;
133 }
134
135 /// Sets the texture, in place of any it has, and returns what the pulls
136 /// of the one it displaced did not get.
137 #[must_use]
138 pub(crate) fn set_texture(&mut self, texture: TextureData) -> Unresolved {
139 displaced(self.texture.replace(texture).as_mut())
140 }
141
142 /// Sets the relief, in place of any it has, and returns what the pulls
143 /// of the one it displaced did not get.
144 #[must_use]
145 pub(crate) fn set_relief(&mut self, relief: ReliefData) -> Unresolved {
146 displaced(
147 self.relief
148 .replace(relief)
149 .as_mut()
150 .map(ReliefData::map_mut),
151 )
152 }
153
154 /// Sets the shading map, in place of any it has, and returns what the
155 /// pulls of the one it displaced did not get.
156 #[must_use]
157 pub(crate) fn set_shading(&mut self, shading: ShadingData) -> Unresolved {
158 displaced(
159 self.shading
160 .replace(shading)
161 .as_mut()
162 .map(ShadingData::map_mut),
163 )
164 }
165
166 /// Sets the emissive map, in place of any it has, and returns what the
167 /// pulls of the one it displaced did not get.
168 #[must_use]
169 pub(crate) fn set_emissive_map(&mut self, emissive: TextureData) -> Unresolved {
170 displaced(self.emissive.replace(emissive).as_mut())
171 }
172
173 /// Memory the pixels this slot samples hold.
174 pub(crate) fn bytes(&self) -> usize {
175 [
176 self.texture.as_ref(),
177 self.relief.as_ref().map(ReliefData::map),
178 self.shading.as_ref().map(ShadingData::map),
179 self.emissive.as_ref(),
180 ]
181 .into_iter()
182 .flatten()
183 .map(|texture| texture.pixels().len())
184 .sum()
185 }
186
187 /// What the pulls of the maps this slot samples did not get, taken out
188 /// of all four of them.
189 pub(crate) fn take_unresolved(&mut self) -> Unresolved {
190 let maps = [
191 self.texture.as_mut(),
192 self.relief.as_mut().map(ReliefData::map_mut),
193 self.shading.as_mut().map(ShadingData::map_mut),
194 self.emissive.as_mut(),
195 ];
196
197 maps.into_iter()
198 .flatten()
199 .fold(Unresolved::default(), |mut unresolved, map| {
200 unresolved.record(map.take_unresolved());
201 unresolved
202 })
203 }
204}
205
206/// What the pulls of a map a slot displaced did not get; nothing where it
207/// displaced none.
208fn displaced(map: Option<&mut TextureData>) -> Unresolved {
209 map.map(TextureData::take_unresolved).unwrap_or_default()
210}