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 common src noise.rs
//! Ports of `NoiseGeneratorPerlin`, `NoiseGeneratorOctaves`, `NoiseGenerator2` and
//! `NoiseGeneratorOctaves2`, including their quirks.
use crate::JavaRandom;
fn shuffled_permutations(random: &mut JavaRandom) -> [u8; 512] {
let mut permutations = std::array::from_fn(|i| i as u8);
for i in 0..256 {
let j = (random.next_int(256 - i as i32) + i as i32) as usize;
permutations.swap(i, j);
permutations[i + 256] = permutations[i];
}
permutations
}
fn fade(t: f64) -> f64 {
t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
}
fn lerp(t: f64, a: f64, b: f64) -> f64 {
a + t * (b - a)
}
fn grad(hash: u8, x: f64, y: f64, z: f64) -> f64 {
let hash = hash & 15;
let u = if hash < 8 { x } else { y };
let v = match hash {
0..4 => y,
12 | 14 => x,
_ => z,
};
(if hash & 1 == 0 { u } else { -u }) + (if hash & 2 == 0 { v } else { -v })
}
fn grad_2d(hash: u8, x: f64, z: f64) -> f64 {
let hash = hash & 15;
let u = f64::from(1 - i32::from((hash & 8) >> 3)) * x;
let v = match hash {
0..4 => 0.0,
12 | 14 => x,
_ => z,
};
(if hash & 1 == 0 { u } else { -u }) + (if hash & 2 == 0 { v } else { -v })
}
/// Splits a coordinate into its 256-periodic lattice cell and the fractional part.
fn lattice(coordinate: f64) -> (usize, f64) {
let mut cell = coordinate as i32;
if coordinate < f64::from(cell) {
cell = cell.wrapping_sub(1);
}
((cell & 255) as usize, coordinate - f64::from(cell))
}
pub struct Perlin {
permutations: [u8; 512],
offset: [f64; 3],
}
impl Perlin {
pub fn new(random: &mut JavaRandom) -> Self {
let offset = std::array::from_fn(|_| random.next_double() * 256.0);
Self {
permutations: shuffled_permutations(random),
offset,
}
}
fn p(&self, index: usize) -> usize {
usize::from(self.permutations[index])
}
pub fn sample(&self, x: f64, y: f64, z: f64) -> f64 {
let (xi, x) = lattice(x + self.offset[0]);
let (yi, y) = lattice(y + self.offset[1]);
let (zi, z) = lattice(z + self.offset[2]);
let (v, w) = (fade(y), fade(z));
let c = self.corners([xi, yi, zi], [x, y, z], fade(x));
lerp(w, lerp(v, c[0], c[1]), lerp(v, c[2], c[3]))
}
/// The four edges along y of the lattice cube, interpolated along x by `u`.
fn corners(&self, [xi, yi, zi]: [usize; 3], [x, y, z]: [f64; 3], u: f64) -> [f64; 4] {
let a = self.p(xi) + yi;
let aa = self.p(a) + zi;
let ab = self.p(a + 1) + zi;
let b = self.p(xi + 1) + yi;
let ba = self.p(b) + zi;
let bb = self.p(b + 1) + zi;
let g = |i: usize, dx: f64, dy: f64, dz: f64| grad(self.permutations[i], dx, dy, dz);
[
lerp(u, g(aa, x, y, z), g(ba, x - 1.0, y, z)),
lerp(u, g(ab, x, y - 1.0, z), g(bb, x - 1.0, y - 1.0, z)),
lerp(u, g(aa + 1, x, y, z - 1.0), g(ba + 1, x - 1.0, y, z - 1.0)),
lerp(
u,
g(ab + 1, x, y - 1.0, z - 1.0),
g(bb + 1, x - 1.0, y - 1.0, z - 1.0),
),
]
}
/// Adds `noise / amplitude` for a `width × height × depth` grid, laid out x-major, then z, then y.
fn add_to(
&self,
out: &mut [f64],
origin: [f64; 3],
size: [usize; 3],
scale: [f64; 3],
amplitude: f64,
) {
let [width, height, depth] = size;
let inverse_amplitude = 1.0 / amplitude;
let mut index = 0;
if height == 1 {
for ix in 0..width {
let (xi, x) = lattice((origin[0] + ix as f64) * scale[0] + self.offset[0]);
let u = fade(x);
for iz in 0..depth {
let (zi, z) = lattice((origin[2] + iz as f64) * scale[2] + self.offset[2]);
let w = fade(z);
let a = self.p(self.p(xi)) + zi;
let b = self.p(self.p(xi + 1)) + zi;
let near = lerp(
u,
grad_2d(self.permutations[a], x, z),
grad(self.permutations[b], x - 1.0, 0.0, z),
);
let far = lerp(
u,
grad(self.permutations[a + 1], x, 0.0, z - 1.0),
grad(self.permutations[b + 1], x - 1.0, 0.0, z - 1.0),
);
out[index] += lerp(w, near, far) * inverse_amplitude;
index += 1;
}
}
return;
}
let mut cached_yi = usize::MAX;
let mut corners = [0.0; 4];
for ix in 0..width {
let (xi, x) = lattice((origin[0] + ix as f64) * scale[0] + self.offset[0]);
let u = fade(x);
for iz in 0..depth {
let (zi, z) = lattice((origin[2] + iz as f64) * scale[2] + self.offset[2]);
let w = fade(z);
for iy in 0..height {
let (yi, y) = lattice((origin[1] + iy as f64) * scale[1] + self.offset[1]);
let v = fade(y);
// The original only refreshes gradients when the y cell changes,
// reusing the stale y fraction otherwise.
if iy == 0 || yi != cached_yi {
cached_yi = yi;
corners = self.corners([xi, yi, zi], [x, y, z], u);
}
let near = lerp(v, corners[0], corners[1]);
let far = lerp(v, corners[2], corners[3]);
out[index] += lerp(w, near, far) * inverse_amplitude;
index += 1;
}
}
}
}
}
pub struct PerlinOctaves {
octaves: Box<[Perlin]>,
}
impl PerlinOctaves {
pub fn new(random: &mut JavaRandom, count: usize) -> Self {
Self {
octaves: (0..count).map(|_| Perlin::new(random)).collect(),
}
}
pub fn sample_2d(&self, x: f64, z: f64) -> f64 {
let mut total = 0.0;
let mut frequency = 1.0;
for octave in &self.octaves {
total += octave.sample(x * frequency, z * frequency, 0.0) / frequency;
frequency /= 2.0;
}
total
}
/// Fills `out` with a grid laid out x-major, then z, then y. A height of 1 uses the 2D path.
pub fn fill(&self, out: &mut [f64], origin: [f64; 3], size: [usize; 3], scale: [f64; 3]) {
let len = size.iter().product();
out[..len].fill(0.0);
let mut amplitude = 1.0;
for octave in &self.octaves {
let scale = scale.map(|s| s * amplitude);
octave.add_to(&mut out[..len], origin, size, scale, amplitude);
amplitude /= 2.0;
}
}
/// The `(x, z)` overload of `generateNoiseOctaves`.
pub fn fill_2d(&self, out: &mut [f64], x: i32, z: i32, size: [usize; 2], scale: [f64; 2]) {
self.fill(
out,
[f64::from(x), 10.0, f64::from(z)],
[size[0], 1, size[1]],
[scale[0], 1.0, scale[1]],
);
}
}
const GRADIENTS_2D: [[f64; 2]; 12] = [
[1.0, 1.0],
[-1.0, 1.0],
[1.0, -1.0],
[-1.0, -1.0],
[1.0, 0.0],
[-1.0, 0.0],
[1.0, 0.0],
[-1.0, 0.0],
[0.0, 1.0],
[0.0, -1.0],
[0.0, 1.0],
[0.0, -1.0],
];
pub struct Simplex {
permutations: [u8; 512],
offset: [f64; 2],
}
impl Simplex {
const SKEW: f64 = 0.366_025_403_784_438_6;
const UNSKEW: f64 = 0.211_324_865_405_187_13;
pub fn new(random: &mut JavaRandom) -> Self {
let [x, z, _unused] = std::array::from_fn(|_| random.next_double() * 256.0);
Self {
permutations: shuffled_permutations(random),
offset: [x, z],
}
}
fn p(&self, index: usize) -> usize {
usize::from(self.permutations[index])
}
fn corner(gradient: usize, x: f64, z: f64) -> f64 {
let t = 0.5 - x * x - z * z;
if t < 0.0 {
return 0.0;
}
let t = t * t;
let [gx, gz] = GRADIENTS_2D[gradient];
t * t * (gx * x + gz * z)
}
fn floor(value: f64) -> i32 {
if value > 0.0 {
value as i32
} else {
value as i32 - 1
}
}
fn add_to(
&self,
out: &mut [f64],
origin: [f64; 2],
size: [usize; 2],
scale: [f64; 2],
amplitude: f64,
) {
let mut index = 0;
for ix in 0..size[0] {
let x = (origin[0] + ix as f64) * scale[0] + self.offset[0];
for iz in 0..size[1] {
let z = (origin[1] + iz as f64) * scale[1] + self.offset[1];
let skew = (x + z) * Self::SKEW;
let i = Self::floor(x + skew);
let j = Self::floor(z + skew);
let unskew = f64::from(i + j) * Self::UNSKEW;
let x0 = x - (f64::from(i) - unskew);
let z0 = z - (f64::from(j) - unskew);
let (i1, j1) = if x0 > z0 { (1, 0) } else { (0, 1) };
let x1 = x0 - i1 as f64 + Self::UNSKEW;
let z1 = z0 - j1 as f64 + Self::UNSKEW;
let x2 = x0 - 1.0 + 2.0 * Self::UNSKEW;
let z2 = z0 - 1.0 + 2.0 * Self::UNSKEW;
let ii = (i & 255) as usize;
let jj = (j & 255) as usize;
let g0 = self.p(ii + self.p(jj)) % 12;
let g1 = self.p(ii + i1 + self.p(jj + j1)) % 12;
let g2 = self.p(ii + 1 + self.p(jj + 1)) % 12;
let n =
Self::corner(g0, x0, z0) + Self::corner(g1, x1, z1) + Self::corner(g2, x2, z2);
out[index] += 70.0 * n * amplitude;
index += 1;
}
}
}
}
pub struct SimplexOctaves {
octaves: Box<[Simplex]>,
}
impl SimplexOctaves {
pub fn new(random: &mut JavaRandom, count: usize) -> Self {
Self {
octaves: (0..count).map(|_| Simplex::new(random)).collect(),
}
}
/// A `width × depth` grid laid out x-major.
pub fn sample(
&self,
origin: [f64; 2],
size: [usize; 2],
scale: [f64; 2],
lacunarity: f64,
persistence: f64,
) -> Vec<f64> {
let mut out = vec![0.0; size[0] * size[1]];
let scale = scale.map(|s| s / 1.5);
let (mut frequency, mut amplitude) = (1.0, 1.0);
for octave in &self.octaves {
let scale = scale.map(|s| s * frequency);
octave.add_to(&mut out, origin, size, scale, 0.55 / amplitude);
frequency *= lacunarity;
amplitude *= persistence;
}
out
}
}