alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH 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
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,
);
}
}
}
}