minetest_worldmapper/
mapblock.rs1use crate::config::Config;
2use crate::terrain::TerrainCell;
3use futures::stream::{Stream, StreamExt};
4use minetestworld::MAPBLOCK_LENGTH;
5use minetestworld::{MapBlock, Position};
6use std::collections::{BinaryHeap, HashMap};
7use std::ops::Range;
8
9pub const CHUNK_SIZE: usize = MAPBLOCK_LENGTH as usize * MAPBLOCK_LENGTH as usize;
10
11#[derive(Debug)]
12pub struct Bbox {
13 pub x: Range<i16>,
14 pub z: Range<i16>,
15}
16
17impl Bbox {
18 fn from_opt_ranges(x: Option<Range<i16>>, z: Option<Range<i16>>) -> Option<Bbox> {
19 Some(Bbox { x: x?, z: z? })
20 }
21}
22
23fn update_range(range: &mut Option<Range<i16>>, new_value: i16) {
24 if let Some(range) = range {
25 if new_value < range.start {
26 range.start = new_value;
27 } else if new_value >= range.end {
28 range.end = new_value + 1;
29 }
30 } else {
31 *range = Some(new_value..new_value + 1);
32 }
33}
34
35type SortedPositions = HashMap<(i16, i16), BinaryHeap<i16>>;
39
40pub(crate) async fn analyze_positions<S, E>(mut positions: S) -> Result<(SortedPositions, Bbox), E>
44where
45 S: Stream<Item = Result<Position, E>> + Unpin,
46{
47 let mut sorted_positions = HashMap::new();
48 let (mut x_range, mut z_range) = (None, None);
49 while let Some(pos) = positions.next().await {
50 let pos = pos?;
51 update_range(&mut x_range, pos.x);
52 update_range(&mut z_range, pos.z);
53 let key = (pos.x, pos.z);
54 let y_stack = sorted_positions.entry(key).or_insert_with(BinaryHeap::new);
55 y_stack.push(pos.y);
56 }
57 Ok((
58 sorted_positions,
59 Bbox::from_opt_ranges(x_range, z_range).unwrap_or(Bbox { x: 0..0, z: 0..0 }),
60 ))
61}
62
63pub fn compute_mapblock(
64 mapblock: &MapBlock,
65 config: &Config,
66 base_height: i16,
67 acc: &mut [TerrainCell; CHUNK_SIZE],
68) {
69 if mapblock.name_id_mappings.values().eq([b"air"]) {
70 return;
71 }
72 for z in 0..MAPBLOCK_LENGTH {
73 for x in 0..MAPBLOCK_LENGTH {
74 let index = (x + MAPBLOCK_LENGTH * z) as usize;
75 if acc[index].alpha() > 230 {
76 continue;
77 }
78
79 for y in (0..MAPBLOCK_LENGTH).rev() {
80 let node = mapblock.get_node_at(Position::new(x, y, z));
81 if let Some(color) = config.get_color(&node.param0) {
82 acc[index].add_background(*color);
83 if config.hill_shading.enabled
84 && acc[index].alpha() > config.hill_shading.min_alpha
85 {
86 acc[index].set_height(base_height + y as i16);
87 }
88 if acc[index].alpha() > config.sufficient_alpha {
89 acc[index].set_height(base_height + y as i16);
90 break;
91 }
92 }
93 }
94 }
95 }
96}