Change to 64x64x64 chunks, parallellize worldgen and geometry generation

This commit is contained in:
Sijmen 2021-06-01 16:01:38 +02:00
parent aec8d36647
commit 7fdee1dbda
Signed by: vijfhoek
GPG key ID: 82D05C89B28B0DAE
7 changed files with 70 additions and 44 deletions

1
Cargo.lock generated
View file

@ -1532,6 +1532,7 @@ dependencies = [
"image",
"log",
"noise",
"rayon",
"wgpu",
"winit",
]

View file

@ -16,6 +16,7 @@ futures = "0.3.15"
image = "0.23.14"
log = "0.4.14"
noise = "0.7.0"
rayon = "1.5.1"
wgpu = "0.8.1"
winit = { version = "0.25.0", default_features = false, features = ["x11", "web-sys"] }

View file

@ -2,7 +2,7 @@ use std::{collections::VecDeque, convert::TryInto, usize};
use crate::{cube, quad::Quad, vertex::Vertex};
use ahash::{AHashMap, AHashSet};
use cgmath::{InnerSpace, Vector3, Zero};
use cgmath::{Vector3, Zero};
use noise::utils::{NoiseMapBuilder, PlaneMapBuilder};
#[allow(dead_code)]
@ -52,7 +52,7 @@ pub struct Block {
pub block_type: BlockType,
}
pub(crate) const CHUNK_SIZE: usize = 16;
pub const CHUNK_SIZE: usize = 64;
type ChunkBlocks = [[[Option<Block>; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE];
@ -64,8 +64,8 @@ impl Chunk {
pub fn generate(chunk_x: i32, chunk_y: i32, chunk_z: i32) -> Self {
let fbm = noise::Fbm::new();
const TERRAIN_NOISE_SCALE: f64 = 0.1;
const TERRAIN_NOISE_OFFSET: f64 = 0.0;
const TERRAIN_NOISE_SCALE: f64 = 0.1 / 16.0 * CHUNK_SIZE as f64;
const TERRAIN_NOISE_OFFSET: f64 = 0.0 / 16.0 * CHUNK_SIZE as f64;
let terrain_noise = PlaneMapBuilder::new(&fbm)
.set_size(CHUNK_SIZE, CHUNK_SIZE)
.set_x_bounds(
@ -78,8 +78,8 @@ impl Chunk {
)
.build();
const STONE_NOISE_SCALE: f64 = 0.07;
const STONE_NOISE_OFFSET: f64 = 11239.0;
const STONE_NOISE_SCALE: f64 = 0.07 / 16.0 * CHUNK_SIZE as f64;
const STONE_NOISE_OFFSET: f64 = 11239.0 / 16.0 * CHUNK_SIZE as f64;
let stone_noise = PlaneMapBuilder::new(&fbm)
.set_size(CHUNK_SIZE, CHUNK_SIZE)
.set_x_bounds(
@ -92,7 +92,7 @@ impl Chunk {
)
.build();
let mut blocks: ChunkBlocks = Default::default();
let mut blocks: ChunkBlocks = [[[Default::default(); CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE];
for z in 0..CHUNK_SIZE {
for x in 0..CHUNK_SIZE {
let v = terrain_noise.get_value(x, z) * 20.0 + 128.0;
@ -101,14 +101,14 @@ impl Chunk {
let s = stone_noise.get_value(x, z) * 20.0 + 4.5;
let s = (s.round() as i32).min(10).max(3);
let stone_max = (v - s - chunk_y * 16).min(CHUNK_SIZE as i32);
let stone_max = (v - s - chunk_y * CHUNK_SIZE as i32).min(CHUNK_SIZE as i32);
for y in 0..stone_max {
blocks[y as usize][z][x] = Some(Block {
block_type: BlockType::Stone,
});
}
let dirt_max = (v - chunk_y * 16).min(CHUNK_SIZE as i32);
let dirt_max = (v - chunk_y * CHUNK_SIZE as i32).min(CHUNK_SIZE as i32);
for y in stone_max.max(0)..dirt_max {
blocks[y as usize][z][x] = Some(Block {
block_type: BlockType::Dirt,

View file

@ -31,7 +31,7 @@ pub fn vertices(
let mut current_index = start_index;
let mut vertices = Vec::new();
let mut indices = Vec::new();
let highlighted: [f32; 3] = (-highlighted).map(|x| x as f32).into();
let highlighted: [f32; 3] = (-highlighted).cast().unwrap().into();
if visible_faces & FACE_LEFT == FACE_LEFT {
let normal = [-1.0, 0.0, 0.0];

View file

@ -3,7 +3,7 @@ pub mod world_state;
use std::time::Duration;
use cgmath::{EuclideanSpace, InnerSpace, Rad, Vector3};
use cgmath::{EuclideanSpace, InnerSpace, Rad, Vector2, Vector3};
use winit::{
event::{DeviceEvent, ElementState, KeyboardInput, VirtualKeyCode},
window::Window,
@ -225,9 +225,9 @@ impl State {
if *button == 1 {
world.set_block(pos.x as isize, pos.y as isize, pos.z as isize, None);
self.world_state
.update_chunk_geometry(&self.render_device, pos / 16);
.update_chunk_geometry(&self.render_device, pos / CHUNK_SIZE);
} else if *button == 3 {
let new_pos = pos.map(|x| x as i32) - axis;
let new_pos = pos.cast().unwrap() - axis;
world.set_block(
new_pos.x as isize,
@ -239,7 +239,7 @@ impl State {
);
self.world_state
.update_chunk_geometry(&self.render_device, pos / 16);
.update_chunk_geometry(&self.render_device, pos / CHUNK_SIZE);
}
}
}
@ -322,9 +322,18 @@ impl State {
render_pass.set_bind_group(1, &self.world_state.uniform_bind_group, &[]);
render_pass.set_bind_group(2, &self.world_state.time_bind_group, &[]);
for (chunk_vertices, chunk_indices, index_count) in
self.world_state.chunk_buffers.values()
let camera_pos = self.world_state.camera.position.to_vec();
let camera_pos = Vector2::new(camera_pos.x, camera_pos.z);
for (position, (chunk_vertices, chunk_indices, index_count)) in
&self.world_state.chunk_buffers
{
let pos = (position * CHUNK_SIZE).cast().unwrap();
let pos = Vector2::new(pos.x, pos.z);
if (pos - camera_pos).magnitude() > 300.0 {
continue;
}
render_pass.set_vertex_buffer(0, chunk_vertices.slice(..));
render_pass.set_index_buffer(chunk_indices.slice(..), wgpu::IndexFormat::Uint16);
render_pass.draw_indexed(0..*index_count as u32, 0, 0..1);

View file

@ -57,7 +57,7 @@ impl WorldState {
swap_chain_descriptor.height,
cgmath::Deg(45.0),
0.1,
500.0,
5000.0,
);
(camera, projection)

View file

@ -3,28 +3,37 @@ use crate::{
vertex::Vertex,
};
use cgmath::{InnerSpace, Vector3};
use rayon::prelude::*;
pub struct World {
pub chunks: Vec<Vec<Vec<Chunk>>>,
}
const WORLD_SIZE: usize = 16;
const WORLD_SIZE: Vector3<usize> = Vector3::new(
32 * 16 / CHUNK_SIZE,
16 * 16 / CHUNK_SIZE,
32 * 16 / CHUNK_SIZE,
);
impl World {
pub fn generate() -> Self {
let mut chunks = Vec::with_capacity(WORLD_SIZE);
for y in 0..WORLD_SIZE {
let mut chunks_z = Vec::with_capacity(WORLD_SIZE);
for z in 0..WORLD_SIZE {
let mut chunks_x = Vec::with_capacity(WORLD_SIZE);
for x in 0..WORLD_SIZE {
let chunk = Chunk::generate(x as i32, y as i32, z as i32);
chunks_x.push(chunk);
let mut chunks = Vec::new();
(0..WORLD_SIZE.y)
.into_par_iter()
.map(|y| {
let mut chunks_z = Vec::new();
for z in 0..WORLD_SIZE.z {
let mut chunks_x = Vec::new();
for x in 0..WORLD_SIZE.x {
let chunk = Chunk::generate(x as i32, y as i32, z as i32);
chunks_x.push(chunk);
}
chunks_z.push(chunks_x);
}
chunks_z.push(chunks_x);
}
chunks.push(chunks_z);
}
chunks_z
})
.collect_into_vec(&mut chunks);
Self { chunks }
}
@ -33,7 +42,7 @@ impl World {
highlighted: Option<(Vector3<usize>, Vector3<i32>)>,
chunk_position: Vector3<usize>,
) -> Option<(Vector3<usize>, Vector3<i32>)> {
let position = chunk_position * 16;
let position = chunk_position * CHUNK_SIZE;
if let Some((pos, face)) = highlighted {
if pos.x >= position.x
&& pos.x < position.x + CHUNK_SIZE
@ -56,19 +65,25 @@ impl World {
highlighted: Option<(Vector3<usize>, Vector3<i32>)>,
) -> Vec<(Vector3<usize>, Vec<Vertex>, Vec<u16>)> {
let instant = std::time::Instant::now();
let mut geometry = Vec::new();
for (y, chunks_y) in self.chunks.iter().enumerate() {
for (z, chunks_z) in chunks_y.iter().enumerate() {
for (x, chunk) in chunks_z.iter().enumerate() {
let chunk_position = Vector3::new(x as usize, y as usize, z as usize);
let offset = chunk_position.map(|x| x as i32 * 16);
let h = Self::highlighted_for_chunk(highlighted, chunk_position);
let (vertices, indices) = chunk.to_geometry(offset, h.as_ref());
geometry.push((Vector3::new(x, y, z), vertices, indices));
let chunks = &self.chunks;
let geometry = chunks
.par_iter()
.enumerate()
.flat_map(|(y, chunks_y)| {
let mut geometry = Vec::new();
for (z, chunks_z) in chunks_y.iter().enumerate() {
for (x, chunk) in chunks_z.iter().enumerate() {
let chunk_position = Vector3::new(x as usize, y as usize, z as usize);
let offset = (chunk_position * CHUNK_SIZE).cast().unwrap();
let h = Self::highlighted_for_chunk(highlighted, chunk_position);
let (vertices, indices) = chunk.to_geometry(offset, h.as_ref());
geometry.push((Vector3::new(x, y, z), vertices, indices));
}
}
}
}
geometry
})
.collect();
let elapsed = instant.elapsed();
println!("Generating world geometry took {:?}", elapsed);
@ -135,7 +150,7 @@ impl World {
Self::calc_scale(direction, direction.z),
);
let mut position = origin.map(|x| x as i32);
let mut position: Vector3<i32> = origin.cast().unwrap();
let step = direction.map(|x| x.signum() as i32);
// Truncate the origin
@ -185,7 +200,7 @@ impl World {
.is_some()
{
// Intersection occurred
return Some((position.map(|x| x as usize), face));
return Some((position.cast().unwrap(), face));
}
}