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 / scene / hand.rs

use std::f32::consts::PI;

use common::math::sin;
use glam::{Mat3, Mat4, Vec3};

use super::{
    Geometry,
    models::{Model, Part},
};
use crate::{interaction::Interaction, player::LocalPlayer, world::ClientWorld};

const WATER: i32 = crate::assets::entity_texture("misc/water.png");

const SCREEN: [Vec3; 4] = [
    Vec3::new(-1.0, -1.0, -0.5),
    Vec3::new(1.0, -1.0, -0.5),
    Vec3::new(1.0, 1.0, -0.5),
    Vec3::new(-1.0, 1.0, -0.5),
];

impl Geometry {
    pub(super) fn first_person(
        &mut self,
        player: &LocalPlayer,
        interaction: &Interaction,
        world: &ClientWorld,
        partial: f32,
        brightness: f32,
    ) {
        let equipped = super::lerp(interaction.equipped[0], interaction.equipped[1], partial);
        let living = player.entity.living.as_ref();
        let swing = living.map_or(0.0, |l| super::swing(l, partial));
        let wave = sin(swing * PI);
        let root = sin(swing.sqrt() * PI);
        let twice = sin(swing.sqrt() * std::f32::consts::TAU);
        let squared = sin(swing * swing * PI);
        let mut transform = player.camera_effects(partial, player.view_bobbing);
        if let Some(stack) = interaction.rendered_item {
            transform *= Mat4::from_translation(Vec3::new(-root * 0.4, twice * 0.2, -wave * 0.2));
            if stack.item == common::item::Item::MAP {
                self.map = Some(stack.damage as i16);
                let map = world.maps.get(&(stack.damage as i16));
                let pitch = super::lerp(player.entity.prev_pitch, player.entity.pitch, partial);
                self.held_map(transform, map, equipped, [root, squared], pitch, brightness);
                return;
            }
            transform *=
                Mat4::from_translation(Vec3::new(0.56, -0.52 - (1.0 - equipped) * 0.6, -0.72));
            transform *= Mat4::from_rotation_y((45.0 - squared * 20.0).to_radians())
                * Mat4::from_rotation_z((-root * 20.0).to_radians())
                * Mat4::from_rotation_x((-root * 80.0).to_radians())
                * Mat4::from_scale(Vec3::splat(0.4));
            self.held_item(transform, stack, brightness, Some(player.entity.id));
        } else {
            transform *= Mat4::from_translation(Vec3::new(-root * 0.3, twice * 0.4, -wave * 0.4));
            transform *=
                Mat4::from_translation(Vec3::new(0.64, -0.6 - (1.0 - equipped) * 0.6, -0.72));
            transform *= Mat4::from_rotation_y((45.0 + root * 70.0).to_radians())
                * Mat4::from_rotation_z((-squared * 20.0).to_radians());
            transform *= Mat4::from_translation(Vec3::new(-1.0, 3.6, 3.5))
                * Mat4::from_rotation_z(120_f32.to_radians())
                * Mat4::from_rotation_x(200_f32.to_radians())
                * Mat4::from_rotation_y(-135_f32.to_radians())
                * Mat4::from_translation(Vec3::new(5.6, 0.0, 0.0));
            self.arm(transform, brightness);
        }
    }

    /// The bare right arm of the first-person view.
    pub(super) fn arm(&mut self, transform: Mat4, brightness: f32) {
        let mut model = Model::new(self, transform, 21, brightness);
        model.transform /= 16.0;
        model.part(Part::new(
            [-3.0, -2.0, -2.0],
            [4.0, 12.0, 4.0],
            [-5.0, 2.0, 0.0],
            [40.0, 16.0],
        ));
    }

