1use bincode::{
2 config::legacy,
3 serde::{decode_from_slice, encode_to_vec},
4};
5use common::{
6 terrain::{Block, BlockKind, chonk::Chonk},
7 vol::{BaseVol, ReadVol, RectVolSize, WriteVol},
8 volumes::vol_grid_2d::VolGrid2d,
9};
10use hashbrown::HashMap;
11use image::{ImageBuffer, ImageDecoder, ImageEncoder, Pixel};
12use num_traits::cast::FromPrimitive;
13use serde::{Deserialize, Serialize};
14use std::{
15 fmt::Debug,
16 io::{Cursor, Read, Write},
17 marker::PhantomData,
18};
19use tracing::warn;
20use vek::*;
21
22#[derive(Clone, Debug, Serialize, Deserialize)]
25pub struct CompressedData<T> {
26 pub data: Vec<u8>,
27 compressed: bool,
28 _phantom: PhantomData<T>,
29}
30
31impl<T: Serialize> CompressedData<T> {
32 pub fn compress(t: &T, level: u32) -> Self {
33 use flate2::{Compression, write::DeflateEncoder};
34 let uncompressed = encode_to_vec(t, legacy())
35 .expect("bincode serialization can only fail if a byte limit is set");
36
37 if uncompressed.len() >= 32 {
38 const EXPECT_MSG: &str =
39 "compression only fails for fallible Read/Write impls (which Vec<u8> is not)";
40
41 let buf = Vec::with_capacity(uncompressed.len() / 10);
42 let mut encoder = DeflateEncoder::new(buf, Compression::new(level));
43 encoder.write_all(&uncompressed).expect(EXPECT_MSG);
44 let compressed = encoder.finish().expect(EXPECT_MSG);
45 CompressedData {
46 data: compressed,
47 compressed: true,
48 _phantom: PhantomData,
49 }
50 } else {
51 CompressedData {
52 data: uncompressed,
53 compressed: false,
54 _phantom: PhantomData,
55 }
56 }
57 }
58}
59
60impl<T: for<'a> Deserialize<'a>> CompressedData<T> {
61 pub fn decompress(&self) -> Option<T> {
62 if self.compressed {
63 let mut uncompressed = Vec::with_capacity(self.data.len());
64 flate2::read::DeflateDecoder::new(&*self.data)
65 .read_to_end(&mut uncompressed)
66 .ok()?;
67 decode_from_slice(&uncompressed, legacy())
68 .ok()
69 .map(|(t, _)| t)
70 } else {
71 decode_from_slice(&self.data, legacy()).ok().map(|(t, _)| t)
72 }
73 }
74}
75
76pub trait PackingFormula: Copy {
78 fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32);
79 fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32);
80}
81
82#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
88pub struct WidePacking<const FLIP_X: bool>();
89
90impl<const FLIP_X: bool> PackingFormula for WidePacking<FLIP_X> {
91 #[inline(always)]
92 fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32) { (dims.x * dims.z, dims.y) }
93
94 #[inline(always)]
95 fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
96 let i0 = if FLIP_X {
97 if z.is_multiple_of(2) {
98 x
99 } else {
100 dims.x - x - 1
101 }
102 } else {
103 x
104 };
105 let i = z * dims.x + i0;
106 let j = y;
107 (i, j)
108 }
109}
110
111#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
116pub struct GridLtrPacking;
117
118impl PackingFormula for GridLtrPacking {
119 #[inline(always)]
120 fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32) {
121 let rootz = (dims.z as f64).sqrt().ceil() as u32;
122 (dims.x * rootz, dims.y * rootz)
123 }
124
125 #[inline(always)]
126 fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
127 let rootz = (dims.z as f64).sqrt().ceil() as u32;
128 let i = x + (z % rootz) * dims.x;
129 let j = y + (z / rootz) * dims.y;
130 (i, j)
131 }
132}
133
134pub trait VoxelImageEncoding {
135 type Workspace;
136 type Output;
137 fn create(width: u32, height: u32) -> Self::Workspace;
138 fn put_solid(&self, ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>);
139 fn put_sprite(
140 &self,
141 ws: &mut Self::Workspace,
142 x: u32,
143 y: u32,
144 kind: BlockKind,
145 sprite_data: [u8; 3],
146 );
147 fn finish(ws: &Self::Workspace) -> Option<Self::Output>;
148}
149
150pub trait VoxelImageDecoding: VoxelImageEncoding {
151 fn start(ws: &Self::Output) -> Option<Self::Workspace>;
152 fn get_block(ws: &Self::Workspace, x: u32, y: u32, is_border: bool) -> Block;
153}
154
155pub fn image_from_bytes<I: ImageDecoder, P: 'static + Pixel<Subpixel = u8>>(
156 decoder: I,
157) -> Option<ImageBuffer<P, Vec<u8>>> {
158 let (w, h) = decoder.dimensions();
159 let mut buf = vec![0; decoder.total_bytes() as usize];
160 decoder.read_image(&mut buf).ok()?;
161 ImageBuffer::from_raw(w, h, buf)
162}
163
164impl<VIE: VoxelImageEncoding> VoxelImageEncoding for &VIE {
165 type Output = VIE::Output;
166 type Workspace = VIE::Workspace;
167
168 fn create(width: u32, height: u32) -> Self::Workspace { VIE::create(width, height) }
169
170 fn put_solid(&self, ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>) {
171 (*self).put_solid(ws, x, y, kind, rgb)
172 }
173
174 fn put_sprite(
175 &self,
176 ws: &mut Self::Workspace,
177 x: u32,
178 y: u32,
179 kind: BlockKind,
180 sprite_data: [u8; 3],
181 ) {
182 (*self).put_sprite(ws, x, y, kind, sprite_data)
183 }
184
185 fn finish(ws: &Self::Workspace) -> Option<Self::Output> { VIE::finish(ws) }
186}
187
188impl<VIE: VoxelImageDecoding> VoxelImageDecoding for &VIE {
189 fn start(ws: &Self::Output) -> Option<Self::Workspace> { VIE::start(ws) }
190
191 fn get_block(ws: &Self::Workspace, x: u32, y: u32, is_border: bool) -> Block {
192 VIE::get_block(ws, x, y, is_border)
193 }
194}
195
196#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
197pub struct QuadPngEncoding<const RESOLUTION_DIVIDER: u32>();
198
199impl<const N: u32> VoxelImageEncoding for QuadPngEncoding<N> {
200 type Output = CompressedData<(Vec<u8>, [usize; 3], Vec<[u8; 3]>)>;
201 type Workspace = (
202 ImageBuffer<image::Luma<u8>, Vec<u8>>,
203 ImageBuffer<image::Luma<u8>, Vec<u8>>,
204 ImageBuffer<image::Luma<u8>, Vec<u8>>,
205 ImageBuffer<image::Rgb<u8>, Vec<u8>>,
206 Vec<[u8; 3]>,
207 HashMap<[u8; 3], u16>,
208 );
209
210 fn create(width: u32, height: u32) -> Self::Workspace {
211 (
212 ImageBuffer::new(width, height),
213 ImageBuffer::new(width, height),
214 ImageBuffer::new(width, height),
215 ImageBuffer::new(width / N, height / N),
216 Vec::new(),
217 HashMap::new(),
218 )
219 }
220
221 #[inline(always)]
222 fn put_solid(&self, ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>) {
223 ws.0.put_pixel(x, y, image::Luma([kind as u8]));
224 ws.3.put_pixel(x / N, y / N, image::Rgb([rgb.r, rgb.g, rgb.b]));
225 }
226
227 #[inline(always)]
228 fn put_sprite(
229 &self,
230 ws: &mut Self::Workspace,
231 x: u32,
232 y: u32,
233 kind: BlockKind,
234 sprite_data: [u8; 3],
235 ) {
236 let index = ws.5.entry(sprite_data).or_insert_with(|| {
237 let index =
238 ws.4.len()
239 .try_into()
240 .expect("Cannot have more than 2^16 unique sprites in one chunk");
241 ws.4.push(sprite_data);
242 index
243 });
244
245 let index = index.to_be_bytes();
246
247 ws.0.put_pixel(x, y, image::Luma([kind as u8]));
248 ws.1.put_pixel(x, y, image::Luma([index[0]]));
249 ws.2.put_pixel(x, y, image::Luma([index[1]]));
250 }
251
252 fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
253 let mut buf = Vec::new();
254 use image::codecs::png::{CompressionType, FilterType};
255 let mut indices = [0; 3];
256 let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| {
257 let png = image::codecs::png::PngEncoder::new_with_quality(
258 &mut buf,
259 CompressionType::Fast,
260 FilterType::Up,
261 );
262 png.write_image(
263 x.as_raw(),
264 x.width(),
265 x.height(),
266 image::ExtendedColorType::L8,
267 )
268 .ok()?;
269 indices[i] = buf.len();
270 Some(())
271 };
272 f(&ws.0, 0)?;
273 f(&ws.1, 1)?;
274 f(&ws.2, 2)?;
275
276 {
277 let png = image::codecs::png::PngEncoder::new_with_quality(
278 &mut buf,
279 CompressionType::Fast,
280 FilterType::Sub,
281 );
282 png.write_image(
283 ws.3.as_raw(),
284 ws.3.width(),
285 ws.3.height(),
286 image::ExtendedColorType::Rgb8,
287 )
288 .ok()?;
289 }
290
291 Some(CompressedData::compress(&(buf, indices, ws.4.clone()), 4))
292 }
293}
294
295const fn sin(x: f64) -> f64 {
298 use std::f64::consts::PI;
299 let mut x = (x - PI * 0.5) % (PI * 2.0);
300 x = if x < 0.0 { -x } else { x } - PI;
301 x = if x < 0.0 { -x } else { x } - PI * 0.5;
302
303 let x2 = x * x;
304 let x3 = x * x2 / 6.0;
305 let x5 = x3 * x2 / 20.0;
306 let x7 = x5 * x2 / 42.0;
307 let x9 = x7 * x2 / 72.0;
308 let x11 = x9 * x2 / 110.0;
309 x - x3 + x5 - x7 + x9 - x11
310}
311
312const fn lanczos(x: f64, a: f64) -> f64 {
314 use std::f64::consts::PI;
315 if x < f64::EPSILON {
316 1.0
317 } else if -a <= x && x <= a {
318 (a * sin(PI * x) * sin(PI * x / a)) / (PI * PI * x * x)
319 } else {
320 0.0
321 }
322}
323
324const fn lanczos_lookup_array_size(n: u32, r: u32) -> usize { (2 * n * (r + 1) - 1) as usize }
327
328const fn gen_lanczos_lookup<const N: u32, const R: u32>(
329 a: f64,
330) -> [f64; lanczos_lookup_array_size(N, R)] {
331 let quadpng_n = N as f64;
332 let sample_radius = R as f64;
333
334 let step = 1.0 / (2.0 * quadpng_n);
335 let max = (quadpng_n - 1.0) / (2.0 * quadpng_n) + sample_radius;
336 let mut array = [0.0; lanczos_lookup_array_size(N, R)];
338
339 let mut i = 0;
340 while i < array.len() {
341 array[i] = lanczos(step * i as f64 - max, a);
342 i += 1;
343 }
344 array
345}
346
347impl<const N: u32> VoxelImageDecoding for QuadPngEncoding<N> {
348 fn start(data: &Self::Output) -> Option<Self::Workspace> {
349 use image::codecs::png::PngDecoder;
350 let (quad, indices, sprite_data) = data.decompress()?;
351 let ranges: [_; 4] = [
352 0..indices[0],
353 indices[0]..indices[1],
354 indices[1]..indices[2],
355 indices[2]..quad.len(),
356 ];
357 let a = image_from_bytes(PngDecoder::new(Cursor::new(&quad[ranges[0].clone()])).ok()?)?;
358 let b = image_from_bytes(PngDecoder::new(Cursor::new(&quad[ranges[1].clone()])).ok()?)?;
359 let c = image_from_bytes(PngDecoder::new(Cursor::new(&quad[ranges[2].clone()])).ok()?)?;
360 let d = image_from_bytes(PngDecoder::new(Cursor::new(&quad[ranges[3].clone()])).ok()?)?;
361 Some((a, b, c, d, sprite_data, HashMap::new()))
362 }
363
364 fn get_block(ws: &Self::Workspace, x: u32, y: u32, is_border: bool) -> Block {
365 if let Some(kind) = BlockKind::from_u8(ws.0.get_pixel(x, y).0[0]) {
366 if kind.is_filled() {
367 let (w, h) = ws.3.dimensions();
368 let mut rgb = match 0 {
369 0 => {
371 const SAMPLE_RADIUS: i32 = 2i32; let mut rgb: Vec3<f64> = Vec3::zero();
373 let mut total = 0.0;
374 for dx in -SAMPLE_RADIUS..=SAMPLE_RADIUS {
375 for dy in -SAMPLE_RADIUS..=SAMPLE_RADIUS {
376 let (i, j) = (
377 (x.wrapping_add(dx as u32) / N),
378 (y.wrapping_add(dy as u32) / N),
379 );
380 if i < w && j < h {
381 let r = 5.0 - (dx.abs() + dy.abs()) as f64;
382 let pix = Vec3::<u8>::from(ws.3.get_pixel(i, j).0);
383 if pix != Vec3::zero() {
384 rgb += r * pix.as_();
385 total += r;
386 }
387 }
388 }
389 }
390 rgb /= total;
391 rgb
392 },
393 1 if N == 4 => {
395 const LANCZOS_A: f64 = 2.0; const SAMPLE_RADIUS: i32 = 2i32; const LANCZOS_LUT: [f64; lanczos_lookup_array_size(4, 2)] =
400 gen_lanczos_lookup::<4, 2>(LANCZOS_A);
401
402 let mut rgb: Vec3<f64> = Vec3::zero();
404 for dx in -SAMPLE_RADIUS..=SAMPLE_RADIUS {
405 for dy in -SAMPLE_RADIUS..=SAMPLE_RADIUS {
406 let (src_x, src_y) = (
408 (x.wrapping_add(dx as u32) / N),
409 (y.wrapping_add(dy as u32) / N),
410 );
411 if src_x < w && src_y < h {
412 let pix: Vec3<f64> =
413 Vec3::<u8>::from(ws.3.get_pixel(src_x, src_y).0).as_();
414 let x_rel = ((x % N) as f64 - (N - 1) as f64 / 2.0) / N as f64;
417 let y_rel = ((y % N) as f64 - (N - 1) as f64 / 2.0) / N as f64;
418 rgb += LANCZOS_LUT
421 .get((dx as f64 - x_rel).abs() as usize)
422 .unwrap_or(&0.0)
423 * LANCZOS_LUT
424 .get((dy as f64 - y_rel).abs() as usize)
425 .unwrap_or(&0.0)
426 * pix;
427 }
428 }
429 }
430 rgb
431 },
432 1 | 2 => {
434 const LANCZOS_A: f64 = 2.0; const SAMPLE_RADIUS: i32 = 2i32; let mut rgb: Vec3<f64> = Vec3::zero();
438 for dx in -SAMPLE_RADIUS..=SAMPLE_RADIUS {
439 for dy in -SAMPLE_RADIUS..=SAMPLE_RADIUS {
440 let (src_x, src_y) = (
442 (x.wrapping_add(dx as u32) / N),
443 (y.wrapping_add(dy as u32) / N),
444 );
445 if src_x < w && src_y < h {
446 let pix: Vec3<f64> =
447 Vec3::<u8>::from(ws.3.get_pixel(src_x, src_y).0).as_();
448 let x_rel = ((x % N) as f64 - (N - 1) as f64 / 2.0) / N as f64;
451 let y_rel = ((y % N) as f64 - (N - 1) as f64 / 2.0) / N as f64;
452 rgb += lanczos((dx as f64 - x_rel).abs(), LANCZOS_A)
455 * lanczos((dy as f64 - y_rel).abs(), LANCZOS_A)
456 * pix;
457 }
458 }
459 }
460 rgb
461 },
462 _ => Vec3::<u8>::from(ws.3.get_pixel(x / N, y / N).0).as_(),
464 };
465 if is_border {
466 rgb = Vec3::<u8>::from(ws.3.get_pixel(x / N, y / N).0).as_();
467 }
468 Block::new(kind, Rgb {
469 r: rgb.x as u8,
470 g: rgb.y as u8,
471 b: rgb.z as u8,
472 })
473 } else {
474 let index =
475 u16::from_be_bytes([ws.1.get_pixel(x, y).0[0], ws.2.get_pixel(x, y).0[0]]);
476 Block::from_raw(kind, ws.4[index as usize])
477 }
478 } else {
479 Block::empty()
480 }
481 }
482}
483
484#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
485pub struct TriPngEncoding<const AVERAGE_PALETTE: bool>();
486
487impl<const AVERAGE_PALETTE: bool> VoxelImageEncoding for TriPngEncoding<AVERAGE_PALETTE> {
488 type Output = CompressedData<(Vec<u8>, Vec<Rgb<u8>>, [usize; 3], Vec<[u8; 3]>)>;
489 type Workspace = (
490 ImageBuffer<image::Luma<u8>, Vec<u8>>,
491 ImageBuffer<image::Luma<u8>, Vec<u8>>,
492 ImageBuffer<image::Luma<u8>, Vec<u8>>,
493 HashMap<BlockKind, HashMap<Rgb<u8>, usize>>,
494 Vec<[u8; 3]>,
495 HashMap<[u8; 3], u16>,
496 );
497
498 fn create(width: u32, height: u32) -> Self::Workspace {
499 (
500 ImageBuffer::new(width, height),
501 ImageBuffer::new(width, height),
502 ImageBuffer::new(width, height),
503 HashMap::new(),
504 Vec::new(),
505 HashMap::new(),
506 )
507 }
508
509 fn put_solid(&self, ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>) {
510 ws.0.put_pixel(x, y, image::Luma([kind as u8]));
511 ws.1.put_pixel(x, y, image::Luma([0]));
512 ws.2.put_pixel(x, y, image::Luma([0]));
513 if AVERAGE_PALETTE {
514 *ws.3.entry(kind).or_default().entry(rgb).or_insert(0) += 1;
515 }
516 }
517
518 fn put_sprite(
519 &self,
520 ws: &mut Self::Workspace,
521 x: u32,
522 y: u32,
523 kind: BlockKind,
524 sprite_data: [u8; 3],
525 ) {
526 let index = ws.5.entry(sprite_data).or_insert_with(|| {
527 let index =
528 ws.4.len()
529 .try_into()
530 .expect("Cannot have more than 2^16 sprites in one chunk");
531 ws.4.push(sprite_data);
532 index
533 });
534 let index = index.to_be_bytes();
535
536 ws.0.put_pixel(x, y, image::Luma([kind as u8]));
537 ws.1.put_pixel(x, y, image::Luma([index[0]]));
538 ws.2.put_pixel(x, y, image::Luma([index[1]]));
539 }
540
541 fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
542 let mut buf = Vec::new();
543 use image::codecs::png::{CompressionType, FilterType};
544 let mut indices = [0; 3];
545 let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| {
546 let png = image::codecs::png::PngEncoder::new_with_quality(
547 &mut buf,
548 CompressionType::Fast,
549 FilterType::Up,
550 );
551 png.write_image(
552 x.as_raw(),
553 x.width(),
554 x.height(),
555 image::ExtendedColorType::L8,
556 )
557 .ok()?;
558 indices[i] = buf.len();
559 Some(())
560 };
561 f(&ws.0, 0)?;
562 f(&ws.1, 1)?;
563 f(&ws.2, 2)?;
564
565 let palette = if AVERAGE_PALETTE {
566 let mut palette = vec![Rgb { r: 0, g: 0, b: 0 }; 256];
567 for (block, hist) in ws.3.iter() {
568 let (mut r, mut g, mut b) = (0.0, 0.0, 0.0);
569 let mut total = 0;
570 for (color, count) in hist.iter() {
571 r += color.r as f64 * *count as f64;
572 g += color.g as f64 * *count as f64;
573 b += color.b as f64 * *count as f64;
574 total += *count;
575 }
576 r /= total as f64;
577 g /= total as f64;
578 b /= total as f64;
579 palette[*block as u8 as usize].r = r as u8;
580 palette[*block as u8 as usize].g = g as u8;
581 palette[*block as u8 as usize].b = b as u8;
582 }
583 palette
584 } else {
585 Vec::new()
586 };
587
588 Some(CompressedData::compress(
589 &(buf, palette, indices, ws.4.clone()),
590 4,
591 ))
592 }
593}
594
595impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<AVERAGE_PALETTE> {
596 fn start(data: &Self::Output) -> Option<Self::Workspace> {
597 use image::codecs::png::PngDecoder;
598 let (quad, palette, indices, sprite_data) = data.decompress()?;
599 let ranges: [_; 3] = [
600 0..indices[0],
601 indices[0]..indices[1],
602 indices[1]..indices[2],
603 ];
604 let a = image_from_bytes(PngDecoder::new(Cursor::new(&quad[ranges[0].clone()])).ok()?)?;
605 let b = image_from_bytes(PngDecoder::new(Cursor::new(&quad[ranges[1].clone()])).ok()?)?;
606 let c = image_from_bytes(PngDecoder::new(Cursor::new(&quad[ranges[2].clone()])).ok()?)?;
607 let mut d: HashMap<_, HashMap<_, _>> = HashMap::new();
608 if AVERAGE_PALETTE {
609 for i in 0..=255 {
610 if let Some(block) = BlockKind::from_u8(i) {
611 d.entry(block)
612 .or_default()
613 .entry(palette[i as usize])
614 .insert(1);
615 }
616 }
617 }
618
619 Some((a, b, c, d, sprite_data, HashMap::new()))
620 }
621
622 fn get_block(ws: &Self::Workspace, x: u32, y: u32, _: bool) -> Block {
623 if let Some(kind) = BlockKind::from_u8(ws.0.get_pixel(x, y).0[0]) {
624 if kind.is_filled() {
625 let rgb = if AVERAGE_PALETTE {
626 *ws.3
627 .get(&kind)
628 .and_then(|h| h.keys().next())
629 .unwrap_or(&Rgb::default())
630 } else {
631 use BlockKind::*;
632 match kind {
633 Air | Water | Lava => Rgb { r: 0, g: 0, b: 0 },
634 Rock => Rgb {
635 r: 93,
636 g: 110,
637 b: 145,
638 },
639 WeakRock => Rgb {
640 r: 93,
641 g: 132,
642 b: 145,
643 },
644 GlowingRock => Rgb {
645 r: 61,
646 g: 229,
647 b: 198,
648 },
649 GlowingWeakRock => Rgb {
650 r: 61,
651 g: 185,
652 b: 240,
653 },
654 Grass => Rgb {
655 r: 51,
656 g: 160,
657 b: 94,
658 },
659 Snow => Rgb {
660 r: 192,
661 g: 255,
662 b: 255,
663 },
664 ArtSnow => Rgb {
665 r: 192,
666 g: 255,
667 b: 255,
668 },
669 Ice => Rgb {
670 r: 150,
671 g: 190,
672 b: 255,
673 },
674 Earth => Rgb {
675 r: 200,
676 g: 140,
677 b: 93,
678 },
679 Sand => Rgb {
680 r: 241,
681 g: 177,
682 b: 128,
683 },
684 Wood => Rgb {
685 r: 128,
686 g: 77,
687 b: 51,
688 },
689 Leaves => Rgb {
690 r: 93,
691 g: 206,
692 b: 64,
693 },
694 ArtLeaves => Rgb {
695 r: 93,
696 g: 206,
697 b: 64,
698 },
699 GlowingMushroom => Rgb {
700 r: 50,
701 g: 250,
702 b: 250,
703 },
704 Misc => Rgb {
705 r: 255,
706 g: 0,
707 b: 255,
708 },
709 }
710 };
711 Block::new(kind, rgb)
712 } else {
713 let index =
714 u16::from_be_bytes([ws.1.get_pixel(x, y).0[0], ws.2.get_pixel(x, y).0[0]]);
715 Block::from_raw(kind, ws.4[index as usize])
716 }
717 } else {
718 Block::empty()
719 }
720 }
721}
722
723pub fn image_terrain_chonk<S: RectVolSize, M: Clone, P: PackingFormula, VIE: VoxelImageEncoding>(
724 vie: &VIE,
725 packing: P,
726 chonk: &Chonk<Block, S, M>,
727) -> Option<VIE::Output> {
728 image_terrain(
729 vie,
730 packing,
731 chonk,
732 Vec3::new(0, 0, chonk.get_min_z() as u32),
733 Vec3::new(S::RECT_SIZE.x, S::RECT_SIZE.y, chonk.get_max_z() as u32),
734 )
735}
736
737pub fn image_terrain_volgrid<
738 S: RectVolSize + Debug,
739 M: Clone + Debug,
740 P: PackingFormula,
741 VIE: VoxelImageEncoding,
742>(
743 vie: &VIE,
744 packing: P,
745 volgrid: &VolGrid2d<Chonk<Block, S, M>>,
746) -> Option<VIE::Output> {
747 let mut lo = Vec3::broadcast(i32::MAX);
748 let mut hi = Vec3::broadcast(i32::MIN);
749 for (pos, chonk) in volgrid.iter() {
750 lo.x = lo.x.min(pos.x * S::RECT_SIZE.x as i32);
751 lo.y = lo.y.min(pos.y * S::RECT_SIZE.y as i32);
752 lo.z = lo.z.min(chonk.get_min_z());
753
754 hi.x = hi.x.max((pos.x + 1) * S::RECT_SIZE.x as i32);
755 hi.y = hi.y.max((pos.y + 1) * S::RECT_SIZE.y as i32);
756 hi.z = hi.z.max(chonk.get_max_z());
757 }
758
759 image_terrain(vie, packing, volgrid, lo.as_(), hi.as_())
760}
761
762pub fn image_terrain<
763 V: BaseVol<Vox = Block> + ReadVol,
764 P: PackingFormula,
765 VIE: VoxelImageEncoding,
766>(
767 vie: &VIE,
768 packing: P,
769 vol: &V,
770 lo: Vec3<u32>,
771 hi: Vec3<u32>,
772) -> Option<VIE::Output> {
773 let dims = Vec3::new(
774 hi.x.wrapping_sub(lo.x),
775 hi.y.wrapping_sub(lo.y),
776 hi.z.wrapping_sub(lo.z),
777 );
778
779 let (width, height) = packing.dimensions(dims);
780 let mut image = VIE::create(width, height);
781 for z in 0..dims.z {
782 for y in 0..dims.y {
783 for x in 0..dims.x {
784 let (i, j) = packing.index(dims, x, y, z);
785
786 let block = *vol
787 .get(
788 Vec3::new(
789 x.wrapping_add(lo.x),
790 y.wrapping_add(lo.y),
791 z.wrapping_add(lo.z),
792 )
793 .as_(),
794 )
795 .unwrap_or(&Block::empty());
796 match (block.get_color(), block.get_sprite()) {
797 (Some(rgb), None) => {
798 VIE::put_solid(vie, &mut image, i, j, *block, rgb);
799 },
800 (None, Some(_)) => {
801 let data = block.to_u32().to_le_bytes();
802
803 VIE::put_sprite(vie, &mut image, i, j, *block, [data[1], data[2], data[3]]);
804 },
805 _ => panic!(
806 "attr being used for color vs sprite is mutually exclusive (and that's \
807 required for this translation to be lossless), but there's no way to \
808 guarantee that at the type level with Block's public API"
809 ),
810 }
811 }
812 }
813 }
814
815 VIE::finish(&image)
816}
817
818pub fn write_image_terrain<
819 V: BaseVol<Vox = Block> + WriteVol,
820 P: PackingFormula,
821 VIE: VoxelImageEncoding + VoxelImageDecoding,
822>(
823 _: VIE,
824 packing: P,
825 vol: &mut V,
826 data: &VIE::Output,
827 lo: Vec3<u32>,
828 hi: Vec3<u32>,
829) -> Option<()> {
830 let ws = VIE::start(data)?;
831 let dims = Vec3::new(
832 hi.x.wrapping_sub(lo.x),
833 hi.y.wrapping_sub(lo.y),
834 hi.z.wrapping_sub(lo.z),
835 );
836 for z in 0..dims.z {
837 for y in 0..dims.y {
838 for x in 0..dims.x {
839 let (i, j) = packing.index(dims, x, y, z);
840 let is_border = x <= 1 || x >= dims.x - 2 || y <= 1 || y >= dims.y - 2;
841 let block = VIE::get_block(&ws, i, j, is_border);
842 if let Err(e) = vol.set(lo.as_() + Vec3::new(x, y, z).as_(), block) {
843 warn!(
844 "Error placing a block into a volume at {:?}: {:?}",
845 (x, y, z),
846 e
847 );
848 }
849 }
850 }
851 }
852 Some(())
853}
854
855#[derive(Debug, Clone, Serialize, Deserialize)]
856pub struct WireChonk<VIE: VoxelImageEncoding, P: PackingFormula, M: Clone, S: RectVolSize> {
857 zmin: i32,
858 zmax: i32,
859 pub(crate) data: VIE::Output,
860 below: Block,
861 above: Block,
862 meta: M,
863 vie: VIE,
864 packing: P,
865 size: PhantomData<S>,
866}
867
868impl<VIE: VoxelImageEncoding + VoxelImageDecoding, P: PackingFormula, M: Clone, S: RectVolSize>
869 WireChonk<VIE, P, M, S>
870{
871 pub fn from_chonk(vie: VIE, packing: P, chonk: &Chonk<Block, S, M>) -> Option<Self> {
872 let data = image_terrain_chonk(&vie, packing, chonk)?;
873 Some(Self {
874 zmin: chonk.get_min_z(),
875 zmax: chonk.get_max_z(),
876 data,
877 below: *chonk
878 .get(Vec3::new(0, 0, chonk.get_min_z().saturating_sub(1)))
879 .ok()?,
880 above: *chonk.get(Vec3::new(0, 0, chonk.get_max_z() + 1)).ok()?,
881 meta: chonk.meta().clone(),
882 vie,
883 packing,
884 size: PhantomData,
885 })
886 }
887
888 pub fn to_chonk(&self) -> Option<Chonk<Block, S, M>> {
889 let mut chonk = Chonk::new(self.zmin, self.below, self.above, self.meta.clone());
890 write_image_terrain(
891 &self.vie,
892 self.packing,
893 &mut chonk,
894 &self.data,
895 Vec3::new(0, 0, self.zmin as u32),
896 Vec3::new(S::RECT_SIZE.x, S::RECT_SIZE.y, self.zmax as u32),
897 )?;
898 Some(chonk)
899 }
900}