mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added chonks
Former-commit-id: a62fb321dbfb7541feaa9de4e641db9887b061fd
This commit is contained in:
parent
0688151afd
commit
f82c5eab42
@ -46,7 +46,7 @@ pub struct Client {
|
|||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
view_distance: u64,
|
view_distance: u64,
|
||||||
|
|
||||||
pending_chunks: HashMap<Vec3<i32>, Instant>,
|
pending_chunks: HashMap<Vec2<i32>, Instant>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
@ -221,18 +221,16 @@ impl Client {
|
|||||||
'outer: for dist in 0..10 {
|
'outer: for dist in 0..10 {
|
||||||
for i in chunk_pos.x - dist..chunk_pos.x + dist + 1 {
|
for i in chunk_pos.x - dist..chunk_pos.x + dist + 1 {
|
||||||
for j in chunk_pos.y - dist..chunk_pos.y + dist + 1 {
|
for j in chunk_pos.y - dist..chunk_pos.y + dist + 1 {
|
||||||
for k in 0..6 {
|
let key = Vec2::new(i, j);
|
||||||
let key = Vec3::new(i, j, k);
|
if self.state.terrain().get_key(key).is_none()
|
||||||
if self.state.terrain().get_key(key).is_none()
|
&& !self.pending_chunks.contains_key(&key)
|
||||||
&& !self.pending_chunks.contains_key(&key)
|
{
|
||||||
{
|
if self.pending_chunks.len() < 4 {
|
||||||
if self.pending_chunks.len() < 4 {
|
self.postbox
|
||||||
self.postbox
|
.send_message(ClientMsg::TerrainChunkRequest { key });
|
||||||
.send_message(ClientMsg::TerrainChunkRequest { key });
|
self.pending_chunks.insert(key, Instant::now());
|
||||||
self.pending_chunks.insert(key, Instant::now());
|
} else {
|
||||||
} else {
|
break 'outer;
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ pub enum ClientMsg {
|
|||||||
dir: comp::phys::Dir,
|
dir: comp::phys::Dir,
|
||||||
},
|
},
|
||||||
TerrainChunkRequest {
|
TerrainChunkRequest {
|
||||||
key: Vec3<i32>,
|
key: Vec2<i32>,
|
||||||
},
|
},
|
||||||
Disconnect,
|
Disconnect,
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ pub enum ServerMsg {
|
|||||||
animation_history: comp::AnimationHistory,
|
animation_history: comp::AnimationHistory,
|
||||||
},
|
},
|
||||||
TerrainChunkUpdate {
|
TerrainChunkUpdate {
|
||||||
key: Vec3<i32>,
|
key: Vec2<i32>,
|
||||||
chunk: Box<TerrainChunk>,
|
chunk: Box<TerrainChunk>,
|
||||||
},
|
},
|
||||||
Disconnect,
|
Disconnect,
|
||||||
|
@ -42,9 +42,9 @@ pub struct DeltaTime(pub f32);
|
|||||||
const MAX_DELTA_TIME: f32 = 0.15;
|
const MAX_DELTA_TIME: f32 = 0.15;
|
||||||
|
|
||||||
pub struct Changes {
|
pub struct Changes {
|
||||||
pub new_chunks: HashSet<Vec3<i32>>,
|
pub new_chunks: HashSet<Vec2<i32>>,
|
||||||
pub changed_chunks: HashSet<Vec3<i32>>,
|
pub changed_chunks: HashSet<Vec2<i32>>,
|
||||||
pub removed_chunks: HashSet<Vec3<i32>>,
|
pub removed_chunks: HashSet<Vec2<i32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Changes {
|
impl Changes {
|
||||||
@ -181,7 +181,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert the provided chunk into this state's terrain.
|
/// Insert the provided chunk into this state's terrain.
|
||||||
pub fn insert_chunk(&mut self, key: Vec3<i32>, chunk: TerrainChunk) {
|
pub fn insert_chunk(&mut self, key: Vec2<i32>, chunk: TerrainChunk) {
|
||||||
if self
|
if self
|
||||||
.ecs
|
.ecs
|
||||||
.write_resource::<TerrainMap>()
|
.write_resource::<TerrainMap>()
|
||||||
@ -195,7 +195,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the chunk with the given key from this state's terrain, if it exists
|
/// Remove the chunk with the given key from this state's terrain, if it exists
|
||||||
pub fn remove_chunk(&mut self, key: Vec3<i32>) {
|
pub fn remove_chunk(&mut self, key: Vec2<i32>) {
|
||||||
if self
|
if self
|
||||||
.ecs
|
.ecs
|
||||||
.write_resource::<TerrainMap>()
|
.write_resource::<TerrainMap>()
|
||||||
|
@ -4,7 +4,7 @@ use vek::*;
|
|||||||
// Crate
|
// Crate
|
||||||
use crate::vol::Vox;
|
use crate::vol::Vox;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
kind: u8,
|
kind: u8,
|
||||||
color: [u8; 3],
|
color: [u8; 3],
|
||||||
|
139
common/src/terrain/chonk.rs
Normal file
139
common/src/terrain/chonk.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
use vek::*;
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use crate::{
|
||||||
|
vol::{
|
||||||
|
BaseVol,
|
||||||
|
ReadVol,
|
||||||
|
WriteVol,
|
||||||
|
VolSize,
|
||||||
|
},
|
||||||
|
volumes::chunk::{Chunk, ChunkErr},
|
||||||
|
};
|
||||||
|
use super::{
|
||||||
|
block::Block,
|
||||||
|
TerrainChunkSize,
|
||||||
|
TerrainChunkMeta,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ChonkError {
|
||||||
|
ChunkError(ChunkErr),
|
||||||
|
OutOfBounds,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Chonk {
|
||||||
|
z_offset: i32,
|
||||||
|
sub_chunks: Vec<SubChunk>,
|
||||||
|
below: Block,
|
||||||
|
above: Block,
|
||||||
|
meta: TerrainChunkMeta,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chonk {
|
||||||
|
pub fn new(z_offset: i32, below: Block, above: Block, meta: TerrainChunkMeta) -> Self {
|
||||||
|
Self {
|
||||||
|
z_offset,
|
||||||
|
sub_chunks: Vec::new(),
|
||||||
|
below,
|
||||||
|
above,
|
||||||
|
meta,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub_chunk_idx(&self, z: i32) -> usize {
|
||||||
|
((z - self.z_offset) as u32 / TerrainChunkSize::SIZE.z as u32) as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BaseVol for Chonk {
|
||||||
|
type Vox = Block;
|
||||||
|
type Err = ChonkError;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ReadVol for Chonk {
|
||||||
|
#[inline(always)]
|
||||||
|
fn get(&self, pos: Vec3<i32>) -> Result<&Block, ChonkError> {
|
||||||
|
if pos.z < self.z_offset {
|
||||||
|
// Below the terrain
|
||||||
|
Ok(&self.below)
|
||||||
|
} else if pos.z >= self.z_offset + TerrainChunkSize::SIZE.z as i32 * self.sub_chunks.len() as i32 {
|
||||||
|
// Above the terrain
|
||||||
|
Ok(&self.above)
|
||||||
|
} else {
|
||||||
|
// Within the terrain
|
||||||
|
|
||||||
|
let sub_chunk_idx = self.sub_chunk_idx(pos.z);
|
||||||
|
|
||||||
|
match &self.sub_chunks[sub_chunk_idx] { // Can't fail
|
||||||
|
SubChunk::Homogeneous(block) => Ok(block),
|
||||||
|
SubChunk::Heterogeneous(chunk) => {
|
||||||
|
let rpos = pos - Vec3::unit_z() * (
|
||||||
|
self.z_offset +
|
||||||
|
sub_chunk_idx as i32 * TerrainChunkSize::SIZE.z as i32
|
||||||
|
);
|
||||||
|
chunk
|
||||||
|
.get(rpos)
|
||||||
|
.map_err(|err| ChonkError::ChunkError(err))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WriteVol for Chonk {
|
||||||
|
#[inline(always)]
|
||||||
|
fn set(&mut self, pos: Vec3<i32>, block: Block) -> Result<(), ChonkError> {
|
||||||
|
if pos.z < self.z_offset {
|
||||||
|
Err(ChonkError::OutOfBounds)
|
||||||
|
} else {
|
||||||
|
let sub_chunk_idx = self.sub_chunk_idx(pos.z);
|
||||||
|
|
||||||
|
while self.sub_chunks.get(sub_chunk_idx).is_none() {
|
||||||
|
self.sub_chunks.push(SubChunk::Homogeneous(self.above));
|
||||||
|
}
|
||||||
|
|
||||||
|
let rpos = pos - Vec3::unit_z() * (
|
||||||
|
self.z_offset +
|
||||||
|
sub_chunk_idx as i32 * TerrainChunkSize::SIZE.z as i32
|
||||||
|
);
|
||||||
|
|
||||||
|
match &mut self.sub_chunks[sub_chunk_idx] { // Can't fail
|
||||||
|
SubChunk::Homogeneous(cblock) if *cblock == block => Ok(()),
|
||||||
|
SubChunk::Homogeneous(cblock) => {
|
||||||
|
let mut new_chunk = Chunk::filled(*cblock, ());
|
||||||
|
match new_chunk
|
||||||
|
.set(rpos, block)
|
||||||
|
.map_err(|err| {
|
||||||
|
println!("Error!! {:?}", rpos);
|
||||||
|
ChonkError::ChunkError(err)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Ok(()) => {
|
||||||
|
self.sub_chunks[sub_chunk_idx] = SubChunk::Heterogeneous(new_chunk);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
SubChunk::Heterogeneous(chunk) => chunk
|
||||||
|
.set(rpos, block)
|
||||||
|
.map_err(|err| ChonkError::ChunkError(err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum SubChunk {
|
||||||
|
Homogeneous(Block),
|
||||||
|
Heterogeneous(Chunk<Block, TerrainChunkSize, ()>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubChunk {
|
||||||
|
pub fn filled(block: Block) -> Self {
|
||||||
|
SubChunk::Homogeneous(block)
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
pub mod biome;
|
pub mod biome;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
|
pub mod chonk;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use self::{biome::BiomeKind, block::Block};
|
pub use self::{biome::BiomeKind, block::Block};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vol::VolSize,
|
vol::VolSize,
|
||||||
volumes::{chunk::Chunk, vol_map::VolMap},
|
volumes::{chunk::Chunk, vol_map_2d::VolMap2d},
|
||||||
};
|
};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -41,5 +42,5 @@ impl TerrainChunkMeta {
|
|||||||
|
|
||||||
// Terrain type aliases
|
// Terrain type aliases
|
||||||
|
|
||||||
pub type TerrainChunk = Chunk<Block, TerrainChunkSize, TerrainChunkMeta>;
|
pub type TerrainChunk = chonk::Chonk; //Chunk<Block, TerrainChunkSize, TerrainChunkMeta>;
|
||||||
pub type TerrainMap = VolMap<Block, TerrainChunkSize, TerrainChunkMeta>;
|
pub type TerrainMap = VolMap2d<TerrainChunk, TerrainChunkSize>;
|
||||||
|
@ -74,10 +74,7 @@ pub trait ReadVol: BaseVol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A volume that provides the ability to sample (i.e: clone a section of) its voxel data.
|
/// A volume that provides the ability to sample (i.e: clone a section of) its voxel data.
|
||||||
pub trait SampleVol: BaseVol
|
pub trait SampleVol<I>: BaseVol {
|
||||||
where
|
|
||||||
Self::Vox: Clone,
|
|
||||||
{
|
|
||||||
type Sample: BaseVol + ReadVol;
|
type Sample: BaseVol + ReadVol;
|
||||||
/// Take a sample of the volume by cloning voxels within the provided range.
|
/// Take a sample of the volume by cloning voxels within the provided range.
|
||||||
///
|
///
|
||||||
@ -86,7 +83,7 @@ where
|
|||||||
///
|
///
|
||||||
/// Note that the resultant volume has a coordinate space relative to the sample, not the
|
/// Note that the resultant volume has a coordinate space relative to the sample, not the
|
||||||
/// original volume.
|
/// original volume.
|
||||||
fn sample(&self, range: Aabb<i32>) -> Result<Self::Sample, Self::Err>;
|
fn sample(&self, range: I) -> Result<Self::Sample, Self::Err>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A volume that provides write access to its voxel data.
|
/// A volume that provides write access to its voxel data.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
pub mod chunk;
|
pub mod chunk;
|
||||||
pub mod dyna;
|
pub mod dyna;
|
||||||
pub mod vol_map;
|
pub mod vol_map_3d;
|
||||||
|
pub mod vol_map_2d;
|
||||||
|
@ -1,227 +0,0 @@
|
|||||||
// Standard
|
|
||||||
use std::{collections::HashMap, sync::Arc};
|
|
||||||
|
|
||||||
// Library
|
|
||||||
use vek::*;
|
|
||||||
|
|
||||||
// Crate
|
|
||||||
use crate::{
|
|
||||||
terrain::TerrainChunkMeta,
|
|
||||||
vol::{BaseVol, ReadVol, SampleVol, SizedVol, VolSize, Vox, WriteVol},
|
|
||||||
volumes::{
|
|
||||||
chunk::{Chunk, ChunkErr},
|
|
||||||
dyna::{Dyna, DynaErr},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum VolMapErr {
|
|
||||||
NoSuchChunk,
|
|
||||||
ChunkErr(ChunkErr),
|
|
||||||
DynaErr(DynaErr),
|
|
||||||
InvalidChunkSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
// V = Voxel
|
|
||||||
// S = Size (replace with a const when const generics is a thing)
|
|
||||||
// M = Chunk metadata
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct VolMap<V: Vox, S: VolSize, M> {
|
|
||||||
chunks: HashMap<Vec3<i32>, Arc<Chunk<V, S, M>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Vox, S: VolSize, M> VolMap<V, S, M> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn chunk_key(pos: Vec3<i32>) -> Vec3<i32> {
|
|
||||||
pos.map2(S::SIZE, |e, sz| {
|
|
||||||
// Horrid, but it's faster than a cheetah with a red bull blood transfusion
|
|
||||||
let log2 = (sz - 1).count_ones();
|
|
||||||
((((e as i64 + (1 << 32)) as u64) >> log2) - (1 << (32 - log2))) as i32
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn chunk_offs(pos: Vec3<i32>) -> Vec3<i32> {
|
|
||||||
pos.map2(S::SIZE, |e, sz| {
|
|
||||||
// Horrid, but it's even faster than the aforementioned cheetah
|
|
||||||
(((e as i64 + (1 << 32)) as u64) & (sz - 1) as u64) as i32
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Vox, S: VolSize, M> BaseVol for VolMap<V, S, M> {
|
|
||||||
type Vox = V;
|
|
||||||
type Err = VolMapErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Vox, S: VolSize, M> ReadVol for VolMap<V, S, M> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn get(&self, pos: Vec3<i32>) -> Result<&V, VolMapErr> {
|
|
||||||
let ck = Self::chunk_key(pos);
|
|
||||||
self.chunks
|
|
||||||
.get(&ck)
|
|
||||||
.ok_or(VolMapErr::NoSuchChunk)
|
|
||||||
.and_then(|chunk| {
|
|
||||||
let co = Self::chunk_offs(pos);
|
|
||||||
chunk.get(co).map_err(|err| VolMapErr::ChunkErr(err))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Vox + Clone, S: VolSize, M: Clone> SampleVol for VolMap<V, S, M> {
|
|
||||||
type Sample = VolMap<V, S, M>;
|
|
||||||
|
|
||||||
/// Take a sample of the terrain by cloning the voxels within the provided range.
|
|
||||||
///
|
|
||||||
/// Note that the resultant volume does not carry forward metadata from the original chunks.
|
|
||||||
fn sample(&self, range: Aabb<i32>) -> Result<Self::Sample, VolMapErr> {
|
|
||||||
// Return early if we don't have all the needed chunks that we need!
|
|
||||||
/*
|
|
||||||
let min_chunk = Self::chunk_key(range.min);
|
|
||||||
let max_chunk = Self::chunk_key(range.max - Vec3::one());
|
|
||||||
for x in min_chunk.x..=max_chunk.x {
|
|
||||||
for y in min_chunk.y..=max_chunk.y {
|
|
||||||
for z in min_chunk.z..=max_chunk.z {
|
|
||||||
if self.chunks.get(&Vec3::new(x, y, z)).is_none() {
|
|
||||||
return Err(VolMapErr::NoSuchChunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// let mut sample = Dyna::filled(range.size().map(|e| e as u32).into(), V::empty(), ());
|
|
||||||
|
|
||||||
// let mut last_chunk_pos = self.pos_key(range.min);
|
|
||||||
// let mut last_chunk = self.get_key(last_chunk_pos);
|
|
||||||
|
|
||||||
// for pos in sample.iter_positions() {
|
|
||||||
// let new_chunk_pos = self.pos_key(range.min + pos);
|
|
||||||
// if last_chunk_pos != new_chunk_pos {
|
|
||||||
// last_chunk = self.get_key(new_chunk_pos);
|
|
||||||
// last_chunk_pos = new_chunk_pos;
|
|
||||||
// }
|
|
||||||
// sample
|
|
||||||
// .set(
|
|
||||||
// pos,
|
|
||||||
// if let Some(chunk) = last_chunk {
|
|
||||||
// chunk
|
|
||||||
// .get(Self::chunk_offs(range.min + pos))
|
|
||||||
// .map(|v| v.clone())
|
|
||||||
// .unwrap_or(V::empty())
|
|
||||||
// // Fallback in case the chunk doesn't exist
|
|
||||||
// } else {
|
|
||||||
// self.get(range.min + pos)
|
|
||||||
// .map(|v| v.clone())
|
|
||||||
// .unwrap_or(V::empty())
|
|
||||||
// },
|
|
||||||
// )
|
|
||||||
// .map_err(|err| VolMapErr::DynaErr(err))?;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Ok(sample)
|
|
||||||
|
|
||||||
let mut sample = VolMap::new()?;
|
|
||||||
let chunk_min = Self::chunk_key(range.min);
|
|
||||||
let chunk_max = Self::chunk_key(range.max);
|
|
||||||
for x in chunk_min.x..=chunk_max.x {
|
|
||||||
for y in chunk_min.y..=chunk_max.y {
|
|
||||||
for z in chunk_min.z..=chunk_max.z {
|
|
||||||
let chunk_key = Vec3::new(x, y, z);
|
|
||||||
|
|
||||||
let chunk = self.get_key_arc(chunk_key).map(|v| v.clone());
|
|
||||||
|
|
||||||
if let Some(chunk) = chunk {
|
|
||||||
sample.insert(chunk_key, chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(sample)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Vox + Clone, S: VolSize + Clone, M: Clone> WriteVol for VolMap<V, S, M> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn set(&mut self, pos: Vec3<i32>, vox: V) -> Result<(), VolMapErr> {
|
|
||||||
let ck = Self::chunk_key(pos);
|
|
||||||
self.chunks
|
|
||||||
.get_mut(&ck)
|
|
||||||
.ok_or(VolMapErr::NoSuchChunk)
|
|
||||||
.and_then(|chunk| {
|
|
||||||
let co = Self::chunk_offs(pos);
|
|
||||||
Arc::make_mut(chunk)
|
|
||||||
.set(co, vox)
|
|
||||||
.map_err(|err| VolMapErr::ChunkErr(err))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Vox, S: VolSize, M> VolMap<V, S, M> {
|
|
||||||
pub fn new() -> Result<Self, VolMapErr> {
|
|
||||||
if Self::chunk_size()
|
|
||||||
.map(|e| e.is_power_of_two() && e > 0)
|
|
||||||
.reduce_and()
|
|
||||||
{
|
|
||||||
Ok(Self {
|
|
||||||
chunks: HashMap::new(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(VolMapErr::InvalidChunkSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chunk_size() -> Vec3<u32> {
|
|
||||||
S::SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(
|
|
||||||
&mut self,
|
|
||||||
key: Vec3<i32>,
|
|
||||||
chunk: Arc<Chunk<V, S, M>>,
|
|
||||||
) -> Option<Arc<Chunk<V, S, M>>> {
|
|
||||||
self.chunks.insert(key, chunk)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_key(&self, key: Vec3<i32>) -> Option<&Chunk<V, S, M>> {
|
|
||||||
match self.chunks.get(&key) {
|
|
||||||
Some(arc_chunk) => Some(arc_chunk.as_ref()),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_key_arc(&self, key: Vec3<i32>) -> Option<&Arc<Chunk<V, S, M>>> {
|
|
||||||
self.chunks.get(&key)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(&mut self, key: Vec3<i32>) -> Option<Arc<Chunk<V, S, M>>> {
|
|
||||||
self.chunks.remove(&key)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn key_pos(&self, key: Vec3<i32>) -> Vec3<i32> {
|
|
||||||
key * S::SIZE.map(|e| e as i32)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pos_key(&self, pos: Vec3<i32>) -> Vec3<i32> {
|
|
||||||
Self::chunk_key(pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter<'a>(&'a self) -> ChunkIter<'a, V, S, M> {
|
|
||||||
ChunkIter {
|
|
||||||
iter: self.chunks.iter(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ChunkIter<'a, V: Vox, S: VolSize, M> {
|
|
||||||
iter: std::collections::hash_map::Iter<'a, Vec3<i32>, Arc<Chunk<V, S, M>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, V: Vox, S: VolSize, M> Iterator for ChunkIter<'a, V, S, M> {
|
|
||||||
type Item = (Vec3<i32>, &'a Arc<Chunk<V, S, M>>);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.iter.next().map(|(k, c)| (*k, c))
|
|
||||||
}
|
|
||||||
}
|
|
185
common/src/volumes/vol_map_2d.rs
Normal file
185
common/src/volumes/vol_map_2d.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::Arc,
|
||||||
|
marker::PhantomData,
|
||||||
|
};
|
||||||
|
use vek::*;
|
||||||
|
use crate::{
|
||||||
|
terrain::TerrainChunkMeta,
|
||||||
|
vol::{BaseVol, ReadVol, SampleVol, SizedVol, VolSize, Vox, WriteVol},
|
||||||
|
volumes::{
|
||||||
|
chunk::{Chunk, ChunkErr},
|
||||||
|
dyna::{Dyna, DynaErr},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum VolMap2dErr<V: BaseVol> {
|
||||||
|
NoSuchChunk,
|
||||||
|
ChunkErr(V::Err),
|
||||||
|
DynaErr(DynaErr),
|
||||||
|
InvalidChunkSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// V = Voxel
|
||||||
|
// S = Size (replace with a const when const generics is a thing)
|
||||||
|
// M = Chunk metadata
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct VolMap2d<V: BaseVol, S: VolSize> {
|
||||||
|
chunks: HashMap<Vec2<i32>, Arc<V>>,
|
||||||
|
phantom: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: BaseVol, S: VolSize> VolMap2d<V, S> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn chunk_key<P: Into<Vec2<i32>>>(pos: P) -> Vec2<i32> {
|
||||||
|
pos.into().map2(S::SIZE.into(), |e, sz: u32| {
|
||||||
|
// Horrid, but it's faster than a cheetah with a red bull blood transfusion
|
||||||
|
let log2 = (sz - 1).count_ones();
|
||||||
|
((((e as i64 + (1 << 32)) as u64) >> log2) - (1 << (32 - log2))) as i32
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn chunk_offs(pos: Vec3<i32>) -> Vec3<i32> {
|
||||||
|
let offs = pos.map2(S::SIZE, |e, sz| {
|
||||||
|
// Horrid, but it's even faster than the aforementioned cheetah
|
||||||
|
(((e as i64 + (1 << 32)) as u64) & (sz - 1) as u64) as i32
|
||||||
|
});
|
||||||
|
Vec3::new(offs.x, offs.y, pos.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: BaseVol, S: VolSize> BaseVol for VolMap2d<V, S> {
|
||||||
|
type Vox = V::Vox;
|
||||||
|
type Err = VolMap2dErr<V>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: BaseVol + ReadVol, S: VolSize> ReadVol for VolMap2d<V, S> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn get(&self, pos: Vec3<i32>) -> Result<&V::Vox, VolMap2dErr<V>> {
|
||||||
|
let ck = Self::chunk_key(pos);
|
||||||
|
self.chunks
|
||||||
|
.get(&ck)
|
||||||
|
.ok_or(VolMap2dErr::NoSuchChunk)
|
||||||
|
.and_then(|chunk| {
|
||||||
|
let co = Self::chunk_offs(pos);
|
||||||
|
chunk.get(co).map_err(|err| VolMap2dErr::ChunkErr(err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This actually breaks the API: samples are supposed to have an offset of zero!
|
||||||
|
// TODO: Should this be changed, perhaps?
|
||||||
|
impl<I: Into<Aabr<i32>>, V: BaseVol + ReadVol, S: VolSize> SampleVol<I> for VolMap2d<V, S> {
|
||||||
|
type Sample = VolMap2d<V, S>;
|
||||||
|
|
||||||
|
/// Take a sample of the terrain by cloning the voxels within the provided range.
|
||||||
|
///
|
||||||
|
/// Note that the resultant volume does not carry forward metadata from the original chunks.
|
||||||
|
fn sample(&self, range: I) -> Result<Self::Sample, VolMap2dErr<V>> {
|
||||||
|
let range = range.into();
|
||||||
|
|
||||||
|
let mut sample = VolMap2d::new()?;
|
||||||
|
let chunk_min = Self::chunk_key(range.min);
|
||||||
|
let chunk_max = Self::chunk_key(range.max);
|
||||||
|
for x in chunk_min.x..=chunk_max.x {
|
||||||
|
for y in chunk_min.y..=chunk_max.y {
|
||||||
|
let chunk_key = Vec2::new(x, y);
|
||||||
|
|
||||||
|
let chunk = self.get_key_arc(chunk_key).map(|v| v.clone());
|
||||||
|
|
||||||
|
if let Some(chunk) = chunk {
|
||||||
|
sample.insert(chunk_key, chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(sample)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: BaseVol + WriteVol + Clone, S: VolSize + Clone> WriteVol for VolMap2d<V, S> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn set(&mut self, pos: Vec3<i32>, vox: V::Vox) -> Result<(), VolMap2dErr<V>> {
|
||||||
|
let ck = Self::chunk_key(pos);
|
||||||
|
self.chunks
|
||||||
|
.get_mut(&ck)
|
||||||
|
.ok_or(VolMap2dErr::NoSuchChunk)
|
||||||
|
.and_then(|chunk| {
|
||||||
|
let co = Self::chunk_offs(pos);
|
||||||
|
Arc::make_mut(chunk)
|
||||||
|
.set(co, vox)
|
||||||
|
.map_err(|err| VolMap2dErr::ChunkErr(err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: BaseVol, S: VolSize> VolMap2d<V, S> {
|
||||||
|
pub fn new() -> Result<Self, VolMap2dErr<V>> {
|
||||||
|
if Self::chunk_size()
|
||||||
|
.map(|e| e.is_power_of_two() && e > 0)
|
||||||
|
.reduce_and()
|
||||||
|
{
|
||||||
|
Ok(Self {
|
||||||
|
chunks: HashMap::new(),
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(VolMap2dErr::InvalidChunkSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chunk_size() -> Vec2<u32> {
|
||||||
|
S::SIZE.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(
|
||||||
|
&mut self,
|
||||||
|
key: Vec2<i32>,
|
||||||
|
chunk: Arc<V>,
|
||||||
|
) -> Option<Arc<V>> {
|
||||||
|
self.chunks.insert(key, chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_key(&self, key: Vec2<i32>) -> Option<&V> {
|
||||||
|
match self.chunks.get(&key) {
|
||||||
|
Some(arc_chunk) => Some(arc_chunk.as_ref()),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_key_arc(&self, key: Vec2<i32>) -> Option<&Arc<V>> {
|
||||||
|
self.chunks.get(&key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, key: Vec2<i32>) -> Option<Arc<V>> {
|
||||||
|
self.chunks.remove(&key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn key_pos(&self, key: Vec2<i32>) -> Vec2<i32> {
|
||||||
|
key * Vec2::<u32>::from(S::SIZE).map(|e| e as i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos_key(&self, pos: Vec3<i32>) -> Vec2<i32> {
|
||||||
|
Self::chunk_key(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter<'a>(&'a self) -> ChunkIter<'a, V> {
|
||||||
|
ChunkIter {
|
||||||
|
iter: self.chunks.iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ChunkIter<'a, V: BaseVol> {
|
||||||
|
iter: std::collections::hash_map::Iter<'a, Vec2<i32>, Arc<V>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, V: BaseVol> Iterator for ChunkIter<'a, V> {
|
||||||
|
type Item = (Vec2<i32>, &'a Arc<V>);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.iter.next().map(|(k, c)| (*k, c))
|
||||||
|
}
|
||||||
|
}
|
185
common/src/volumes/vol_map_3d.rs
Normal file
185
common/src/volumes/vol_map_3d.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::Arc,
|
||||||
|
marker::PhantomData,
|
||||||
|
};
|
||||||
|
use vek::*;
|
||||||
|
use crate::{
|
||||||
|
terrain::TerrainChunkMeta,
|
||||||
|
vol::{BaseVol, ReadVol, SampleVol, SizedVol, VolSize, Vox, WriteVol},
|
||||||
|
volumes::{
|
||||||
|
chunk::{Chunk, ChunkErr},
|
||||||
|
dyna::{Dyna, DynaErr},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum VolMap3dErr<V: BaseVol> {
|
||||||
|
NoSuchChunk,
|
||||||
|
ChunkErr(V::Err),
|
||||||
|
DynaErr(DynaErr),
|
||||||
|
InvalidChunkSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// V = Voxel
|
||||||
|
// S = Size (replace with a const when const generics is a thing)
|
||||||
|
// M = Chunk metadata
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct VolMap3d<V: BaseVol, S: VolSize> {
|
||||||
|
chunks: HashMap<Vec3<i32>, Arc<V>>,
|
||||||
|
phantom: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: BaseVol, S: VolSize> VolMap3d<V, S> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn chunk_key(pos: Vec3<i32>) -> Vec3<i32> {
|
||||||
|
pos.map2(S::SIZE, |e, sz| {
|
||||||
|
// Horrid, but it's faster than a cheetah with a red bull blood transfusion
|
||||||
|
let log2 = (sz - 1).count_ones();
|
||||||
|
((((e as i64 + (1 << 32)) as u64) >> log2) - (1 << (32 - log2))) as i32
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn chunk_offs(pos: Vec3<i32>) -> Vec3<i32> {
|
||||||
|
pos.map2(S::SIZE, |e, sz| {
|
||||||
|
// Horrid, but it's even faster than the aforementioned cheetah
|
||||||
|
(((e as i64 + (1 << 32)) as u64) & (sz - 1) as u64) as i32
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: BaseVol, S: VolSize> BaseVol for VolMap3d<V, S> {
|
||||||
|
type Vox = V::Vox;
|
||||||
|
type Err = VolMap3dErr<V>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: BaseVol + ReadVol, S: VolSize> ReadVol for VolMap3d<V, S> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn get(&self, pos: Vec3<i32>) -> Result<&V::Vox, VolMap3dErr<V>> {
|
||||||
|
let ck = Self::chunk_key(pos);
|
||||||
|
self.chunks
|
||||||
|
.get(&ck)
|
||||||
|
.ok_or(VolMap3dErr::NoSuchChunk)
|
||||||
|
.and_then(|chunk| {
|
||||||
|
let co = Self::chunk_offs(pos);
|
||||||
|
chunk.get(co).map_err(|err| VolMap3dErr::ChunkErr(err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This actually breaks the API: samples are supposed to have an offset of zero!
|
||||||
|
// TODO: Should this be changed, perhaps?
|
||||||
|
impl<I: Into<Aabb<i32>>, V: BaseVol + ReadVol, S: VolSize> SampleVol<I> for VolMap3d<V, S> {
|
||||||
|
type Sample = VolMap3d<V, S>;
|
||||||
|
|
||||||
|
/// Take a sample of the terrain by cloning the voxels within the provided range.
|
||||||
|
///
|
||||||
|
/// Note that the resultant volume does not carry forward metadata from the original chunks.
|
||||||
|
fn sample(&self, range: I) -> Result<Self::Sample, VolMap3dErr<V>> {
|
||||||
|
let range = range.into();
|
||||||
|
let mut sample = VolMap3d::new()?;
|
||||||
|
let chunk_min = Self::chunk_key(range.min);
|
||||||
|
let chunk_max = Self::chunk_key(range.max);
|
||||||
|
for x in chunk_min.x..=chunk_max.x {
|
||||||
|
for y in chunk_min.y..=chunk_max.y {
|
||||||
|
for z in chunk_min.z..=chunk_max.z {
|
||||||
|
let chunk_key = Vec3::new(x, y, z);
|
||||||
|
|
||||||
|
let chunk = self.get_key_arc(chunk_key).map(|v| v.clone());
|
||||||
|
|
||||||
|
if let Some(chunk) = chunk {
|
||||||
|
sample.insert(chunk_key, chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(sample)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: BaseVol + WriteVol + Clone, S: VolSize + Clone> WriteVol for VolMap3d<V, S> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn set(&mut self, pos: Vec3<i32>, vox: V::Vox) -> Result<(), VolMap3dErr<V>> {
|
||||||
|
let ck = Self::chunk_key(pos);
|
||||||
|
self.chunks
|
||||||
|
.get_mut(&ck)
|
||||||
|
.ok_or(VolMap3dErr::NoSuchChunk)
|
||||||
|
.and_then(|chunk| {
|
||||||
|
let co = Self::chunk_offs(pos);
|
||||||
|
Arc::make_mut(chunk)
|
||||||
|
.set(co, vox)
|
||||||
|
.map_err(|err| VolMap3dErr::ChunkErr(err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: BaseVol, S: VolSize> VolMap3d<V, S> {
|
||||||
|
pub fn new() -> Result<Self, VolMap3dErr<V>> {
|
||||||
|
if Self::chunk_size()
|
||||||
|
.map(|e| e.is_power_of_two() && e > 0)
|
||||||
|
.reduce_and()
|
||||||
|
{
|
||||||
|
Ok(Self {
|
||||||
|
chunks: HashMap::new(),
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(VolMap3dErr::InvalidChunkSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chunk_size() -> Vec3<u32> {
|
||||||
|
S::SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(
|
||||||
|
&mut self,
|
||||||
|
key: Vec3<i32>,
|
||||||
|
chunk: Arc<V>,
|
||||||
|
) -> Option<Arc<V>> {
|
||||||
|
self.chunks.insert(key, chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_key(&self, key: Vec3<i32>) -> Option<&V> {
|
||||||
|
match self.chunks.get(&key) {
|
||||||
|
Some(arc_chunk) => Some(arc_chunk.as_ref()),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_key_arc(&self, key: Vec3<i32>) -> Option<&Arc<V>> {
|
||||||
|
self.chunks.get(&key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, key: Vec3<i32>) -> Option<Arc<V>> {
|
||||||
|
self.chunks.remove(&key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn key_pos(&self, key: Vec3<i32>) -> Vec3<i32> {
|
||||||
|
key * S::SIZE.map(|e| e as i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos_key(&self, pos: Vec3<i32>) -> Vec3<i32> {
|
||||||
|
Self::chunk_key(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter<'a>(&'a self) -> ChunkIter<'a, V> {
|
||||||
|
ChunkIter {
|
||||||
|
iter: self.chunks.iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ChunkIter<'a, V: BaseVol> {
|
||||||
|
iter: std::collections::hash_map::Iter<'a, Vec3<i32>, Arc<V>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, V: BaseVol> Iterator for ChunkIter<'a, V> {
|
||||||
|
type Item = (Vec3<i32>, &'a Arc<V>);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.iter.next().map(|(k, c)| (*k, c))
|
||||||
|
}
|
||||||
|
}
|
@ -44,9 +44,9 @@ pub struct Server {
|
|||||||
clients: Clients,
|
clients: Clients,
|
||||||
|
|
||||||
thread_pool: ThreadPool,
|
thread_pool: ThreadPool,
|
||||||
chunk_tx: mpsc::Sender<(Vec3<i32>, TerrainChunk)>,
|
chunk_tx: mpsc::Sender<(Vec2<i32>, TerrainChunk)>,
|
||||||
chunk_rx: mpsc::Receiver<(Vec3<i32>, TerrainChunk)>,
|
chunk_rx: mpsc::Receiver<(Vec2<i32>, TerrainChunk)>,
|
||||||
pending_chunks: HashSet<Vec3<i32>>,
|
pending_chunks: HashSet<Vec2<i32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
@ -555,7 +555,7 @@ impl Server {
|
|||||||
.clear();
|
.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_chunk(&mut self, key: Vec3<i32>) {
|
pub fn generate_chunk(&mut self, key: Vec2<i32>) {
|
||||||
if self.pending_chunks.insert(key) {
|
if self.pending_chunks.insert(key) {
|
||||||
let chunk_tx = self.chunk_tx.clone();
|
let chunk_tx = self.chunk_tx.clone();
|
||||||
self.thread_pool
|
self.thread_pool
|
||||||
|
@ -4,8 +4,8 @@ use vek::*;
|
|||||||
// Project
|
// Project
|
||||||
use common::{
|
use common::{
|
||||||
terrain::Block,
|
terrain::Block,
|
||||||
vol::{ReadVol, SizedVol, VolSize, Vox},
|
vol::{BaseVol, ReadVol, SizedVol, VolSize, Vox},
|
||||||
volumes::{dyna::Dyna, vol_map::VolMap},
|
volumes::{dyna::Dyna, vol_map_2d::VolMap2d, vol_map_3d::VolMap3d},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Crate
|
// Crate
|
||||||
@ -44,7 +44,71 @@ impl<M> Meshable for Dyna<Block, M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: VolSize + Clone, M: Clone> Meshable for VolMap<Block, S, M> {
|
impl<V: BaseVol<Vox=Block> + ReadVol, S: VolSize + Clone> Meshable for VolMap2d<V, S> {
|
||||||
|
type Pipeline = TerrainPipeline;
|
||||||
|
type Supplement = Aabb<i32>;
|
||||||
|
|
||||||
|
fn generate_mesh(&self, range: Self::Supplement) -> Mesh<Self::Pipeline> {
|
||||||
|
let mut mesh = Mesh::new();
|
||||||
|
|
||||||
|
let mut last_chunk_pos = self.pos_key(range.min);
|
||||||
|
let mut last_chunk = self.get_key(last_chunk_pos);
|
||||||
|
|
||||||
|
let size = range.max - range.min;
|
||||||
|
for x in 1..size.x - 1 {
|
||||||
|
for y in 1..size.y - 1 {
|
||||||
|
for z in 0..size.z {
|
||||||
|
let pos = Vec3::new(x, y, z);
|
||||||
|
|
||||||
|
let new_chunk_pos = self.pos_key(range.min + pos);
|
||||||
|
if last_chunk_pos != new_chunk_pos {
|
||||||
|
last_chunk = self.get_key(new_chunk_pos);
|
||||||
|
last_chunk_pos = new_chunk_pos;
|
||||||
|
}
|
||||||
|
let offs = pos.map(|e| e as f32) - Vec3::new(1.0, 1.0, 0.0);
|
||||||
|
if let Some(chunk) = last_chunk {
|
||||||
|
let chunk_pos = Self::chunk_offs(range.min + pos);
|
||||||
|
if let Some(col) = chunk.get(chunk_pos).ok().and_then(|vox| vox.get_color())
|
||||||
|
{
|
||||||
|
let col = col.map(|e| e as f32 / 255.0);
|
||||||
|
|
||||||
|
vol::push_vox_verts(
|
||||||
|
&mut mesh,
|
||||||
|
self,
|
||||||
|
range.min + pos,
|
||||||
|
offs,
|
||||||
|
col,
|
||||||
|
TerrainVertex::new,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(col) = self
|
||||||
|
.get(range.min + pos)
|
||||||
|
.ok()
|
||||||
|
.and_then(|vox| vox.get_color())
|
||||||
|
{
|
||||||
|
let col = col.map(|e| e as f32 / 255.0);
|
||||||
|
|
||||||
|
vol::push_vox_verts(
|
||||||
|
&mut mesh,
|
||||||
|
self,
|
||||||
|
range.min + pos,
|
||||||
|
offs,
|
||||||
|
col,
|
||||||
|
TerrainVertex::new,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mesh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: BaseVol<Vox=Block> + ReadVol, S: VolSize + Clone> Meshable for VolMap3d<V, S> {
|
||||||
type Pipeline = TerrainPipeline;
|
type Pipeline = TerrainPipeline;
|
||||||
type Supplement = Aabb<i32>;
|
type Supplement = Aabb<i32>;
|
||||||
|
|
||||||
|
@ -1,14 +1,7 @@
|
|||||||
// Standard
|
|
||||||
use std::{collections::HashMap, sync::mpsc, time::Duration};
|
use std::{collections::HashMap, sync::mpsc, time::Duration};
|
||||||
|
|
||||||
// Library
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
// Project
|
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use common::{terrain::TerrainMap, vol::SampleVol, volumes::vol_map::VolMapErr};
|
use common::{terrain::TerrainMap, vol::SampleVol, volumes::vol_map_2d::VolMap2dErr};
|
||||||
|
|
||||||
// Crate
|
|
||||||
use crate::{
|
use crate::{
|
||||||
mesh::Meshable,
|
mesh::Meshable,
|
||||||
render::{Consts, Globals, Mesh, Model, Renderer, TerrainLocals, TerrainPipeline},
|
render::{Consts, Globals, Mesh, Model, Renderer, TerrainLocals, TerrainPipeline},
|
||||||
@ -21,40 +14,40 @@ struct TerrainChunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ChunkMeshState {
|
struct ChunkMeshState {
|
||||||
pos: Vec3<i32>,
|
pos: Vec2<i32>,
|
||||||
started_tick: u64,
|
started_tick: u64,
|
||||||
active_worker: bool,
|
active_worker: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type produced by mesh worker threads corresponding to the position and mesh of a chunk
|
/// A type produced by mesh worker threads corresponding to the position and mesh of a chunk
|
||||||
struct MeshWorkerResponse {
|
struct MeshWorkerResponse {
|
||||||
pos: Vec3<i32>,
|
pos: Vec2<i32>,
|
||||||
mesh: Mesh<TerrainPipeline>,
|
mesh: Mesh<TerrainPipeline>,
|
||||||
started_tick: u64,
|
started_tick: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function executed by worker threads dedicated to chunk meshing
|
/// Function executed by worker threads dedicated to chunk meshing
|
||||||
fn mesh_worker(
|
fn mesh_worker(
|
||||||
pos: Vec3<i32>,
|
pos: Vec2<i32>,
|
||||||
started_tick: u64,
|
started_tick: u64,
|
||||||
volume: <TerrainMap as SampleVol>::Sample,
|
volume: <TerrainMap as SampleVol<Aabr<i32>>>::Sample,
|
||||||
supplement: Aabb<i32>,
|
range: Aabb<i32>,
|
||||||
) -> MeshWorkerResponse {
|
) -> MeshWorkerResponse {
|
||||||
MeshWorkerResponse {
|
MeshWorkerResponse {
|
||||||
pos,
|
pos,
|
||||||
mesh: volume.generate_mesh(supplement),
|
mesh: volume.generate_mesh(range),
|
||||||
started_tick,
|
started_tick,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Terrain {
|
pub struct Terrain {
|
||||||
chunks: HashMap<Vec3<i32>, TerrainChunk>,
|
chunks: HashMap<Vec2<i32>, TerrainChunk>,
|
||||||
|
|
||||||
// The mpsc sender and receiver used for talking to meshing worker threads.
|
// The mpsc sender and receiver used for talking to meshing worker threads.
|
||||||
// We keep the sender component for no reason othe than to clone it and send it to new workers.
|
// We keep the sender component for no reason othe than to clone it and send it to new workers.
|
||||||
mesh_send_tmp: mpsc::Sender<MeshWorkerResponse>,
|
mesh_send_tmp: mpsc::Sender<MeshWorkerResponse>,
|
||||||
mesh_recv: mpsc::Receiver<MeshWorkerResponse>,
|
mesh_recv: mpsc::Receiver<MeshWorkerResponse>,
|
||||||
mesh_todo: HashMap<Vec3<i32>, ChunkMeshState>,
|
mesh_todo: HashMap<Vec2<i32>, ChunkMeshState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Terrain {
|
impl Terrain {
|
||||||
@ -90,28 +83,26 @@ impl Terrain {
|
|||||||
// elision information changes too!
|
// elision information changes too!
|
||||||
for i in -1..2 {
|
for i in -1..2 {
|
||||||
for j in -1..2 {
|
for j in -1..2 {
|
||||||
for k in -1..2 {
|
let pos = pos + Vec2::new(i, j);
|
||||||
let pos = pos + Vec3::new(i, j, k);
|
|
||||||
|
|
||||||
if !self.chunks.contains_key(&pos) {
|
if !self.chunks.contains_key(&pos) {
|
||||||
let mut neighbours = true;
|
let mut neighbours = true;
|
||||||
for i in -1..2 {
|
for i in -1..2 {
|
||||||
for j in -1..2 {
|
for j in -1..2 {
|
||||||
neighbours &= client
|
neighbours &= client
|
||||||
.state()
|
.state()
|
||||||
.terrain()
|
.terrain()
|
||||||
.get_key(pos + Vec2::new(i, j))
|
.get_key(pos + Vec2::new(i, j))
|
||||||
.is_some();
|
.is_some();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if neighbours {
|
if neighbours {
|
||||||
self.mesh_todo.entry(pos).or_insert(ChunkMeshState {
|
self.mesh_todo.entry(pos).or_insert(ChunkMeshState {
|
||||||
pos,
|
pos,
|
||||||
started_tick: current_tick,
|
started_tick: current_tick,
|
||||||
active_worker: false,
|
active_worker: false,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,7 +123,7 @@ impl Terrain {
|
|||||||
// Find the area of the terrain we want. Because meshing needs to compute things like
|
// Find the area of the terrain we want. Because meshing needs to compute things like
|
||||||
// ambient occlusion and edge elision, we also need to borders of the chunk's
|
// ambient occlusion and edge elision, we also need to borders of the chunk's
|
||||||
// neighbours too (hence the `- 1` and `+ 1`).
|
// neighbours too (hence the `- 1` and `+ 1`).
|
||||||
let aabb = Aabb {
|
let aabr = Aabr {
|
||||||
min: todo
|
min: todo
|
||||||
.pos
|
.pos
|
||||||
.map2(TerrainMap::chunk_size(), |e, sz| e * sz as i32 - 1),
|
.map2(TerrainMap::chunk_size(), |e, sz| e * sz as i32 - 1),
|
||||||
@ -141,13 +132,18 @@ impl Terrain {
|
|||||||
.map2(TerrainMap::chunk_size(), |e, sz| (e + 1) * sz as i32 + 1),
|
.map2(TerrainMap::chunk_size(), |e, sz| (e + 1) * sz as i32 + 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let aabb = Aabb {
|
||||||
|
min: Vec3::from(aabr.min),
|
||||||
|
max: Vec3::from(aabr.max) + Vec3::unit_z() * 256,
|
||||||
|
};
|
||||||
|
|
||||||
// Copy out the chunk data we need to perform the meshing. We do this by taking a
|
// Copy out the chunk data we need to perform the meshing. We do this by taking a
|
||||||
// sample of the terrain that includes both the chunk we want and
|
// sample of the terrain that includes both the chunk we want and
|
||||||
let volume = match client.state().terrain().sample(aabb) {
|
let volume = match client.state().terrain().sample(aabr) {
|
||||||
Ok(sample) => sample,
|
Ok(sample) => sample,
|
||||||
// If either this chunk or its neighbours doesn't yet exist, so we keep it in the
|
// If either this chunk or its neighbours doesn't yet exist, so we keep it in the
|
||||||
// todo queue to be processed at a later date when we have its neighbours.
|
// todo queue to be processed at a later date when we have its neighbours.
|
||||||
Err(VolMapErr::NoSuchChunk) => return,
|
Err(VolMap2dErr::NoSuchChunk) => return,
|
||||||
_ => panic!("Unhandled edge case"),
|
_ => panic!("Unhandled edge case"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,11 +174,11 @@ impl Terrain {
|
|||||||
.expect("Failed to upload chunk mesh to the GPU"),
|
.expect("Failed to upload chunk mesh to the GPU"),
|
||||||
locals: renderer
|
locals: renderer
|
||||||
.create_consts(&[TerrainLocals {
|
.create_consts(&[TerrainLocals {
|
||||||
model_offs: response
|
model_offs: Vec3::from(response
|
||||||
.pos
|
.pos
|
||||||
.map2(TerrainMap::chunk_size(), |e, sz| {
|
.map2(TerrainMap::chunk_size(), |e, sz| {
|
||||||
e as f32 * sz as f32
|
e as f32 * sz as f32
|
||||||
})
|
}))
|
||||||
.into_array(),
|
.into_array(),
|
||||||
}])
|
}])
|
||||||
.expect("Failed to upload chunk locals to the GPU"),
|
.expect("Failed to upload chunk locals to the GPU"),
|
||||||
|
@ -4,8 +4,8 @@ use vek::*;
|
|||||||
|
|
||||||
// Project
|
// Project
|
||||||
use common::{
|
use common::{
|
||||||
terrain::{Block, TerrainChunk, TerrainChunkMeta},
|
terrain::{Block, TerrainChunk, TerrainChunkSize, TerrainChunkMeta},
|
||||||
vol::{SizedVol, Vox, WriteVol},
|
vol::{SizedVol, Vox, WriteVol, VolSize},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -20,60 +20,63 @@ impl World {
|
|||||||
Self
|
Self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_chunk(chunk_pos: Vec3<i32>) -> TerrainChunk {
|
pub fn generate_chunk(chunk_pos: Vec2<i32>) -> TerrainChunk {
|
||||||
// TODO: This is all test code, remove/improve this later
|
// TODO: This is all test code, remove/improve this later
|
||||||
|
|
||||||
let mut chunk = TerrainChunk::filled(Block::empty(), TerrainChunkMeta::void());
|
|
||||||
|
|
||||||
let air = Block::empty();
|
let air = Block::empty();
|
||||||
let stone = Block::new(1, Rgb::new(200, 220, 255));
|
let stone = Block::new(1, Rgb::new(200, 220, 255));
|
||||||
let grass = Block::new(2, Rgb::new(75, 150, 0));
|
let grass = Block::new(2, Rgb::new(75, 150, 0));
|
||||||
//let grass = Block::new(2, Rgb::new(50, 255, 0));
|
|
||||||
let dirt = Block::new(3, Rgb::new(128, 90, 0));
|
let dirt = Block::new(3, Rgb::new(128, 90, 0));
|
||||||
let sand = Block::new(4, Rgb::new(180, 150, 50));
|
let sand = Block::new(4, Rgb::new(180, 150, 50));
|
||||||
|
|
||||||
|
let mut chunk = TerrainChunk::new(0, stone, air, TerrainChunkMeta::void());
|
||||||
|
|
||||||
let perlin_nz = Perlin::new().set_seed(1);
|
let perlin_nz = Perlin::new().set_seed(1);
|
||||||
let temp_nz = Perlin::new().set_seed(2);
|
let temp_nz = Perlin::new().set_seed(2);
|
||||||
let chaos_nz = Perlin::new().set_seed(3);
|
let chaos_nz = Perlin::new().set_seed(3);
|
||||||
|
|
||||||
for lpos in chunk.iter_positions() {
|
for x in 0..TerrainChunkSize::SIZE.x as i32 {
|
||||||
let wpos = lpos + chunk_pos * chunk.get_size().map(|e| e as i32);
|
for y in 0..TerrainChunkSize::SIZE.y as i32 {
|
||||||
let wposf = wpos.map(|e| e as f64);
|
for z in 0..256 {
|
||||||
|
let lpos = Vec3::new(x, y, z);
|
||||||
|
let wpos = lpos + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32);
|
||||||
|
let wposf = wpos.map(|e| e as f64);
|
||||||
|
|
||||||
let chaos_freq = 1.0 / 100.0;
|
let chaos_freq = 1.0 / 100.0;
|
||||||
let freq = 1.0 / 128.0;
|
let freq = 1.0 / 128.0;
|
||||||
let ampl = 75.0;
|
let ampl = 75.0;
|
||||||
let small_freq = 1.0 / 32.0;
|
let small_freq = 1.0 / 32.0;
|
||||||
let small_ampl = 6.0;
|
let small_ampl = 6.0;
|
||||||
let offs = 32.0;
|
let offs = 32.0;
|
||||||
|
|
||||||
let chaos = chaos_nz
|
let chaos = chaos_nz
|
||||||
.get(Vec2::from(wposf * chaos_freq).into_array())
|
.get(Vec2::from(wposf * chaos_freq).into_array())
|
||||||
.max(0.0)
|
.max(0.0)
|
||||||
+ 0.5;
|
+ 0.5;
|
||||||
|
|
||||||
let height = perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl * chaos
|
let height = perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl * chaos
|
||||||
+ perlin_nz.get((wposf * small_freq).into_array())
|
+ perlin_nz.get((wposf * small_freq).into_array())
|
||||||
* small_ampl
|
* small_ampl
|
||||||
* 3.0
|
* 3.0
|
||||||
* chaos.powf(2.0)
|
* chaos.powf(2.0)
|
||||||
+ offs;
|
+ offs;
|
||||||
let temp = (temp_nz.get(Vec2::from(wposf * (1.0 / 64.0)).into_array()) + 1.0) * 0.5;
|
let temp = (temp_nz.get(Vec2::from(wposf * (1.0 / 64.0)).into_array()) + 1.0) * 0.5;
|
||||||
|
|
||||||
chunk
|
let _ = chunk
|
||||||
.set(
|
.set(
|
||||||
lpos,
|
lpos,
|
||||||
if wposf.z < height - 4.0 {
|
if wposf.z < height - 4.0 {
|
||||||
stone
|
stone
|
||||||
} else if wposf.z < height - 2.0 {
|
} else if wposf.z < height - 2.0 {
|
||||||
dirt
|
dirt
|
||||||
} else if wposf.z < height {
|
} else if wposf.z < height {
|
||||||
Block::new(2, Rgb::new(10 + (150.0 * temp) as u8, 150, 0))
|
Block::new(2, Rgb::new(10 + (150.0 * temp) as u8, 150, 0))
|
||||||
} else {
|
} else {
|
||||||
air
|
air
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
.unwrap();
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk
|
chunk
|
||||||
|
Loading…
x
Reference in New Issue
Block a user