1pub mod cell;
2pub mod mat_cell;
3pub use mat_cell::Material;
4
5pub use self::{
7 cell::{Cell, CellSurface},
8 mat_cell::MatCell,
9};
10
11use crate::{
12 terrain::{Block, BlockKind, SpriteKind},
13 vol::{FilledVox, IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, WriteVol},
14 volumes::dyna::Dyna,
15};
16use dot_vox::DotVoxData;
17use hashbrown::HashMap;
18use vek::*;
19
20pub type TerrainSegment = Dyna<Block, ()>;
21
22impl From<Segment> for TerrainSegment {
23 fn from(value: Segment) -> Self {
24 TerrainSegment::from_fn(value.sz, (), |pos| match value.get(pos) {
25 Err(_) => Block::air(SpriteKind::Empty),
26 Ok(cell) if !cell.is_filled() => Block::air(SpriteKind::Empty),
27 Ok(cell) => {
28 let kind = match cell.get_surf().unwrap_or(CellSurface::Matte) {
29 CellSurface::Glowy => BlockKind::GlowingRock,
30 CellSurface::Fire => BlockKind::Lava,
31 CellSurface::Water => BlockKind::Water,
32 CellSurface::SwirlyCrystal => BlockKind::GlowingRock,
33 CellSurface::Matte | CellSurface::Shiny => BlockKind::Misc,
34 };
35 Block::new(kind, cell.get_color().unwrap_or_default())
36 },
37 })
38 }
39}
40
41pub type Segment = Dyna<Cell, ()>;
45
46impl Segment {
47 pub fn from_voxes(data: &[(&DotVoxData, Vec3<i32>, bool)]) -> (Self, Vec3<i32>) {
50 let mut union = DynaUnionizer::new();
51 for (datum, offset, xmirror) in data.iter() {
52 union = union.add(Segment::from_vox(datum, *xmirror, 0, None), *offset);
53 }
54 union.unify()
55 }
56
57 pub fn from_vox_model_index(
58 dot_vox_data: &DotVoxData,
59 model_index: usize,
60 custom_indices: Option<&HashMap<u8, Cell>>,
61 ) -> Self {
62 Self::from_vox(dot_vox_data, false, model_index, custom_indices)
63 }
64
65 pub fn from_vox(
66 dot_vox_data: &DotVoxData,
67 flipped: bool,
68 model_index: usize,
69 custom_indices: Option<&HashMap<u8, Cell>>,
70 ) -> Self {
71 if let Some(model) = dot_vox_data.models.get(model_index) {
72 let palette = dot_vox_data
73 .palette
74 .iter()
75 .map(|col| Rgb::new(col.r, col.g, col.b))
76 .collect::<Vec<_>>();
77
78 let mut segment = Segment::filled(
79 Vec3::new(model.size.x, model.size.y, model.size.z),
80 Cell::empty(),
81 (),
82 );
83
84 let mut indices = [Cell::empty(); 256];
86 for i in 0..=255 {
87 indices[i as usize] = Cell::from_index(i, Rgb::zero());
88 }
89 for (i, cell) in custom_indices.iter().flat_map(|x| x.iter()) {
90 indices[*i as usize] = *cell;
91 }
92
93 for voxel in &model.voxels {
94 if let Some(&color) = palette.get(voxel.i as usize) {
95 segment
96 .set(
97 Vec3::new(
98 if flipped {
99 model.size.x as u8 - 1 - voxel.x
100 } else {
101 voxel.x
102 },
103 voxel.y,
104 voxel.z,
105 )
106 .map(i32::from),
107 indices[voxel.i as usize].map_rgb(|_| color),
108 )
109 .unwrap();
110 };
111 }
112
113 segment
114 } else {
115 Segment::filled(Vec3::zero(), Cell::empty(), ())
116 }
117 }
118
119 #[must_use]
121 pub fn map(mut self, transform: impl Fn(Cell) -> Option<Cell>) -> Self {
122 for pos in self.full_pos_iter() {
123 if let Some(new) = transform(*self.get(pos).unwrap()) {
124 self.set(pos, new).unwrap();
125 }
126 }
127
128 self
129 }
130
131 #[must_use]
133 pub fn map_rgb(self, transform: impl Fn(Rgb<u8>) -> Rgb<u8>) -> Self {
134 self.map(|cell| Some(cell.map_rgb(&transform)))
135 }
136}
137
138pub struct DynaUnionizer<V: FilledVox>(Vec<(Dyna<V, ()>, Vec3<i32>)>);
141
142impl<V: FilledVox + Copy> DynaUnionizer<V> {
143 #[expect(clippy::new_without_default)]
144 pub fn new() -> Self { DynaUnionizer(Vec::new()) }
145
146 #[must_use]
147 pub fn add(mut self, dyna: Dyna<V, ()>, offset: Vec3<i32>) -> Self {
148 self.0.push((dyna, offset));
149 self
150 }
151
152 #[must_use]
153 pub fn maybe_add(self, maybe: Option<(Dyna<V, ()>, Vec3<i32>)>) -> Self {
154 match maybe {
155 Some((dyna, offset)) => self.add(dyna, offset),
156 None => self,
157 }
158 }
159
160 pub fn unify(self) -> (Dyna<V, ()>, Vec3<i32>) { self.unify_with(|v, _| v) }
161
162 pub fn unify_with(self, mut f: impl FnMut(V, V) -> V) -> (Dyna<V, ()>, Vec3<i32>) {
165 if self.0.is_empty() {
166 return (
167 Dyna::filled(Vec3::zero(), V::default_non_filled(), ()),
168 Vec3::zero(),
169 );
170 }
171
172 let mut min_point = self.0[0].1;
174 let mut max_point = self.0[0].1 + self.0[0].0.size().map(|e| e as i32);
175 for (dyna, offset) in self.0.iter().skip(1) {
176 let size = dyna.size().map(|e| e as i32);
177 min_point = min_point.map2(*offset, std::cmp::min);
178 max_point = max_point.map2(offset + size, std::cmp::max);
179 }
180 let new_size = (max_point - min_point).map(|e| e as u32);
181 let mut combined = Dyna::filled(new_size, V::default_non_filled(), ());
183 let origin = min_point.map(|e| -e);
185 for (dyna, offset) in self.0 {
186 for (pos, vox) in dyna.full_vol_iter() {
187 let cell_pos = origin + offset + pos;
188 let old_vox = *combined.get(cell_pos).unwrap();
189 let new_vox = f(*vox, old_vox);
190 combined.set(cell_pos, new_vox).unwrap();
191 }
192 }
193
194 (combined, origin)
195 }
196}
197
198pub type MatSegment = Dyna<MatCell, ()>;
199
200impl MatSegment {
201 pub fn to_segment(&self, map: impl Fn(Material) -> Rgb<u8>) -> Segment {
202 let mut vol = Dyna::filled(self.size(), Cell::empty(), ());
203 for (pos, vox) in self.full_vol_iter() {
204 let cell = match vox {
205 MatCell::Mat(mat) => Cell::filled(map(*mat), CellSurface::Matte),
206 MatCell::Normal(cell) => *cell,
207 };
208 vol.set(pos, cell).unwrap();
209 }
210 vol
211 }
212
213 #[must_use]
215 pub fn map(mut self, transform: impl Fn(MatCell) -> Option<MatCell>) -> Self {
216 for pos in self.full_pos_iter() {
217 if let Some(new) = transform(*self.get(pos).unwrap()) {
218 self.set(pos, new).unwrap();
219 }
220 }
221
222 self
223 }
224
225 #[must_use]
227 pub fn map_rgb(self, transform: impl Fn(Rgb<u8>) -> Rgb<u8>) -> Self {
228 self.map(|cell| match cell {
229 MatCell::Normal(cell) => Some(MatCell::Normal(cell.map_rgb(&transform))),
230 _ => None,
231 })
232 }
233
234 pub fn from_vox_model_index(dot_vox_data: &DotVoxData, model_index: usize) -> Self {
235 Self::from_vox(dot_vox_data, false, model_index)
236 }
237
238 pub fn from_vox(dot_vox_data: &DotVoxData, flipped: bool, model_index: usize) -> Self {
239 if let Some(model) = dot_vox_data.models.get(model_index) {
240 let palette = dot_vox_data
241 .palette
242 .iter()
243 .map(|col| Rgb::new(col.r, col.g, col.b))
244 .collect::<Vec<_>>();
245
246 let mut vol = Dyna::filled(
247 Vec3::new(model.size.x, model.size.y, model.size.z),
248 MatCell::Normal(Cell::empty()),
249 (),
250 );
251
252 for voxel in &model.voxels {
253 let block = match voxel.i {
254 0 => MatCell::Mat(Material::Skin),
255 1 => MatCell::Mat(Material::Hair),
256 2 => MatCell::Mat(Material::EyeDark),
257 3 => MatCell::Mat(Material::EyeLight),
258 4 => MatCell::Mat(Material::SkinDark),
259 5 => MatCell::Mat(Material::SkinLight),
260 7 => MatCell::Mat(Material::EyeWhite),
261 index => {
263 let color = palette
264 .get(index as usize)
265 .copied()
266 .unwrap_or_else(|| Rgb::broadcast(0));
267 MatCell::Normal(Cell::from_index(index, color))
268 },
269 };
270
271 vol.set(
272 Vec3::new(
273 if flipped {
274 model.size.x as u8 - 1 - voxel.x
275 } else {
276 voxel.x
277 },
278 voxel.y,
279 voxel.z,
280 )
281 .map(i32::from),
282 block,
283 )
284 .unwrap();
285 }
286
287 vol
288 } else {
289 Dyna::filled(Vec3::zero(), MatCell::Normal(Cell::empty()), ())
290 }
291 }
292}