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 / effects.rs

//! `Render.doRenderShadowAndFire`: blob shadows under entities and flames on burning ones.

use common::entity::mob::MobKind;
use common::math::floor_f64;
use common::world::Block;
use glam::{DVec3, Mat3, Vec3};

use super::Geometry;
use crate::entities::Kind;
use crate::world::ClientWorld;

const SHADOW: i32 = crate::assets::entity_texture("misc/shadow.png");
/// Scene texture flag: sample smoothly and clamped, like `%clamp%` textures.
const CLAMP: i32 = 1 << 16;

/// The width, height, `posY - boundingBox.minY` and shadow size of an entity kind.
pub fn dimensions(kind: Kind) -> (f32, f32, f32, f32) {
    match kind {
        Kind::Painting(_) | Kind::Arrow => (0.5, 0.5, 0.0, 0.0),
        Kind::Player => (0.6, 1.8, 0.0, 0.5),
        Kind::Mob(mob) => {
            let [width, height] = mob.size();
            let shadow = match mob {
                MobKind::Pig | MobKind::Cow | MobKind::Sheep | MobKind::Squid => 0.7,
                MobKind::Slime => 0.25,
                MobKind::Giant => 3.0,
                MobKind::Chicken => 0.3,
                MobKind::Spider => 1.0,
                _ => 0.5,
            };
            (width, height, 0.0, shadow)
        }
        Kind::Item(_) => (0.25, 0.25, 0.125, 0.15),
        Kind::Falling(_) | Kind::Tnt => (0.98, 0.98, 0.49, 0.5),
        Kind::Fireball => (1.0, 1.0, 0.0, 0.0),
        Kind::Vehicle(kind) => {
            let [width, height] = kind.size();
            (width, height, kind.y_offset(), 0.5)
        }
        Kind::Hook(_) | Kind::Thrown(_) => (0.25, 0.25, 0.0, 0.0),
    }
}

impl Geometry {
    /// `renderShadow`: a blob on the tops of the solid blocks below the entity, fading with
    /// height and distance.
    pub(super) fn shadow(
        &mut self,
        world: &ClientWorld,
        position: DVec3,
        eye: DVec3,
        (height, size, opaque): (f32, f32, f64),
        sky_darkness: u32,
    ) {
        let opacity = (1.0 - position.distance_squared(eye) / 256.0) * opaque;
        if opacity <= 0.0 || size <= 0.0 {
            return;
        }
        let size = f64::from(size);
        let center = position + DVec3::Y * f64::from(height / 2.0);
        for x in floor_f64(center.x - size)..=floor_f64(center.x + size) {
            for y in floor_f64(center.y - size)..=floor_f64(center.y) {
                for z in floor_f64(center.z - size)..=floor_f64(center.z + size) {
                    use common::world::shape::BlockView;
                    let below = world.block(x, y - 1, z);
                    let point = DVec3::new(f64::from(x), f64::from(y), f64::from(z)) + 0.5;
                    let light = world.light_level(point.x, point.y, point.z, sky_darkness);
                    if below.is_air() || light <= 3 || !below.properties().is_some_and(|p| p.normal)
                    {
                        continue;
                    }
                    let brightness = f64::from(world.brightness[usize::from(light)]);
                    let alpha = (opacity - (center.y - f64::from(y)) / 2.0) * 0.5 * brightness;
                    if alpha < 0.0 {
                        continue;
                    }
                    let (x0, x1) = (f64::from(x), f64::from(x + 1));
                    let (z0, z1) = (f64::from(z), f64::from(z + 1));
                    let top = f64::from(y) + 1.0 / 64.0;
                    let corners = [(x0, z0), (x0, z1), (x1, z1), (x1, z0)];
                    let points = corners.map(|(wx, wz)| (DVec3::new(wx, top, wz) - eye).as_vec3());
                    let uv = corners.map(|(wx, wz)| {
                        [center.x - wx, center.z - wz].map(|d| (d / 2.0 / size + 0.5) as f32)
                    });
                    let color = ((alpha.min(1.0) * 255.0) as u32) << 24 | 0xffffff;
                    self.quad_uv(points, uv, SHADOW | CLAMP, color);
                }
            }
        }
    }

    /// `renderEntityOnFire`: layers of fire facing the camera, narrowing upwards.
    pub(super) fn entity_fire(
        &mut self,
        relative: Vec3,
        (width, height, offset): (f32, f32, f32),
        view_yaw: f32,
    ) {
        let tile = Block::FIRE.properties().map_or(0, |p| p.texture);
        let (tx, ty) = (f32::from(tile & 15) * 16.0, f32::from(tile & 0xF0));
        let scale = width * 1.4;
        let mut remaining = height / scale;
        let rotation = Mat3::from_rotation_y((-view_yaw).to_radians());
        let shift = Vec3::new(0.0, 0.0, -0.3 + (remaining as i32) as f32 * 0.02);
        let (mut half, mut bottom, mut depth) = (0.5, offset, 0.0);
        let mut layer = 0;
        while remaining > 0.0 {
            let v0 = if layer % 2 == 0 { ty } else { ty + 16.0 };
            let (v0, v1) = (v0 / 256.0, (v0 + 15.99) / 256.0);
            let (mut u0, mut u1) = (tx / 256.0, (tx + 15.99) / 256.0);
            if layer / 2 % 2 == 0 {
                (u0, u1) = (u1, u0);
            }
            let place =
                |x: f32, y: f32| relative + rotation * ((Vec3::new(x, y, depth) + shift) * scale);
            let points = [
                place(half, -bottom),
                place(-half, -bottom),
                place(-half, 1.4 - bottom),
                place(half, 1.4 - bottom),
            ];
            self.quad_uv(
                points,
                [[u1, v1], [u0, v1], [u0, v0], [u1, v0]],
                0,
                0xffffffff,
            );
            remaining -= 0.45;
            bottom -= 0.45;
            half *= 0.9;
            depth += 0.03;
            layer += 1;
        }
    }
}