    /// `ItemRenderer.renderOverlays`: flames while burning, the texture of a block the eyes
    /// are stuck in, and the water surface while submerged, all in front of the camera.
    pub(super) fn hazard_overlays(
        &mut self,
        player: &LocalPlayer,
        world: &ClientWorld,
        brightness: f32,
    ) {
        use common::world::shape::BlockView;
        use common::world::{Block, Material, texture};
        let entity = &player.entity;
        if entity.fire > 0 || entity.flag(common::entity::flags::ON_FIRE) {
            for side in 0..2 {
                let tile = Block::FIRE.properties().map_or(0, |p| p.texture) + side as u8 * 16;
                let (tx, ty) = (f32::from(tile & 15) * 16.0, f32::from(tile & 0xF0));
                let (u0, v0) = (tx / 256.0, ty / 256.0);
                let (u1, v1) = ((tx + 15.99) / 256.0, (ty + 15.99) / 256.0);
                let sign = (side * 2 - 1) as f32;
                let transform = Mat4::from_translation(Vec3::new(-sign * 0.24, -0.3, 0.0))
                    * Mat4::from_rotation_y((sign * 10.0).to_radians());
                let points = [[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]]
                    .map(|[x, y]| transform.transform_point3(Vec3::new(x, y, -0.5)));
                let uvs = [[u1, v1], [u0, v1], [u0, v0], [u1, v0]];
                self.quad_uv(points, uvs, 0, 0xe6ffffff);
            }
        }
        if entity.is_inside_opaque_block(world) {
            let [x, y, z] = entity.pos.to_array().map(common::math::floor_f64);
            let mut block = world.block(x, y, z);
            if !block.is_normal_cube() {
                for i in 0..8 {
                    let offset = |bit: i32, size: f32| (((i >> bit) % 2) as f32 - 0.5) * size;
                    let bx = common::math::floor_f32(x as f32 + offset(0, entity.width * 0.9));
                    let by = common::math::floor_f32(y as f32 + offset(1, entity.height * 0.2));
                    let bz = common::math::floor_f32(z as f32 + offset(2, entity.width * 0.9));
                    if world.block(bx, by, bz).is_normal_cube() {
                        block = world.block(bx, by, bz);
                    }
                }
            }
            if block.properties().is_some() {
                // The original divides the tile index by 256 without scaling it to texels
                let tile = texture::face(block, 0, 2).0;
                let edge = 0.0078125;
                let u0 = f32::from(tile % 16) / 256.0 - edge;
                let u1 = (f32::from(tile % 16) + 15.99) / 256.0 + edge;
                let v0 = f32::from(tile / 16) / 256.0 - edge;
                let v1 = (f32::from(tile / 16) + 15.99) / 256.0 + edge;
                let uvs = [[u1, v1], [u0, v1], [u0, v0], [u1, v0]];
                self.quad_uv(SCREEN, uvs, 0, 0x801a1a1a);
            }
        }
        if entity.eyes_in(world, Material::Water) {
            let (du, dv) = (-entity.yaw / 64.0, entity.pitch / 64.0);
            let uvs = [
                [4.0 + du, 4.0 + dv],
                [du, 4.0 + dv],
                [du, dv],
                [4.0 + du, dv],
            ];
            let gray = (brightness * 255.0) as u32 * 0x010101;
            self.quad_uv(SCREEN, uvs, WATER, 0x80000000 | gray);
        }
    }

    pub(super) fn item_sprite(&mut self, transform: Mat4, atlas: i32, icon: u8, brightness: f32) {
        let u = f32::from(icon & 15) / 16.0;
        let v = f32::from(icon >> 4) / 16.0;
        let tile = 15.99 / 256.0;
        let normals = Mat3::from_mat4(transform).inverse().transpose();
        let mut quad = |points: [Vec3; 4], uv, normal: Vec3| {
            let lighting = super::models::item_lighting((normals * normal).normalize_or_zero());
            let points = points.map(|p| transform.transform_point3(p));
            self.quad(points, uv, atlas, super::gray(brightness * lighting))
        };
        for (z, normal) in [(0.0, Vec3::Z), (-1.0 / 16.0, Vec3::NEG_Z)] {
            quad(
                [
                    Vec3::new(0.0, 0.0, z),
                    Vec3::new(1.0, 0.0, z),
                    Vec3::new(1.0, 1.0, z),
                    Vec3::new(0.0, 1.0, z),
                ],
                [u + tile, v + tile, -tile, -tile],
                normal,
            );
        }
        for i in 0..16 {
            let fraction = i as f32 / 16.0;
            for (edge, sign) in [(0.0, -1.0), (1.0 / 16.0, 1.0)] {
                let p = fraction + edge;
                quad(
                    [
                        Vec3::new(p, 0.0, -1.0 / 16.0),
                        Vec3::new(p, 0.0, 0.0),
                        Vec3::new(p, 1.0, 0.0),
                        Vec3::new(p, 1.0, -1.0 / 16.0),
                    ],
                    [
                        u + tile * (1.0 - fraction) - 0.5 / 256.0,
                        v + tile,
                        0.0,
                        -tile,
                    ],
                    Vec3::X * sign,
                );
                quad(
                    [
                        Vec3::new(0.0, p, 0.0),
                        Vec3::new(1.0, p, 0.0),
                        Vec3::new(1.0, p, -1.0 / 16.0),
                        Vec3::new(0.0, p, -1.0 / 16.0),
                    ],
                    [
                        u + tile,
                        v + tile * (1.0 - fraction) - 0.5 / 256.0,
                        -tile,
                        0.0,
                    ],
                    Vec3::Y * sign,
                );
            }
        }
    }
}