Skip to main content

mirage_engine/mesh/
slot.rs

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