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 particles ambient.rs
use common::math::floor_f64;
use common::world::Block;
use common::world::shape::BlockView;
use glam::DVec3;
use super::Particles;
use crate::world::ClientWorld;
impl Particles {
pub fn ambient(&mut self, world: &mut ClientWorld, center: DVec3) {
let center = center.to_array().map(floor_f64);
for _ in 0..1000 {
let [x, y, z] = center.map(|p| p + self.random.next_int(16) - self.random.next_int(16));
match world.block(x, y, z) {
Block::TORCH => {
let point = [x as f32 + 0.5, y as f32 + 0.7, z as f32 + 0.5].map(f64::from);
self.flame(DVec3::from_array(point) + torch_offset(world.metadata(x, y, z)));
}
Block::LIT_FURNACE => {
let mut point = [
x as f32 + 0.5,
y as f32 + self.random.next_float() * 6.0 / 16.0,
z as f32 + 0.5,
];
let offset = self.random.next_float() * 0.6 - 0.3;
let (dx, dz) = match world.metadata(x, y, z) {
2 => (offset, -0.52),
3 => (offset, 0.52),
4 => (-0.52, offset),
5 => (0.52, offset),
_ => continue,
};
point[0] += dx;
point[2] += dz;
self.flame(DVec3::from_array(point.map(f64::from)));
}
Block::FIRE => self.fire(world, [x, y, z]),
Block::REDSTONE_WIRE => self.wire(world.metadata(x, y, z), [x, y, z]),
Block::REDSTONE_TORCH => self.redstone_torch(world.metadata(x, y, z), [x, y, z]),
Block::POWERED_REPEATER => self.repeater(world.metadata(x, y, z), [x, y, z]),
Block::PORTAL => self.portal(world, [x, y, z]),
Block::LIT_REDSTONE_ORE => self.sparkle(world, [x, y, z]),
Block::WATER | Block::FLOWING_WATER => {
// `BlockFluid.randomDisplayTick`
let metadata = world.metadata(x, y, z);
if self.random.next_int(64) == 0 && (1..8).contains(&metadata) {
world.sounds.push((
"liquid.water".into(),
DVec3::new(x as f64, y as f64, z as f64) + 0.5,
self.random.next_float() * 0.25 + 0.75,
self.random.next_float() + 0.5,
));
}
}
Block::LAVA | Block::FLOWING_LAVA => {
let above = world.block(x, y + 1, z);
if above.is_air() && !above.is_opaque_cube() && self.random.next_int(100) == 0 {
let point = DVec3::new(
f64::from(x as f32 + self.random.next_float()),
f64::from(y) + 1.0,
f64::from(z as f32 + self.random.next_float()),
);
self.effect("lava", point, DVec3::ZERO);
}
}
_ => {}
}
}
}
/// The client half of `TileEntityMobSpawner.updateEntity`: smoke and flames in the cage
/// of each spawner near a player.
pub fn spawners(&mut self, world: &ClientWorld, player: DVec3) {
let center = common::world::ChunkPos::containing(floor_f64(player.x), floor_f64(player.z));
for [x, y, z] in world.spawners_near(center, 2) {
let middle = DVec3::new(f64::from(x), f64::from(y), f64::from(z)) + 0.5;
if middle.distance_squared(player) < 256.0 {
let point = self.random_point([x, y, z]);
self.flame(point);
}
}
}
/// A random point in the block at `(x, y, z)`.
fn random_point(&mut self, position: [i32; 3]) -> DVec3 {
DVec3::from_array(position.map(|v| f64::from(v as f32 + self.random.next_float())))
}
/// A point jittered by up to 0.1 around `(x, y, z)` offset by `base`, the way redstone
/// torches and repeaters place their dust.
fn jitter(&mut self, [x, y, z]: [i32; 3], base: f32) -> DVec3 {
let mut jitter = |v: i32, offset: f32| {
f64::from(v as f32 + offset) + f64::from(self.random.next_float() - 0.5) * 0.2
};
DVec3::new(jitter(x, 0.5), jitter(y, base), jitter(z, 0.5))
}
/// `BlockRedstoneWire.randomDisplayTick`: dust as red as the wire's power.
fn wire(&mut self, power: u8, [x, y, z]: [i32; 3]) {
if power == 0 {
return;
}
let px = f64::from(x) + 0.5 + (f64::from(self.random.next_float()) - 0.5) * 0.2;
let py = f64::from(y as f32 + 1.0 / 16.0);
let pz = f64::from(z) + 0.5 + (f64::from(self.random.next_float()) - 0.5) * 0.2;
let level = f32::from(power) / 15.0;
let color = DVec3::new(
f64::from(level * 0.6 + 0.4),
f64::from((level * level * 0.7 - 0.5).max(0.0)),
f64::from((level * level * 0.6 - 0.7).max(0.0)),
);
self.effect("reddust", DVec3::new(px, py, pz), color);
}
/// `BlockRedstoneTorch.randomDisplayTick` of a lit torch.
fn redstone_torch(&mut self, metadata: u8, position: [i32; 3]) {
let point = self.jitter(position, 0.7) + torch_offset(metadata);
self.effect("reddust", point, DVec3::ZERO);
}
/// `BlockRedstoneRepeater.randomDisplayTick`: dust from either of its torches.
fn repeater(&mut self, metadata: u8, position: [i32; 3]) {
const TORCH: [f64; 4] = [-0.0625, 0.0625, 0.1875, 0.3125];
let point = self.jitter(position, 0.4);
let offset = if self.random.next_int(2) == 0 {
0.3125
} else {
-TORCH[usize::from(metadata & 12) >> 2]
};
let shift = match metadata & 3 {
0 => DVec3::new(0.0, 0.0, -offset),
1 => DVec3::new(offset, 0.0, 0.0),
2 => DVec3::new(0.0, 0.0, offset),
_ => DVec3::new(-offset, 0.0, 0.0),
};
self.effect("reddust", point + shift, DVec3::ZERO);
}
/// `BlockPortal.randomDisplayTick`: its hum and particles drifting out of its surface.
fn portal(&mut self, world: &mut ClientWorld, [x, y, z]: [i32; 3]) {
if self.random.next_int(100) == 0 {
world.sounds.push((
"portal.portal".into(),
DVec3::new(x as f64, y as f64, z as f64) + 0.5,
1.0,
self.random.next_float() * 0.4 + 0.8,
));
}
let along_x =
world.block(x - 1, y, z) == Block::PORTAL || world.block(x + 1, y, z) == Block::PORTAL;
for _ in 0..4 {
let mut point = self.random_point([x, y, z]);
let side = f64::from(self.random.next_int(2) * 2 - 1);
let mut velocity = DVec3::from_array(std::array::from_fn(|_| {
(f64::from(self.random.next_float()) - 0.5) * 0.5
}));
let push = f64::from(self.random.next_float() * 2.0 * side as f32);
if along_x {
point.z = f64::from(z) + 0.5 + 0.25 * side;
velocity.z = push;
} else {
point.x = f64::from(x) + 0.5 + 0.25 * side;
velocity.x = push;
}
self.effect("portal", point, velocity);
}
}
/// `BlockRedstoneOre.sparkle`: dust on the faces of glowing ore that are open to the air.
fn sparkle(&mut self, world: &mut ClientWorld, [x, y, z]: [i32; 3]) {
const OFFSET: f64 = 1.0 / 16.0;
for face in 0..6 {
let mut point = [x, y, z].map(|v| f64::from(v as f32 + world.rand.next_float()));
let open =
|dx: i32, dy: i32, dz: i32| !world.block(x + dx, y + dy, z + dz).is_opaque_cube();
match face {
0 if open(0, 1, 0) => point[1] = f64::from(y + 1) + OFFSET,
1 if open(0, -1, 0) => point[1] = f64::from(y) - OFFSET,
2 if open(0, 0, 1) => point[2] = f64::from(z + 1) + OFFSET,
3 if open(0, 0, -1) => point[2] = f64::from(z) - OFFSET,
4 if open(1, 0, 0) => point[0] = f64::from(x + 1) + OFFSET,
5 if open(-1, 0, 0) => point[0] = f64::from(x) - OFFSET,
_ => {}
}
// The original compares the height against 0 instead of the block's bottom
let [px, py, pz] = point;
if px < f64::from(x)
|| px > f64::from(x + 1)
|| py < 0.0
|| py > f64::from(y + 1)
|| pz < f64::from(z)
|| pz > f64::from(z + 1)
{
self.effect("reddust", DVec3::from_array(point), DVec3::ZERO);
}
}
}
fn flame(&mut self, point: DVec3) {
self.effect("smoke", point, DVec3::ZERO);
self.effect("flame", point, DVec3::ZERO);
}
fn fire(&mut self, world: &mut ClientWorld, [x, y, z]: [i32; 3]) {
if self.random.next_int(24) == 0 {
world.sounds.push((
"fire.fire".into(),
DVec3::new(x as f64 + 0.5, y as f64 + 0.5, z as f64 + 0.5),
1.0 + self.random.next_float(),
self.random.next_float() * 0.7 + 0.3,
));
}
let below = world.block(x, y - 1, z);
if below.is_normal_cube() || below.fire_rates().0 > 0 {
for _ in 0..3 {
let point = DVec3::new(
f64::from(x as f32 + self.random.next_float()),
f64::from(y as f32 + self.random.next_float() * 0.5 + 0.5),
f64::from(z as f32 + self.random.next_float()),
);
self.effect("largesmoke", point, DVec3::ZERO);
}
} else {
for (axis, direction) in [(0, -1), (0, 1), (2, -1), (2, 1), (1, 1)] {
let mut neighbor = [x, y, z];
neighbor[axis] += direction;
let [nx, ny, nz] = neighbor;
if world.block(nx, ny, nz).fire_rates().0 == 0 {
continue;
}
for _ in 0..2 {
let mut point = [x, y, z].map(|p| p as f32 + self.random.next_float());
let fraction = point[axis] - [x, y, z][axis] as f32;
point[axis] = [x, y, z][axis] as f32
+ if direction < 0 {
fraction * 0.1
} else {
1.0 - fraction * 0.1
};
self.effect(
"largesmoke",
DVec3::from_array(point.map(f64::from)),
DVec3::ZERO,
);
}
}
}
}
}
/// Where a torch on the given side of a block shows its flame, relative to a standing one.
fn torch_offset(metadata: u8) -> DVec3 {
let (up, out) = (f64::from(0.22_f32), f64::from(0.27_f32));
match metadata {
1 => DVec3::new(-out, up, 0.0),
2 => DVec3::new(out, up, 0.0),
3 => DVec3::new(0.0, up, -out),
4 => DVec3::new(0.0, up, out),
_ => DVec3::ZERO,
}
}