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}