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 weather.rs
//! Precipitation (`EntityRenderer.renderRainSnow`) and lightning (`RenderLightningBolt`).
use common::JavaRandom;
use common::math::floor_f64;
use glam::{DVec3, Vec3};
use super::{Geometry, brightness_at};
use crate::world::ClientWorld;
/// The per-column seed of `renderRainSnow`, computed in `int` like the original.
fn column_seed(x: i32, z: i32) -> i32 {
x.wrapping_mul(x)
.wrapping_mul(3121)
.wrapping_add(x.wrapping_mul(45_238_971))
.wrapping_add(z.wrapping_mul(z).wrapping_mul(418_711))
.wrapping_add(z.wrapping_mul(13_761))
}
pub struct Viewer {
/// The entity position, which picks the columns.
pub position: DVec3,
/// The interpolated eye, which vertices are relative to.
pub eye: DVec3,
}
impl Geometry {
/// Falling rain and snow in the columns around the viewer.
pub fn precipitation(
&mut self,
world: &ClientWorld,
viewer: &Viewer,
ticks: i32,
partial: f32,
fancy: bool,
sky_darkness: u32,
) {
let start = self.vertices.len() as u32;
self.weather = start..start;
let strength = world.weather.rain_strength(partial);
if strength <= 0.0 {
return;
}
let [px, py, pz] = viewer.position.to_array().map(floor_f64);
let radius = if fancy { 10 } else { 5 };
let size = (radius * 2 + 1) as usize;
let biomes = world
.climate()
.sample(px - radius, pz - radius, size, size)
.biomes;
let ticks_f = ticks as f32 + partial;
let slide = ((ticks & 511) as f32 + partial) / 512.0;
let base_y = floor_f64(viewer.eye.y);
let fade = |x: i32, z: i32| {
let dx = f64::from(x as f32 + 0.5) - viewer.position.x;
let dz = f64::from(z as f32 + 0.5) - viewer.position.z;
(dx * dx + dz * dz).sqrt() as f32 / radius as f32
};
let snow = crate::assets::entity_texture("environment/snow.png");
let rain = crate::assets::entity_texture("environment/rain.png");
for pass in [true, false] {
let mut index = 0;
for x in px - radius..=px + radius {
for z in pz - radius..=pz + radius {
let biome = biomes[index];
index += 1;
if (pass && !biome.snows()) || (!pass && !biome.rains()) {
continue;
}
let top = world.top_solid_or_liquid(x, z);
let (low, high) = ((py - radius).max(top), (py + radius).max(top));
if low == high {
continue;
}
let mut random = JavaRandom::new(i64::from(column_seed(x, z)));
let distance = fade(x, z);
let (cx, cz) = (f64::from(x), f64::from(z));
let (v, u, light, alpha, texture) = if pass {
let u =
random.next_float() + ticks_f * 0.01 * random.next_gaussian() as f32;
let jitter =
random.next_float() + ticks_f * random.next_gaussian() as f32 * 0.001;
let lit = f64::from(top.max(0).max(base_y));
let light = brightness_at(world, DVec3::new(cx, lit, cz), sky_darkness);
let alpha = ((1.0 - distance * distance) * 0.3 + 0.5) * strength;
let v = |y: i32| y as f32 / 4.0 + slide + jitter;
([v(low), v(high)], u, light, alpha, snow)
} else {
let scroll =
((ticks.wrapping_add(column_seed(x, z)) & 31) as f32 + partial) / 32.0
* (3.0 + random.next_float());
let light = brightness_at(world, DVec3::new(cx, 127.0, cz), sky_darkness)
* 0.85
+ 0.15;
let alpha = ((1.0 - distance * distance) * 0.5 + 0.5) * strength;
let v = |y: i32| y as f32 / 4.0 + scroll;
([v(low), v(high)], 0.0, light, alpha, rain)
};
let uv = [u, v[0], 1.0, v[1] - v[0]];
let color = ((alpha.clamp(0.0, 1.0) * 255.0) as u32 * 0x0100_0000)
| ((light * 255.0) as u32 * 0x010101);
let at = |(dx, dz): (f64, f64), y: i32| {
(DVec3::new(cx + dx, f64::from(y), cz + dz) - viewer.eye).as_vec3()
};
for (a, b) in [((0.0, 0.5), (1.0, 0.5)), ((0.5, 0.0), (0.5, 1.0))] {
let points = [at(a, low), at(b, low), at(b, high), at(a, high)];
self.quad(points, uv, texture, color);
}
}
}
}
self.weather.end = self.vertices.len() as u32;
}
/// `RenderLightningBolt`: four widening layers of jagged glowing strips.
pub fn bolts(&mut self, world: &ClientWorld, eye: DVec3) {
let start = self.vertices.len() as u32;
self.bolts = start..start;
for bolt in world.bolts.iter().filter(|bolt| bolt.visible()) {
let origin = (bolt.position - eye).as_vec3();
let mut offsets = [[0.0_f32; 2]; 8];
let (mut x, mut z) = (0.0_f32, 0.0_f32);
let mut random = JavaRandom::new(bolt.vertex);
for step in (0..8).rev() {
offsets[step] = [x, z];
x += (random.next_int(11) - 5) as f32;
z += (random.next_int(11) - 5) as f32;
}
for layer in 0..4 {
let mut random = JavaRandom::new(bolt.vertex);
for branch in 0..3 {
let top: i32 = if branch > 0 { 7 - branch } else { 7 };
let bottom = if branch > 0 { top - 2 } else { 0 };
let mut dx = offsets[top as usize][0] - x;
let mut dz = offsets[top as usize][1] - z;
for segment in (bottom..=top).rev() {
let (upper_x, upper_z) = (dx, dz);
let spread = if branch == 0 { 11 } else { 31 };
dx += (random.next_int(spread) - spread / 2) as f32;
dz += (random.next_int(spread) - spread / 2) as f32;
let width = |extra: i32| {
let mut width = 0.1 + layer as f32 * 0.2;
if branch == 0 {
width *= (segment + extra) as f32 * 0.1 + 1.0;
}
width
};
let (upper, lower) = (width(0), width(-1));
let corner = |index: usize, width: f32| {
let shift =
|far: bool| 0.5 - width + if far { width * 2.0 } else { 0.0 };
(shift(matches!(index, 1 | 2)), shift(matches!(index, 2 | 3)))
};
for side in 0..4 {
let point = |index: usize, width: f32, offset: (f32, f32), y: i32| {
let (x, z) = corner(index % 4, width);
origin + Vec3::new(x + offset.0, (y * 16) as f32, z + offset.1)
};
let (a, b) = (side, side + 1);
self.quad(
[
point(a, lower, (dx, dz), segment),
point(a, upper, (upper_x, upper_z), segment + 1),
point(b, upper, (upper_x, upper_z), segment + 1),
point(b, lower, (dx, dz), segment),
],
[0.0; 4],
-1,
0x4c7f_7272,
);
}
}
}
}
}
self.bolts.end = self.vertices.len() as u32;
}
}
impl Geometry {
/// `RenderGlobal.renderClouds` and `renderCloudsFancy`: the drifting cloud layer at height
/// 108, flat or as boxes four blocks thick.
pub fn clouds(&mut self, camera: DVec3, drift: i32, partial: f32, color: Vec3, fancy: bool) {
let start = self.vertices.len() as u32;
let texture = crate::assets::entity_texture("environment/clouds.png");
let shade = |factor: f32| {
let [r, g, b] = (color * factor)
.to_array()
.map(|c| (c.clamp(0.0, 1.0) * 255.0) as u32);
204 << 24 | r | g << 8 | b << 16
};
let height = 108.0 - camera.y as f32 + 0.33;
let drift = f64::from((drift as f32 + partial) * 0.03);
if !fancy {
const STEP: i32 = 32;
const UV: f32 = 0.5 / 1024.0;
let mut x = camera.x + drift;
let mut z = camera.z;
x -= f64::from(floor_f64(x / 2048.0) * 2048);
z -= f64::from(floor_f64(z / 2048.0) * 2048);
let (u, v) = ((x * f64::from(UV)) as f32, (z * f64::from(UV)) as f32);
let color = shade(1.0);
for gx in (-STEP * 8..STEP * 8).step_by(STEP as usize) {
for gz in (-STEP * 8..STEP * 8).step_by(STEP as usize) {
let (x0, z0) = (gx as f32, gz as f32);
let (x1, z1) = (x0 + STEP as f32, z0 + STEP as f32);
self.quad(
[
Vec3::new(x0, height, z0),
Vec3::new(x1, height, z0),
Vec3::new(x1, height, z1),
Vec3::new(x0, height, z1),
],
[x0 * UV + u, z0 * UV + v, STEP as f32 * UV, STEP as f32 * UV],
texture,
color,
);
}
}
self.clouds = start..self.vertices.len() as u32;
self.fancy_clouds = false;
return;
}
const SCALE: f32 = 12.0;
const THICKNESS: f32 = 4.0;
const CELLS: i32 = 8;
const UV: f32 = 0.003_906_25;
const EDGE: f32 = 1.0 / 1024.0;
let mut x = (camera.x + drift) / f64::from(SCALE);
let mut z = camera.z / f64::from(SCALE) + f64::from(0.33_f32);
x -= f64::from(floor_f64(x / 2048.0) * 2048);
z -= f64::from(floor_f64(z / 2048.0) * 2048);
let (u0, v0) = (floor_f64(x) as f32 * UV, floor_f64(z) as f32 * UV);
let (fx, fz) = (
(x - f64::from(floor_f64(x))) as f32,
(z - f64::from(floor_f64(z))) as f32,
);
let point = |x: f32, y: f32, z: f32| Vec3::new(x * SCALE, y, z * SCALE);
let span = CELLS as f32;
for gx in -2..=3 {
for gz in -2..=3 {
let (cx, cz) = ((gx * CELLS) as f32, (gz * CELLS) as f32);
let (x0, z0) = (cx - fx, cz - fz);
let uv = |du: f32, dv: f32| [(cx + du) * UV + u0, (cz + dv) * UV + v0];
let [u, v] = uv(0.0, 0.0);
let flat = |geometry: &mut Self, y: f32, color: u32| {
let points = [
point(x0, y, z0),
point(x0 + span, y, z0),
point(x0 + span, y, z0 + span),
point(x0, y, z0 + span),
];
geometry.quad(points, [u, v, span * UV, span * UV], texture, color);
};
if height > -THICKNESS - 1.0 {
flat(self, height, shade(0.7));
}
if height <= THICKNESS + 1.0 {
flat(self, height + THICKNESS - EDGE, shade(1.0));
}
let sides = shade(0.9);
for cell in 0..CELLS {
let column = cell as f32;
let [side_u, _] = uv(column + 0.5, 0.0);
for (show, offset) in [(gx > -1, 0.0), (gx <= 1, 1.0 - EDGE)] {
if show {
let px = x0 + column + offset;
let points = [
point(px, height, z0),
point(px, height + THICKNESS, z0),
point(px, height + THICKNESS, z0 + span),
point(px, height, z0 + span),
];
self.quad(points, [side_u, v, 0.0, span * UV], texture, sides);
}
}
}
let ends = shade(0.8);
for cell in 0..CELLS {
let row = cell as f32;
let [_, end_v] = uv(0.0, row + 0.5);
for (show, offset) in [(gz > -1, 0.0), (gz <= 1, 1.0 - EDGE)] {
if show {
let pz = z0 + row + offset;
let points = [
point(x0, height, pz),
point(x0 + span, height, pz),
point(x0 + span, height + THICKNESS, pz),
point(x0, height + THICKNESS, pz),
];
self.quad(points, [u, end_v, span * UV, 0.0], texture, ends);
}
}
}
}
}
self.clouds = start..self.vertices.len() as u32;
self.fancy_clouds = true;
}
}