Change to 64x64x64 chunks, parallellize worldgen and geometry generation
This commit is contained in:
parent
aec8d36647
commit
7fdee1dbda
7 changed files with 70 additions and 44 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1532,6 +1532,7 @@ dependencies = [
|
|||
"image",
|
||||
"log",
|
||||
"noise",
|
||||
"rayon",
|
||||
"wgpu",
|
||||
"winit",
|
||||
]
|
||||
|
|
|
@ -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"] }
|
||||
|
||||
|
|
18
src/chunk.rs
18
src/chunk.rs
|
@ -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,
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -57,7 +57,7 @@ impl WorldState {
|
|||
swap_chain_descriptor.height,
|
||||
cgmath::Deg(45.0),
|
||||
0.1,
|
||||
500.0,
|
||||
5000.0,
|
||||
);
|
||||
|
||||
(camera, projection)
|
||||
|
|
69
src/world.rs
69
src/world.rs
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue