minetest_worldmapper/
terrain.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use crate::color::Color;
use crate::mapblock::CHUNK_SIZE;
use minetestworld::MAPBLOCK_LENGTH;

#[derive(Default, Clone, Copy, Debug)]
pub struct TerrainCell {
    ground_color: Option<Color>,
    pub height: Option<i16>,
}

impl TerrainCell {
    pub fn alpha(&self) -> u8 {
        self.ground_color.map(|c| c.alpha()).unwrap_or(0)
    }

    pub fn add_background(&mut self, color: Color) {
        let new_color = self
            .ground_color
            .map(|c| c.with_background(&color))
            .unwrap_or(color);
        self.ground_color = Some(new_color);
    }

    /// Sets the terrain elevation, if not present already
    pub fn set_height(&mut self, height: i16) {
        if self.height.is_none() {
            self.height = Some(height)
        }
    }
}

pub struct Terrain {
    width: usize,
    height: usize,
    flat_data: Vec<TerrainCell>,
}

impl Terrain {
    pub fn new(width: usize, height: usize) -> Self {
        Terrain {
            width,
            height,
            flat_data: vec![Default::default(); width * height],
        }
    }

    pub fn width(&self) -> usize {
        self.width
    }

    pub fn height(&self) -> usize {
        self.height
    }

    /// Panics if out of bounds
    fn get_cell_mut(&mut self, x: u32, y: u32) -> &mut TerrainCell {
        self.flat_data
            .get_mut(y as usize * self.width + x as usize)
            .unwrap_or_else(|| panic!("Trying to get Terrain[{x}, {y}]"))
    }

    pub fn get_color(&self, x: u32, y: u32) -> Option<Color> {
        self.get_cell(x, y)?.ground_color
    }

    pub fn get_cell(&self, x: u32, y: u32) -> Option<&TerrainCell> {
        self.flat_data.get(y as usize * self.width + x as usize)
    }

    /// Panics if out of bounds
    pub fn insert_chunk(&mut self, offset: (u32, u32), chunk: [TerrainCell; CHUNK_SIZE]) {
        for (i, cell) in chunk.into_iter().enumerate() {
            let (mut x, mut y) = offset;
            x += i as u32 % MAPBLOCK_LENGTH as u32;
            y += MAPBLOCK_LENGTH as u32 - i as u32 / MAPBLOCK_LENGTH as u32 - 1;
            *self.get_cell_mut(x, y) = cell;
        }
    }

    pub fn height_diff_x(&self, x: u32, y: u32) -> Option<i16> {
        let a = self
            .get_cell(x.saturating_sub(1), y)
            .or_else(|| self.get_cell(x, y))?;
        let this_cell = self.get_cell(x, y)?.height?;
        let b = self.get_cell(x + 1, y).or_else(|| self.get_cell(x, y))?;
        let mut ascent = b.height? - this_cell;
        ensure_nonnegative(&mut ascent);
        let mut descent = a.height? - this_cell;
        ensure_nonnegative(&mut descent);
        Some(ascent.saturating_sub(descent))
    }

    pub fn height_diff_y(&self, x: u32, y: u32) -> Option<i16> {
        let a = self
            .get_cell(x, y.saturating_sub(1))
            .or_else(|| self.get_cell(x, y))?;
        let this_cell = self.get_cell(x, y)?.height?;
        let b = self.get_cell(x, y + 1).or_else(|| self.get_cell(x, y))?;
        let mut ascent = b.height? - this_cell;
        ensure_nonnegative(&mut ascent);
        let mut descent = a.height? - this_cell;
        ensure_nonnegative(&mut descent);
        Some(ascent.saturating_sub(descent))
    }
}

fn ensure_nonnegative(value: &mut i16) {
    if *value < 0 {
        *value = 0;
    }
}