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