Theme

alex/beta-minecraft-rust

Minecraft Beta 1.7.3 Rewrite

BRANCH main
main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
Download file

root / crates / common / src / map.rs

use crate::nbt::Compound;
use crate::world::Material;

pub const SIZE: usize = 128;
pub const PALETTE: [u32; 14] = [
    0, 8368696, 16247203, 10987431, 16711680, 10526975, 10987431, 31744, 16777215, 10791096,
    12020271, 7368816, 4210943, 6837042,
];

#[derive(Clone, Debug)]
pub struct Map {
    pub center: [i32; 2],
    pub dimension: i8,
    pub scale: u8,
    pub colors: Box<[u8; SIZE * SIZE]>,
    pub icons: Vec<[i8; 3]>,
}

impl Map {
    pub fn new(center: [i32; 2], dimension: i8) -> Self {
        Self {
            center,
            dimension,
            scale: 3,
            colors: Box::new([0; SIZE * SIZE]),
            icons: Vec::new(),
        }
    }
    pub fn read(nbt: &Compound) -> Self {
        let mut map = Self::new(
            [nbt.int("xCenter"), nbt.int("zCenter")],
            nbt.byte("dimension"),
        );
        map.scale = nbt.byte("scale").clamp(0, 4) as u8;
        let width = i32::from(nbt.short("width"));
        let height = i32::from(nbt.short("height"));
        let bytes = nbt.byte_array("colors");
        if width > 0 && height > 0 {
            for y in ((height - 128) / 2).max(0)..height.min((height + 128) / 2) {
                for x in ((width - 128) / 2).max(0)..width.min((width + 128) / 2) {
                    let tx = x + (128 - width) / 2;
                    let ty = y + (128 - height) / 2;
                    if (0..128).contains(&tx)
                        && (0..128).contains(&ty)
                        && let Some(&color) = bytes.get((x + y * width) as usize)
                    {
                        map.colors[(tx + ty * 128) as usize] = color;
                    }
                }
            }
        }
        map
    }
    pub fn write(&self, nbt: &mut Compound) {
        nbt.set_int("xCenter", self.center[0]);
        nbt.set_int("zCenter", self.center[1]);
        nbt.set_byte("dimension", self.dimension);
        nbt.set_byte("scale", self.scale as i8);
        nbt.set_short("width", 128);
        nbt.set_short("height", 128);
        nbt.set_byte_array("colors", self.colors.to_vec());
    }
    pub fn receive(&mut self, data: &[u8]) -> bool {
        match data {
            [0, x, y, colors @ ..]
                if !colors.is_empty() && *x < 128 && usize::from(*y) + colors.len() <= 128 =>
            {
                for (offset, &color) in colors.iter().enumerate() {
                    self.colors[usize::from(*x) + (usize::from(*y) + offset) * 128] = color;
                }
                true
            }
            [1, icons @ ..] if icons.len().is_multiple_of(3) => {
                self.icons = icons
                    .as_chunks::<3>()
                    .0
                    .iter()
                    .map(|p| [p[0] as i8, p[1] as i8, p[2] as i8])
                    .collect();
                true
            }
            _ => false,
        }
    }
    pub fn rgba(&self) -> Vec<u8> {
        self.colors
            .iter()
            .enumerate()
            .flat_map(|(index, &color)| {
                let Some(&rgb) = PALETTE
                    .get(usize::from(color / 4))
                    .filter(|_| color / 4 != 0)
                else {
                    return [0, 0, 0, ((index + index / 128) & 1) as u8 * 8 + 16];
                };
                let shade = match color & 3 {
                    0 => 180,
                    2 => 255,
                    _ => 220,
                };
                [
                    ((rgb >> 16 & 255) * shade / 255) as u8,
                    ((rgb >> 8 & 255) * shade / 255) as u8,
                    ((rgb & 255) * shade / 255) as u8,
                    255,
                ]
            })
            .collect()
    }
}

pub fn material_color(material: Material) -> u8 {
    match material {
        Material::Grass => 1,
        Material::Sand => 2,
        Material::Cloth | Material::Sponge | Material::Web => 3,
        Material::Lava | Material::Tnt => 4,
        Material::Ice => 5,
        Material::Iron => 6,
        Material::Leaves | Material::Plants | Material::Cactus | Material::Pumpkin => 7,
        Material::Snow | Material::BuiltSnow => 8,
        Material::Clay => 9,
        Material::Ground => 10,
        Material::Rock | Material::Piston => 11,
        Material::Water => 12,
        Material::Wood => 13,
        _ => 0,
    }
}