Move chunk loading/saving logic to World, sort chunk loading based on distance to camera
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
a8936d9046
commit
0ca35638fc
5 changed files with 213 additions and 137 deletions
60
Cargo.lock
generated
60
Cargo.lock
generated
|
@ -518,6 +518,16 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs2"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.15"
|
version = "0.3.15"
|
||||||
|
@ -799,6 +809,12 @@ dependencies = [
|
||||||
"weezl",
|
"weezl",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glow"
|
name = "glow"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -970,6 +986,15 @@ dependencies = [
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -1124,16 +1149,16 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"gltf",
|
"gltf",
|
||||||
"image",
|
"image",
|
||||||
"itertools",
|
"itertools 0.10.0",
|
||||||
"log",
|
"log",
|
||||||
"noise",
|
"noise",
|
||||||
"rayon",
|
"rayon",
|
||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
|
"sled",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"winit",
|
"winit",
|
||||||
"zstd",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1694,6 +1719,23 @@ version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sled"
|
||||||
|
version = "0.34.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d0132f3e393bcb7390c60bb45769498cf4550bcb7a21d7f95c02b69f6362cdc"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"fs2",
|
||||||
|
"fxhash",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"zstd",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slotmap"
|
name = "slotmap"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -2065,18 +2107,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zstd"
|
name = "zstd"
|
||||||
version = "0.8.2+zstd.1.5.0"
|
version = "0.5.4+zstd.1.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c83508bcbbdc9c3abcf77e8e56773d3ffcd2479e0933caab2e7d6b5a9e183aae"
|
checksum = "69996ebdb1ba8b1517f61387a883857818a66c8a295f487b1ffd8fd9d2c82910"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zstd-safe",
|
"zstd-safe",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zstd-safe"
|
name = "zstd-safe"
|
||||||
version = "4.1.0+zstd.1.5.0"
|
version = "2.0.6+zstd.1.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d30375f78e185ca4c91930f42ea2c0162f9aa29737032501f93b79266d985ae7"
|
checksum = "98aa931fb69ecee256d44589d19754e61851ae4769bf963b385119b1cc37a49e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"zstd-sys",
|
"zstd-sys",
|
||||||
|
@ -2084,10 +2126,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zstd-sys"
|
name = "zstd-sys"
|
||||||
version = "1.6.0+zstd.1.5.0"
|
version = "1.4.18+zstd.1.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2141bed8922b427761470e6bbfeff255da94fa20b0bbeab0d9297fcaf71e3aa7"
|
checksum = "a1e6e8778706838f43f771d80d37787cb2fe06dafe89dd3aebaf6721b9eaec81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
|
"glob",
|
||||||
|
"itertools 0.9.0",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
|
@ -23,7 +23,7 @@ serde_repr = "0.1.7"
|
||||||
rmp-serde = "0.15.4"
|
rmp-serde = "0.15.4"
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
serde = { version = "1.0.126", features = ["derive"] }
|
serde = { version = "1.0.126", features = ["derive"] }
|
||||||
zstd = "0.8.2"
|
sled = { version = "0.34.6", features = ["compression"] }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
64
src/chunk.rs
64
src/chunk.rs
|
@ -1,8 +1,4 @@
|
||||||
use std::{
|
use std::{collections::VecDeque, usize};
|
||||||
collections::VecDeque,
|
|
||||||
io::{Read, Write},
|
|
||||||
usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{geometry::Geometry, quad::Quad, vertex::BlockVertex};
|
use crate::{geometry::Geometry, quad::Quad, vertex::BlockVertex};
|
||||||
use ahash::{AHashMap, AHashSet};
|
use ahash::{AHashMap, AHashSet};
|
||||||
|
@ -10,11 +6,10 @@ use cgmath::{Point3, Vector3};
|
||||||
use noise::utils::{NoiseMapBuilder, PlaneMapBuilder};
|
use noise::utils::{NoiseMapBuilder, PlaneMapBuilder};
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{SeqAccess, Visitor},
|
de::{SeqAccess, Visitor},
|
||||||
ser::{SerializeSeq, Serializer},
|
ser::{SerializeSeq, Serializer},
|
||||||
Deserialize,
|
Deserialize, Serialize,
|
||||||
};
|
};
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
||||||
|
@ -131,7 +126,7 @@ impl<'de> Deserialize<'de> for Chunk {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
pub fn generate(chunk_x: i32, chunk_y: i32, chunk_z: i32) -> Self {
|
pub fn generate(&mut self, chunk_x: isize, chunk_y: isize, chunk_z: isize) {
|
||||||
let fbm = noise::Fbm::new();
|
let fbm = noise::Fbm::new();
|
||||||
|
|
||||||
const TERRAIN_NOISE_SCALE: f64 = 0.1 / 16.0 * CHUNK_SIZE as f64;
|
const TERRAIN_NOISE_SCALE: f64 = 0.1 / 16.0 * CHUNK_SIZE as f64;
|
||||||
|
@ -162,42 +157,41 @@ impl Chunk {
|
||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let mut blocks: ChunkBlocks = [[[Default::default(); CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE];
|
|
||||||
for z in 0..CHUNK_SIZE {
|
for z in 0..CHUNK_SIZE {
|
||||||
for x in 0..CHUNK_SIZE {
|
for x in 0..CHUNK_SIZE {
|
||||||
let v = terrain_noise.get_value(x, z) * 20.0 + 128.0;
|
let v = terrain_noise.get_value(x, z) * 20.0 + 128.0;
|
||||||
let v = v.round() as i32;
|
let v = v.round() as isize;
|
||||||
|
|
||||||
let s = stone_noise.get_value(x, z) * 20.0 + 4.5;
|
let s = stone_noise.get_value(x, z) * 20.0 + 4.5;
|
||||||
let s = (s.round() as i32).min(10).max(3);
|
let s = (s.round() as isize).min(10).max(3);
|
||||||
|
|
||||||
let stone_max = (v - s - chunk_y * CHUNK_SIZE as i32).min(CHUNK_SIZE as i32);
|
let stone_max = (v - s - chunk_y * CHUNK_ISIZE).min(CHUNK_ISIZE);
|
||||||
for y in 0..stone_max {
|
for y in 0..stone_max {
|
||||||
blocks[y as usize][z][x] = Some(Block {
|
self.blocks[y as usize][z][x] = Some(Block {
|
||||||
block_type: BlockType::Stone,
|
block_type: BlockType::Stone,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let dirt_max = (v - chunk_y * CHUNK_SIZE as i32).min(CHUNK_SIZE as i32);
|
let dirt_max = (v - chunk_y * CHUNK_ISIZE).min(CHUNK_ISIZE);
|
||||||
for y in stone_max.max(0)..dirt_max {
|
for y in stone_max.max(0)..dirt_max {
|
||||||
blocks[y as usize][z][x] = Some(Block {
|
self.blocks[y as usize][z][x] = Some(Block {
|
||||||
block_type: BlockType::Dirt,
|
block_type: BlockType::Dirt,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if dirt_max >= 0 && dirt_max < CHUNK_SIZE as i32 {
|
if dirt_max >= 0 && dirt_max < CHUNK_ISIZE {
|
||||||
blocks[dirt_max as usize][z][x] = Some(Block {
|
self.blocks[dirt_max as usize][z][x] = Some(Block {
|
||||||
block_type: BlockType::Grass,
|
block_type: BlockType::Grass,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if chunk_y == 0 {
|
if chunk_y == 0 {
|
||||||
blocks[0][z][x] = Some(Block {
|
self.blocks[0][z][x] = Some(Block {
|
||||||
block_type: BlockType::Bedrock,
|
block_type: BlockType::Bedrock,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if chunk_y < 128 / CHUNK_SIZE as i32 {
|
if chunk_y < 128 / CHUNK_ISIZE {
|
||||||
for layer in blocks.iter_mut() {
|
for layer in self.blocks.iter_mut() {
|
||||||
if layer[z][x].is_none() {
|
if layer[z][x].is_none() {
|
||||||
layer[z][x] = Some(Block {
|
layer[z][x] = Some(Block {
|
||||||
block_type: BlockType::Water,
|
block_type: BlockType::Water,
|
||||||
|
@ -207,8 +201,6 @@ impl Chunk {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self { blocks }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
|
@ -400,26 +392,22 @@ impl Chunk {
|
||||||
Self::quads_to_geometry(quads)
|
Self::quads_to_geometry(quads)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&self, position: Point3<isize>) -> anyhow::Result<()> {
|
pub fn save(&self, position: Point3<isize>, store: &sled::Db) -> anyhow::Result<()> {
|
||||||
let data = rmp_serde::encode::to_vec_named(self)?;
|
let data = rmp_serde::encode::to_vec_named(self)?;
|
||||||
let compressed = zstd::block::compress(&data, 0)?;
|
let key = format!("{}_{}_{}", position.x, position.y, position.z);
|
||||||
|
store.insert(key, data)?;
|
||||||
let path = format!("chunks/{}_{}_{}.bin", position.x, position.y, position.z);
|
|
||||||
let mut file = std::fs::File::create(&path)?;
|
|
||||||
file.write(&compressed)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(&mut self, position: Point3<isize>) -> anyhow::Result<()> {
|
pub fn load(&mut self, position: Point3<isize>, store: &sled::Db) -> anyhow::Result<bool> {
|
||||||
let path = format!("chunks/{}_{}_{}.bin", position.x, position.y, position.z);
|
let key = format!("{}_{}_{}", position.x, position.y, position.z);
|
||||||
let mut file = std::fs::File::open(&path)?;
|
|
||||||
|
|
||||||
let mut compressed = Vec::new();
|
if let Some(data) = store.get(key)? {
|
||||||
file.read_to_end(&mut compressed)?;
|
*self = rmp_serde::decode::from_slice(&data)?;
|
||||||
let data = zstd::block::decompress(&compressed, 1024 * 1024)?;
|
Ok(false)
|
||||||
|
} else {
|
||||||
*self = rmp_serde::decode::from_slice(&data)?;
|
self.generate(position.x, position.y, position.z);
|
||||||
Ok(())
|
Ok(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
use std::{
|
use std::time::{Duration, Instant};
|
||||||
collections::VecDeque,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
use ahash::AHashMap;
|
|
||||||
use cgmath::{EuclideanSpace, InnerSpace, Point3, Rad, Vector2, Vector3};
|
use cgmath::{EuclideanSpace, InnerSpace, Point3, Rad, Vector2, Vector3};
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
util::{BufferInitDescriptor, DeviceExt},
|
util::{BufferInitDescriptor, DeviceExt},
|
||||||
|
@ -38,15 +34,11 @@ pub struct WorldState {
|
||||||
pub time_bind_group: wgpu::BindGroup,
|
pub time_bind_group: wgpu::BindGroup,
|
||||||
pub world: World,
|
pub world: World,
|
||||||
|
|
||||||
pub chunk_buffers: AHashMap<Point3<isize>, GeometryBuffers>,
|
|
||||||
pub chunk_save_queue: VecDeque<Point3<isize>>,
|
|
||||||
pub chunk_load_queue: VecDeque<Point3<isize>>,
|
|
||||||
time: Time,
|
time: Time,
|
||||||
time_buffer: wgpu::Buffer,
|
time_buffer: wgpu::Buffer,
|
||||||
wireframe: bool,
|
wireframe: bool,
|
||||||
shader: wgpu::ShaderModule,
|
shader: wgpu::ShaderModule,
|
||||||
render_pipeline_layout: wgpu::PipelineLayout,
|
render_pipeline_layout: wgpu::PipelineLayout,
|
||||||
pub highlighted: Option<(Point3<isize>, Vector3<i32>)>,
|
|
||||||
|
|
||||||
pub forward_pressed: bool,
|
pub forward_pressed: bool,
|
||||||
pub backward_pressed: bool,
|
pub backward_pressed: bool,
|
||||||
|
@ -222,18 +214,19 @@ impl WorldState {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO Move to World
|
||||||
pub fn update_world_geometry(&mut self, render_context: &RenderContext) {
|
pub fn update_world_geometry(&mut self, render_context: &RenderContext) {
|
||||||
let instant = Instant::now();
|
let instant = Instant::now();
|
||||||
|
|
||||||
let world_geometry = self.world.to_geometry(self.highlighted);
|
let world_geometry = self.world.to_geometry(self.world.highlighted);
|
||||||
self.chunk_buffers.clear();
|
self.world.chunk_buffers.clear();
|
||||||
for (chunk_position, chunk_geometry) in world_geometry {
|
for (chunk_position, chunk_geometry) in world_geometry {
|
||||||
let buffers = GeometryBuffers::from_geometry(
|
let buffers = GeometryBuffers::from_geometry(
|
||||||
render_context,
|
render_context,
|
||||||
&chunk_geometry,
|
&chunk_geometry,
|
||||||
BufferUsage::empty(),
|
BufferUsage::empty(),
|
||||||
);
|
);
|
||||||
self.chunk_buffers.insert(chunk_position, buffers);
|
self.world.chunk_buffers.insert(chunk_position, buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = instant.elapsed();
|
let elapsed = instant.elapsed();
|
||||||
|
@ -258,23 +251,6 @@ impl WorldState {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_chunk_geometry(
|
|
||||||
&mut self,
|
|
||||||
render_context: &RenderContext,
|
|
||||||
chunk_position: Point3<isize>,
|
|
||||||
) {
|
|
||||||
let chunk = &mut self.world.chunks.get(&chunk_position).unwrap();
|
|
||||||
let offset = chunk_position * CHUNK_ISIZE;
|
|
||||||
let geometry = chunk.to_geometry(
|
|
||||||
offset,
|
|
||||||
World::highlighted_for_chunk(self.highlighted, &chunk_position).as_ref(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let buffers =
|
|
||||||
GeometryBuffers::from_geometry(render_context, &geometry, BufferUsage::empty());
|
|
||||||
self.chunk_buffers.insert(chunk_position, buffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toggle_wireframe(&mut self, render_context: &RenderContext) {
|
pub fn toggle_wireframe(&mut self, render_context: &RenderContext) {
|
||||||
self.wireframe = !self.wireframe;
|
self.wireframe = !self.wireframe;
|
||||||
self.render_pipeline = Self::create_render_pipeline(
|
self.render_pipeline = Self::create_render_pipeline(
|
||||||
|
@ -286,8 +262,6 @@ impl WorldState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(render_context: &RenderContext) -> WorldState {
|
pub fn new(render_context: &RenderContext) -> WorldState {
|
||||||
let world = World::generate();
|
|
||||||
|
|
||||||
let texture_manager = Self::create_textures(render_context);
|
let texture_manager = Self::create_textures(render_context);
|
||||||
|
|
||||||
let (camera, projection) = Self::create_camera(render_context);
|
let (camera, projection) = Self::create_camera(render_context);
|
||||||
|
@ -297,6 +271,8 @@ impl WorldState {
|
||||||
|
|
||||||
let (time, time_buffer, time_layout, time_bind_group) = Self::create_time(render_context);
|
let (time, time_buffer, time_layout, time_bind_group) = Self::create_time(render_context);
|
||||||
|
|
||||||
|
let world = World::new();
|
||||||
|
|
||||||
let shader = render_context.device.create_shader_module(
|
let shader = render_context.device.create_shader_module(
|
||||||
&(wgpu::ShaderModuleDescriptor {
|
&(wgpu::ShaderModuleDescriptor {
|
||||||
label: Some("shader"),
|
label: Some("shader"),
|
||||||
|
@ -317,10 +293,8 @@ impl WorldState {
|
||||||
&time_layout,
|
&time_layout,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
let render_pipeline =
|
let render_pipeline =
|
||||||
Self::create_render_pipeline(&render_context, &shader, &render_pipeline_layout, false);
|
Self::create_render_pipeline(&render_context, &shader, &render_pipeline_layout, false);
|
||||||
|
|
||||||
let depth_texture = Texture::create_depth_texture(render_context, "depth_texture");
|
let depth_texture = Texture::create_depth_texture(render_context, "depth_texture");
|
||||||
|
|
||||||
let mut world_state = Self {
|
let mut world_state = Self {
|
||||||
|
@ -340,11 +314,8 @@ impl WorldState {
|
||||||
time_bind_group,
|
time_bind_group,
|
||||||
|
|
||||||
world,
|
world,
|
||||||
chunk_buffers: AHashMap::new(),
|
|
||||||
chunk_load_queue: VecDeque::new(),
|
|
||||||
chunk_save_queue: VecDeque::new(),
|
|
||||||
wireframe: false,
|
wireframe: false,
|
||||||
highlighted: None,
|
|
||||||
|
|
||||||
up_speed: 0.0,
|
up_speed: 0.0,
|
||||||
sprinting: false,
|
sprinting: false,
|
||||||
|
@ -352,7 +323,7 @@ impl WorldState {
|
||||||
backward_pressed: false,
|
backward_pressed: false,
|
||||||
left_pressed: false,
|
left_pressed: false,
|
||||||
right_pressed: false,
|
right_pressed: false,
|
||||||
creative: false,
|
creative: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
world_state.update_world_geometry(render_context);
|
world_state.update_world_geometry(render_context);
|
||||||
|
@ -399,7 +370,8 @@ impl WorldState {
|
||||||
let camera_pos = self.camera.position.to_vec();
|
let camera_pos = self.camera.position.to_vec();
|
||||||
let camera_pos = Vector2::new(camera_pos.x, camera_pos.z);
|
let camera_pos = Vector2::new(camera_pos.x, camera_pos.z);
|
||||||
|
|
||||||
for (position, buffers) in &self.chunk_buffers {
|
// TODO Move to World
|
||||||
|
for (position, buffers) in &self.world.chunk_buffers {
|
||||||
let pos = (position * CHUNK_ISIZE).cast().unwrap();
|
let pos = (position * CHUNK_ISIZE).cast().unwrap();
|
||||||
let pos = Vector2::new(pos.x, pos.z);
|
let pos = Vector2::new(pos.x, pos.z);
|
||||||
if (pos - camera_pos).magnitude() > 300.0 {
|
if (pos - camera_pos).magnitude() > 300.0 {
|
||||||
|
@ -419,6 +391,7 @@ impl WorldState {
|
||||||
render_pass
|
render_pass
|
||||||
.set_index_buffer(index_buffer.unwrap().slice(..), wgpu::IndexFormat::Uint32);
|
.set_index_buffer(index_buffer.unwrap().slice(..), wgpu::IndexFormat::Uint32);
|
||||||
render_pass.draw_indexed(0..self.world.npc.indices.len() as u32, 0, 0..1);
|
render_pass.draw_indexed(0..self.world.npc.indices.len() as u32, 0, 0..1);
|
||||||
|
triangle_count += self.world.npc.indices.len() / 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
triangle_count
|
triangle_count
|
||||||
|
@ -436,31 +409,33 @@ impl WorldState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO Move to World
|
||||||
fn update_aim(&mut self, render_context: &RenderContext) {
|
fn update_aim(&mut self, render_context: &RenderContext) {
|
||||||
let camera = &self.camera;
|
let camera = &self.camera;
|
||||||
|
|
||||||
let old = self.highlighted;
|
let old = self.world.highlighted;
|
||||||
let new = self.world.raycast(camera.position, camera.direction());
|
let new = self.world.raycast(camera.position, camera.direction());
|
||||||
|
|
||||||
let old_chunk = old.map(|(pos, _)| pos.map(|n| n.div_euclid(CHUNK_ISIZE)));
|
let old_chunk = old.map(|(pos, _)| pos.map(|n| n.div_euclid(CHUNK_ISIZE)));
|
||||||
let new_chunk = new.map(|(pos, _)| pos.map(|n| n.div_euclid(CHUNK_ISIZE)));
|
let new_chunk = new.map(|(pos, _)| pos.map(|n| n.div_euclid(CHUNK_ISIZE)));
|
||||||
|
|
||||||
if old != new {
|
if old != new {
|
||||||
self.highlighted = new;
|
self.world.highlighted = new;
|
||||||
|
|
||||||
if let Some(old_chunk_) = old_chunk {
|
if let Some(old_chunk_) = old_chunk {
|
||||||
self.update_chunk_geometry(render_context, old_chunk_);
|
self.world.update_chunk_geometry(render_context, old_chunk_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_chunk_) = new_chunk {
|
if let Some(new_chunk_) = new_chunk {
|
||||||
// Don't update the same chunk twice
|
// Don't update the same chunk twice
|
||||||
if old_chunk != new_chunk {
|
if old_chunk != new_chunk {
|
||||||
self.update_chunk_geometry(render_context, new_chunk_);
|
self.world.update_chunk_geometry(render_context, new_chunk_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO Move to World
|
||||||
pub fn input_mouse_button(&mut self, button: &MouseButton, render_context: &RenderContext) {
|
pub fn input_mouse_button(&mut self, button: &MouseButton, render_context: &RenderContext) {
|
||||||
let camera = &self.camera;
|
let camera = &self.camera;
|
||||||
|
|
||||||
|
@ -468,7 +443,8 @@ impl WorldState {
|
||||||
if let Some((pos, face_normal)) = world.raycast(camera.position, camera.direction()) {
|
if let Some((pos, face_normal)) = world.raycast(camera.position, camera.direction()) {
|
||||||
if button == &MouseButton::Left {
|
if button == &MouseButton::Left {
|
||||||
world.set_block(pos.x as isize, pos.y as isize, pos.z as isize, None);
|
world.set_block(pos.x as isize, pos.y as isize, pos.z as isize, None);
|
||||||
self.update_chunk_geometry(render_context, pos / CHUNK_ISIZE);
|
self.world
|
||||||
|
.update_chunk_geometry(render_context, pos / CHUNK_ISIZE);
|
||||||
} else if button == &MouseButton::Right {
|
} else if button == &MouseButton::Right {
|
||||||
let new_pos = pos.cast().unwrap() + face_normal;
|
let new_pos = pos.cast().unwrap() + face_normal;
|
||||||
|
|
||||||
|
@ -481,7 +457,8 @@ impl WorldState {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.update_chunk_geometry(render_context, pos / CHUNK_ISIZE);
|
self.world
|
||||||
|
.update_chunk_geometry(render_context, pos / CHUNK_ISIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -494,8 +471,6 @@ impl WorldState {
|
||||||
VirtualKeyCode::A => self.left_pressed = pressed,
|
VirtualKeyCode::A => self.left_pressed = pressed,
|
||||||
VirtualKeyCode::D => self.right_pressed = pressed,
|
VirtualKeyCode::D => self.right_pressed = pressed,
|
||||||
VirtualKeyCode::F2 if pressed => self.creative = !self.creative,
|
VirtualKeyCode::F2 if pressed => self.creative = !self.creative,
|
||||||
VirtualKeyCode::F3 if pressed => self.chunk_save_queue.extend(self.world.chunks.keys()),
|
|
||||||
VirtualKeyCode::F4 if pressed => self.chunk_load_queue.extend(self.world.chunks.keys()),
|
|
||||||
VirtualKeyCode::Space => {
|
VirtualKeyCode::Space => {
|
||||||
self.up_speed = if pressed {
|
self.up_speed = if pressed {
|
||||||
if self.creative {
|
if self.creative {
|
||||||
|
@ -565,26 +540,11 @@ impl WorldState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, dt: Duration, render_context: &RenderContext) {
|
pub fn update(&mut self, dt: Duration, render_context: &RenderContext) {
|
||||||
if let Some(position) = self.chunk_load_queue.pop_front() {
|
|
||||||
let chunk = self.world.chunks.entry(position).or_default();
|
|
||||||
if let Err(err) = chunk.load(position) {
|
|
||||||
eprintln!("Failed to load chunk {:?}: {:?}", position, err);
|
|
||||||
} else {
|
|
||||||
self.update_chunk_geometry(render_context, position);
|
|
||||||
println!("Loaded chunk {:?}", position);
|
|
||||||
}
|
|
||||||
} else if let Some(position) = self.chunk_save_queue.pop_front() {
|
|
||||||
let chunk = self.world.chunks.get(&position).unwrap();
|
|
||||||
if let Err(err) = chunk.save(position) {
|
|
||||||
eprintln!("Failed to save chunk {:?}: {:?}", position, err);
|
|
||||||
} else {
|
|
||||||
println!("Saved chunk {:?}", position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.update_position(dt);
|
self.update_position(dt);
|
||||||
self.update_aim(render_context);
|
self.update_aim(render_context);
|
||||||
|
|
||||||
|
self.world.update(render_context, &self.camera);
|
||||||
|
|
||||||
self.view
|
self.view
|
||||||
.update_view_projection(&self.camera, &self.projection);
|
.update_view_projection(&self.camera, &self.projection);
|
||||||
render_context
|
render_context
|
||||||
|
|
136
src/world.rs
136
src/world.rs
|
@ -1,45 +1,124 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
chunk::{Block, Chunk, CHUNK_ISIZE, CHUNK_SIZE},
|
camera::Camera,
|
||||||
geometry::Geometry,
|
chunk::{Block, Chunk, CHUNK_ISIZE},
|
||||||
|
geometry::{Geometry, GeometryBuffers},
|
||||||
npc::Npc,
|
npc::Npc,
|
||||||
|
render_context::RenderContext,
|
||||||
vertex::BlockVertex,
|
vertex::BlockVertex,
|
||||||
};
|
};
|
||||||
|
use ahash::AHashMap;
|
||||||
use cgmath::{EuclideanSpace, InnerSpace, Point3, Vector3};
|
use cgmath::{EuclideanSpace, InnerSpace, Point3, Vector3};
|
||||||
use rayon::prelude::*;
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
|
use wgpu::BufferUsage;
|
||||||
|
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub chunks: HashMap<Point3<isize>, Chunk>,
|
pub chunks: HashMap<Point3<isize>, Chunk>,
|
||||||
pub npc: Npc,
|
pub npc: Npc,
|
||||||
|
|
||||||
|
pub chunk_database: sled::Db,
|
||||||
|
pub chunk_save_queue: VecDeque<Point3<isize>>,
|
||||||
|
pub chunk_load_queue: VecDeque<Point3<isize>>,
|
||||||
|
pub chunk_generate_queue: VecDeque<Point3<isize>>,
|
||||||
|
pub chunk_buffers: AHashMap<Point3<isize>, GeometryBuffers>,
|
||||||
|
|
||||||
|
pub highlighted: Option<(Point3<isize>, Vector3<i32>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const WORLD_SIZE: Vector3<usize> = Vector3::new(
|
pub const RENDER_DISTANCE: isize = 8;
|
||||||
8 * 16 / CHUNK_SIZE,
|
pub const WORLD_HEIGHT: isize = 16 * 16 / CHUNK_ISIZE;
|
||||||
16 * 16 / CHUNK_SIZE,
|
|
||||||
8 * 16 / CHUNK_SIZE,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn generate() -> Self {
|
pub fn new() -> Self {
|
||||||
|
let chunks = HashMap::new();
|
||||||
let npc = Npc::load();
|
let npc = Npc::load();
|
||||||
let half: Vector3<isize> = WORLD_SIZE.cast().unwrap() / 2;
|
|
||||||
|
|
||||||
let coords: Vec<_> =
|
let chunk_database = sled::Config::new()
|
||||||
itertools::iproduct!(-half.x..half.x, 0..WORLD_SIZE.y as isize, -half.z..half.z)
|
.path("chunks")
|
||||||
.collect();
|
.mode(sled::Mode::HighThroughput)
|
||||||
|
.use_compression(true)
|
||||||
|
.open()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let chunks: HashMap<_, _> = coords
|
Self {
|
||||||
.par_iter()
|
chunks,
|
||||||
.map(|&(x, y, z)| {
|
npc,
|
||||||
(
|
|
||||||
Point3::new(x, y, z),
|
|
||||||
Chunk::generate(x as i32, y as i32, z as i32),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Self { chunks, npc }
|
chunk_database,
|
||||||
|
chunk_load_queue: VecDeque::new(),
|
||||||
|
chunk_save_queue: VecDeque::new(),
|
||||||
|
chunk_generate_queue: VecDeque::new(),
|
||||||
|
chunk_buffers: AHashMap::new(),
|
||||||
|
|
||||||
|
highlighted: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, render_context: &RenderContext, camera: &Camera) {
|
||||||
|
if let Some(position) = self.chunk_load_queue.pop_front() {
|
||||||
|
let chunk = self.chunks.entry(position).or_default();
|
||||||
|
match chunk.load(position, &self.chunk_database) {
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("Failed to load/generate chunk {:?}: {:?}", position, error)
|
||||||
|
}
|
||||||
|
Ok(true) => {
|
||||||
|
self.update_chunk_geometry(render_context, position);
|
||||||
|
self.chunk_save_queue.push_back(position);
|
||||||
|
// println!("Generated chunk {:?}", position);
|
||||||
|
}
|
||||||
|
Ok(false) => {
|
||||||
|
self.update_chunk_geometry(render_context, position);
|
||||||
|
// println!("Loaded chunk {:?}", position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(position) = self.chunk_save_queue.pop_front() {
|
||||||
|
let chunk = self.chunks.get(&position).unwrap();
|
||||||
|
if let Err(err) = chunk.save(position, &self.chunk_database) {
|
||||||
|
eprintln!("Failed to save chunk {:?}: {:?}", position, err);
|
||||||
|
} else {
|
||||||
|
// println!("Saved chunk {:?}", position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load new chunks, if necessary
|
||||||
|
let camera_pos: Point3<isize> = camera.position.cast().unwrap();
|
||||||
|
let camera_chunk: Point3<isize> = camera_pos.map(|n| n.div_euclid(CHUNK_ISIZE));
|
||||||
|
let mut load_queue = Vec::new();
|
||||||
|
for (x, y, z) in itertools::iproduct!(
|
||||||
|
-RENDER_DISTANCE..RENDER_DISTANCE,
|
||||||
|
0..WORLD_HEIGHT,
|
||||||
|
-RENDER_DISTANCE..RENDER_DISTANCE
|
||||||
|
) {
|
||||||
|
let point: Point3<isize> = Point3::new(x + camera_chunk.x, y, z + camera_chunk.z);
|
||||||
|
if !self.chunks.contains_key(&point) && !self.chunk_load_queue.contains(&point) {
|
||||||
|
load_queue.push(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Sort based on where camera is looking
|
||||||
|
load_queue.sort_unstable_by_key(|f| {
|
||||||
|
(f.x * CHUNK_ISIZE - camera_pos.x).abs() + (f.y * CHUNK_ISIZE - camera_pos.y).abs()
|
||||||
|
});
|
||||||
|
|
||||||
|
self.chunk_load_queue.extend(load_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_chunk_geometry(
|
||||||
|
&mut self,
|
||||||
|
render_context: &RenderContext,
|
||||||
|
chunk_position: Point3<isize>,
|
||||||
|
) {
|
||||||
|
let chunk = &mut self.chunks.get(&chunk_position).unwrap();
|
||||||
|
let offset = chunk_position * CHUNK_ISIZE;
|
||||||
|
let geometry = chunk.to_geometry(
|
||||||
|
offset,
|
||||||
|
World::highlighted_for_chunk(self.highlighted, &chunk_position).as_ref(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let buffers =
|
||||||
|
GeometryBuffers::from_geometry(render_context, &geometry, BufferUsage::empty());
|
||||||
|
self.chunk_buffers.insert(chunk_position, buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn highlighted_for_chunk(
|
pub fn highlighted_for_chunk(
|
||||||
|
@ -105,16 +184,21 @@ impl World {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_block(&mut self, x: isize, y: isize, z: isize, block: Option<Block>) {
|
pub fn set_block(&mut self, x: isize, y: isize, z: isize, block: Option<Block>) {
|
||||||
if let Some(chunk) = self.chunks.get_mut(&Point3::new(
|
let chunk_position = Point3::new(
|
||||||
x.div_euclid(CHUNK_ISIZE),
|
x.div_euclid(CHUNK_ISIZE),
|
||||||
y.div_euclid(CHUNK_ISIZE),
|
y.div_euclid(CHUNK_ISIZE),
|
||||||
z.div_euclid(CHUNK_ISIZE),
|
z.div_euclid(CHUNK_ISIZE),
|
||||||
)) {
|
);
|
||||||
|
|
||||||
|
if let Some(chunk) = self.chunks.get_mut(&chunk_position) {
|
||||||
let bx = x.rem_euclid(CHUNK_ISIZE) as usize;
|
let bx = x.rem_euclid(CHUNK_ISIZE) as usize;
|
||||||
let by = y.rem_euclid(CHUNK_ISIZE) as usize;
|
let by = y.rem_euclid(CHUNK_ISIZE) as usize;
|
||||||
let bz = z.rem_euclid(CHUNK_ISIZE) as usize;
|
let bz = z.rem_euclid(CHUNK_ISIZE) as usize;
|
||||||
chunk.blocks[by][bz][bx] = block;
|
chunk.blocks[by][bz][bx] = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.chunk_save_queue
|
||||||
|
.push_back(chunk_position / CHUNK_ISIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_scale(vector: Vector3<f32>, scalar: f32) -> f32 {
|
fn calc_scale(vector: Vector3<f32>, scalar: f32) -> f32 {
|
||||||
|
|
Loading…
Reference in a new issue