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