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 / client / src / render / item.rs

use common::world::{Block, Shape, texture};
use glam::{BVec3, Vec3};

pub struct Face {
    pub points: [Vec3; 4],
    pub uv: [[f32; 2]; 4],
    pub shade: f32,
}

pub fn in_3d(block: Block) -> bool {
    block.properties().is_some_and(|p| {
        matches!(
            p.shape,
            Shape::Cube | Shape::Stairs | Shape::Fence | Shape::Cactus | Shape::PistonBase
        )
    })
}

pub fn tint(block: Block, metadata: u8) -> u32 {
    match (block, metadata & 3) {
        (Block::LEAVES, 1 | 3) => 6396257,
        (Block::LEAVES, 2) => 8431445,
        (Block::LEAVES, _) => 4764952,
        _ => 0xffffff,
    }
}

pub fn color(tint: u32, brightness: f32) -> u32 {
    let channel =
        |shift: u32| (((tint >> shift) & 255) as f32 * brightness).clamp(0.0, 255.0) as u32;
    0xff000000 | channel(16) | (channel(8) << 8) | (channel(0) << 16)
}

pub fn faces(block: Block, metadata: u8) -> Vec<Face> {
    let mut faces = Vec::with_capacity(24);
    let mut cuboid = |min: [f32; 3], max: [f32; 3]| {
        let (min, max) = (Vec3::from_array(min), Vec3::from_array(max));
        // Per side, the corners whose bits 4, 2 and 1 pick the maximum x, y and z
        for (side, corners) in [
            [1, 0, 4, 5],
            [2, 3, 7, 6],
            [6, 4, 0, 2],
            [3, 1, 5, 7],
            [2, 0, 1, 3],
            [7, 5, 4, 6],
        ]
        .into_iter()
        .enumerate()
        {
            let mut points = corners.map(|bits: u8| {
                Vec3::select(
                    BVec3::new(bits & 4 != 0, bits & 2 != 0, bits & 1 != 0),
                    max,
                    min,
                )
            });
            let tile = texture::inventory_face(block, metadata, side);
            let origin = [f32::from(tile & 15) / 16.0, f32::from(tile >> 4) / 16.0];
            let uv = points.map(|p| {
                let (u, v) = match side {
                    0 | 1 => (p.x, p.z),
                    2 => (1.0 - p.x, 1.0 - p.y),
                    3 => (p.x, 1.0 - p.y),
                    4 => (p.z, 1.0 - p.y),
                    _ => (1.0 - p.z, 1.0 - p.y),
                };
                [
                    origin[0] + u.clamp(0.0, 0.999375) / 16.0,
                    origin[1] + v.clamp(0.0, 0.999375) / 16.0,
                ]
            });
            if block == Block::CACTUS && side >= 2 {
                let axis = if side < 4 { 2 } else { 0 };
                for p in &mut points {
                    p[axis] += if side % 2 == 0 { 0.0625 } else { -0.0625 };
                }
            }
            faces.push(Face {
                points: points.map(|p| p - Vec3::splat(0.5)),
                uv,
                shade: [0.5, 1.0, 0.8, 0.8, 0.6, 0.6][side],
            });
        }
    };
    match block.properties().map(|p| p.shape) {
        Some(Shape::Stairs) => {
            cuboid([0.0, 0.0, 0.0], [1.0, 1.0, 0.5]);
            cuboid([0.0, 0.0, 0.5], [1.0, 0.5, 1.0]);
        }
        Some(Shape::Fence) => {
            cuboid([0.375, 0.0, 0.0], [0.625, 1.0, 0.25]);
            cuboid([0.375, 0.0, 0.75], [0.625, 1.0, 1.0]);
            cuboid([0.4375, 0.8125, -0.125], [0.5625, 0.9375, 1.125]);
            cuboid([0.4375, 0.3125, -0.125], [0.5625, 0.4375, 1.125]);
        }
        _ if block == Block::SLAB => cuboid([0.0; 3], [1.0, 0.5, 1.0]),
        _ if block == Block::FARMLAND => cuboid([0.0; 3], [1.0, 0.9375, 1.0]),
        _ if block == Block::TRAPDOOR => cuboid([0.0, 0.40625, 0.0], [1.0, 0.59375, 1.0]),
        _ if block == Block::SNOW_LAYER => cuboid([0.0; 3], [1.0, 0.125, 1.0]),
        _ if block == Block::CAKE => cuboid([0.0625, 0.0, 0.0625], [0.9375, 0.5, 0.9375]),
        _ if matches!(
            block,
            Block::STONE_PRESSURE_PLATE | Block::WOODEN_PRESSURE_PLATE
        ) =>
        {
            cuboid([0.0, 0.375, 0.0], [1.0, 0.625, 1.0])
        }
        _ if block == Block::BUTTON => cuboid([0.3125, 0.375, 0.375], [0.6875, 0.625, 0.625]),
        _ => cuboid([0.0; 3], [1.0; 3]),
    }
    faces
}

/// `Item.getIconFromDamage`: the item's icon, which for dyes depends on the color.
pub fn icon(stack: common::item::ItemStack) -> u8 {
    let icon = stack.item.properties().map_or(0, |p| p.icon);
    if stack.item == common::item::Item::DYE {
        icon.wrapping_add((stack.damage.rem_euclid(8) * 16 + stack.damage.div_euclid(8)) as u8)
    } else {
        icon
    }
}