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
fa052a22b7
commit
91184356e7
@ -46,7 +46,7 @@ pub struct Client {
|
||||
entity: EcsEntity,
|
||||
view_distance: u64,
|
||||
|
||||
pending_chunks: HashMap<Vec3<i32>, Instant>,
|
||||
pending_chunks: HashMap<Vec2<i32>, Instant>,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
@ -221,18 +221,16 @@ impl Client {
|
||||
'outer: for dist in 0..10 {
|
||||
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 k in 0..6 {
|
||||
let key = Vec3::new(i, j, k);
|
||||
if self.state.terrain().get_key(key).is_none()
|
||||
&& !self.pending_chunks.contains_key(&key)
|
||||
{
|
||||
if self.pending_chunks.len() < 4 {
|
||||
self.postbox
|
||||
.send_message(ClientMsg::TerrainChunkRequest { key });
|
||||
self.pending_chunks.insert(key, Instant::now());
|
||||
} else {
|
||||
break 'outer;
|
||||
}
|
||||
let key = Vec2::new(i, j);
|
||||
if self.state.terrain().get_key(key).is_none()
|
||||
&& !self.pending_chunks.contains_key(&key)
|
||||
{
|
||||
if self.pending_chunks.len() < 4 {
|
||||
self.postbox
|
||||
.send_message(ClientMsg::TerrainChunkRequest { key });
|
||||
self.pending_chunks.insert(key, Instant::now());
|
||||
} else {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ pub enum ClientMsg {
|
||||
dir: comp::phys::Dir,
|
||||
},
|
||||
TerrainChunkRequest {
|
||||
key: Vec3<i32>,
|
||||
key: Vec2<i32>,
|
||||
},
|
||||
Disconnect,
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ pub enum ServerMsg {
|
||||
animation_history: comp::AnimationHistory,
|
||||
},
|
||||
TerrainChunkUpdate {
|
||||
key: Vec3<i32>,
|
||||
key: Vec2<i32>,
|
||||
chunk: Box<TerrainChunk>,
|
||||
},
|
||||
Disconnect,
|
||||
|
@ -42,9 +42,9 @@ pub struct DeltaTime(pub f32);
|
||||
const MAX_DELTA_TIME: f32 = 0.15;
|
||||
|
||||
pub struct Changes {
|
||||
pub new_chunks: HashSet<Vec3<i32>>,
|
||||
pub changed_chunks: HashSet<Vec3<i32>>,
|
||||
pub removed_chunks: HashSet<Vec3<i32>>,
|
||||
pub new_chunks: HashSet<Vec2<i32>>,
|
||||
pub changed_chunks: HashSet<Vec2<i32>>,
|
||||
pub removed_chunks: HashSet<Vec2<i32>>,
|
||||
}
|
||||
|
||||
impl Changes {
|
||||
@ -181,7 +181,7 @@ impl State {
|
||||
}
|
||||
|
||||
/// 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
|
||||
.ecs
|
||||
.write_resource::<TerrainMap>()
|
||||
@ -195,7 +195,7 @@ impl State {
|
||||
}
|
||||
|
||||
/// 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
|
||||
.ecs
|
||||
.write_resource::<TerrainMap>()
|
||||
|
@ -4,7 +4,7 @@ use vek::*;
|
||||
// Crate
|
||||
use crate::vol::Vox;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Block {
|
||||
kind: u8,
|
||||
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 block;
|
||||
pub mod chonk;
|
||||
|
||||
// Reexports
|
||||
pub use self::{biome::BiomeKind, block::Block};
|
||||
|
||||
use crate::{
|
||||
vol::VolSize,
|
||||
volumes::{chunk::Chunk, vol_map::VolMap},
|
||||
volumes::{chunk::Chunk, vol_map_2d::VolMap2d},
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use vek::*;
|
||||
@ -41,5 +42,5 @@ impl TerrainChunkMeta {
|
||||
|
||||
// Terrain type aliases
|
||||
|
||||
pub type TerrainChunk = Chunk<Block, TerrainChunkSize, TerrainChunkMeta>;
|
||||
pub type TerrainMap = VolMap<Block, TerrainChunkSize, TerrainChunkMeta>;
|
||||
pub type TerrainChunk = chonk::Chonk; //Chunk<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.
|
||||
pub trait SampleVol: BaseVol
|
||||
where
|
||||
Self::Vox: Clone,
|
||||
{
|
||||
pub trait SampleVol<I>: BaseVol {
|
||||
type Sample: BaseVol + ReadVol;
|
||||
/// 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
|
||||
/// 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.
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub mod chunk;
|
||||
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,
|
||||
|
||||
thread_pool: ThreadPool,
|
||||
chunk_tx: mpsc::Sender<(Vec3<i32>, TerrainChunk)>,
|
||||
chunk_rx: mpsc::Receiver<(Vec3<i32>, TerrainChunk)>,
|
||||
pending_chunks: HashSet<Vec3<i32>>,
|
||||
chunk_tx: mpsc::Sender<(Vec2<i32>, TerrainChunk)>,
|
||||
chunk_rx: mpsc::Receiver<(Vec2<i32>, TerrainChunk)>,
|
||||
pending_chunks: HashSet<Vec2<i32>>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
@ -555,7 +555,7 @@ impl Server {
|
||||
.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) {
|
||||
let chunk_tx = self.chunk_tx.clone();
|
||||
self.thread_pool
|
||||
|
@ -4,8 +4,8 @@ use vek::*;
|
||||
// Project
|
||||
use common::{
|
||||
terrain::Block,
|
||||
vol::{ReadVol, SizedVol, VolSize, Vox},
|
||||
volumes::{dyna::Dyna, vol_map::VolMap},
|
||||
vol::{BaseVol, ReadVol, SizedVol, VolSize, Vox},
|
||||
volumes::{dyna::Dyna, vol_map_2d::VolMap2d, vol_map_3d::VolMap3d},
|
||||
};
|
||||
|
||||
// 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 Supplement = Aabb<i32>;
|
||||
|
||||
|
@ -1,14 +1,7 @@
|
||||
// Standard
|
||||
use std::{collections::HashMap, sync::mpsc, time::Duration};
|
||||
|
||||
// Library
|
||||
use vek::*;
|
||||
|
||||
// Project
|
||||
use client::Client;
|
||||
use common::{terrain::TerrainMap, vol::SampleVol, volumes::vol_map::VolMapErr};
|
||||
|
||||
// Crate
|
||||
use common::{terrain::TerrainMap, vol::SampleVol, volumes::vol_map_2d::VolMap2dErr};
|
||||
use crate::{
|
||||
mesh::Meshable,
|
||||
render::{Consts, Globals, Mesh, Model, Renderer, TerrainLocals, TerrainPipeline},
|
||||
@ -21,40 +14,40 @@ struct TerrainChunk {
|
||||
}
|
||||
|
||||
struct ChunkMeshState {
|
||||
pos: Vec3<i32>,
|
||||
pos: Vec2<i32>,
|
||||
started_tick: u64,
|
||||
active_worker: bool,
|
||||
}
|
||||
|
||||
/// A type produced by mesh worker threads corresponding to the position and mesh of a chunk
|
||||
struct MeshWorkerResponse {
|
||||
pos: Vec3<i32>,
|
||||
pos: Vec2<i32>,
|
||||
mesh: Mesh<TerrainPipeline>,
|
||||
started_tick: u64,
|
||||
}
|
||||
|
||||
/// Function executed by worker threads dedicated to chunk meshing
|
||||
fn mesh_worker(
|
||||
pos: Vec3<i32>,
|
||||
pos: Vec2<i32>,
|
||||
started_tick: u64,
|
||||
volume: <TerrainMap as SampleVol>::Sample,
|
||||
supplement: Aabb<i32>,
|
||||
volume: <TerrainMap as SampleVol<Aabr<i32>>>::Sample,
|
||||
range: Aabb<i32>,
|
||||
) -> MeshWorkerResponse {
|
||||
MeshWorkerResponse {
|
||||
pos,
|
||||
mesh: volume.generate_mesh(supplement),
|
||||
mesh: volume.generate_mesh(range),
|
||||
started_tick,
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
// 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_recv: mpsc::Receiver<MeshWorkerResponse>,
|
||||
mesh_todo: HashMap<Vec3<i32>, ChunkMeshState>,
|
||||
mesh_todo: HashMap<Vec2<i32>, ChunkMeshState>,
|
||||
}
|
||||
|
||||
impl Terrain {
|
||||
@ -90,28 +83,26 @@ impl Terrain {
|
||||
// elision information changes too!
|
||||
for i in -1..2 {
|
||||
for j in -1..2 {
|
||||
for k in -1..2 {
|
||||
let pos = pos + Vec3::new(i, j, k);
|
||||
let pos = pos + Vec2::new(i, j);
|
||||
|
||||
if !self.chunks.contains_key(&pos) {
|
||||
let mut neighbours = true;
|
||||
for i in -1..2 {
|
||||
for j in -1..2 {
|
||||
neighbours &= client
|
||||
.state()
|
||||
.terrain()
|
||||
.get_key(pos + Vec2::new(i, j))
|
||||
.is_some();
|
||||
}
|
||||
if !self.chunks.contains_key(&pos) {
|
||||
let mut neighbours = true;
|
||||
for i in -1..2 {
|
||||
for j in -1..2 {
|
||||
neighbours &= client
|
||||
.state()
|
||||
.terrain()
|
||||
.get_key(pos + Vec2::new(i, j))
|
||||
.is_some();
|
||||
}
|
||||
}
|
||||
|
||||
if neighbours {
|
||||
self.mesh_todo.entry(pos).or_insert(ChunkMeshState {
|
||||
pos,
|
||||
started_tick: current_tick,
|
||||
active_worker: false,
|
||||
});
|
||||
}
|
||||
if neighbours {
|
||||
self.mesh_todo.entry(pos).or_insert(ChunkMeshState {
|
||||
pos,
|
||||
started_tick: current_tick,
|
||||
active_worker: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,7 +123,7 @@ impl Terrain {
|
||||
// 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
|
||||
// neighbours too (hence the `- 1` and `+ 1`).
|
||||
let aabb = Aabb {
|
||||
let aabr = Aabr {
|
||||
min: todo
|
||||
.pos
|
||||
.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),
|
||||
};
|
||||
|
||||
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
|
||||
// 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,
|
||||
// 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.
|
||||
Err(VolMapErr::NoSuchChunk) => return,
|
||||
Err(VolMap2dErr::NoSuchChunk) => return,
|
||||
_ => panic!("Unhandled edge case"),
|
||||
};
|
||||
|
||||
@ -178,11 +174,11 @@ impl Terrain {
|
||||
.expect("Failed to upload chunk mesh to the GPU"),
|
||||
locals: renderer
|
||||
.create_consts(&[TerrainLocals {
|
||||
model_offs: response
|
||||
model_offs: Vec3::from(response
|
||||
.pos
|
||||
.map2(TerrainMap::chunk_size(), |e, sz| {
|
||||
e as f32 * sz as f32
|
||||
})
|
||||
}))
|
||||
.into_array(),
|
||||
}])
|
||||
.expect("Failed to upload chunk locals to the GPU"),
|
||||
|
@ -4,8 +4,8 @@ use vek::*;
|
||||
|
||||
// Project
|
||||
use common::{
|
||||
terrain::{Block, TerrainChunk, TerrainChunkMeta},
|
||||
vol::{SizedVol, Vox, WriteVol},
|
||||
terrain::{Block, TerrainChunk, TerrainChunkSize, TerrainChunkMeta},
|
||||
vol::{SizedVol, Vox, WriteVol, VolSize},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -20,60 +20,63 @@ impl World {
|
||||
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
|
||||
|
||||
let mut chunk = TerrainChunk::filled(Block::empty(), TerrainChunkMeta::void());
|
||||
|
||||
let air = Block::empty();
|
||||
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(50, 255, 0));
|
||||
let dirt = Block::new(3, Rgb::new(128, 90, 0));
|
||||
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 temp_nz = Perlin::new().set_seed(2);
|
||||
let chaos_nz = Perlin::new().set_seed(3);
|
||||
|
||||
for lpos in chunk.iter_positions() {
|
||||
let wpos = lpos + chunk_pos * chunk.get_size().map(|e| e as i32);
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
for x in 0..TerrainChunkSize::SIZE.x as i32 {
|
||||
for y in 0..TerrainChunkSize::SIZE.y as i32 {
|
||||
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 freq = 1.0 / 128.0;
|
||||
let ampl = 75.0;
|
||||
let small_freq = 1.0 / 32.0;
|
||||
let small_ampl = 6.0;
|
||||
let offs = 32.0;
|
||||
let chaos_freq = 1.0 / 100.0;
|
||||
let freq = 1.0 / 128.0;
|
||||
let ampl = 75.0;
|
||||
let small_freq = 1.0 / 32.0;
|
||||
let small_ampl = 6.0;
|
||||
let offs = 32.0;
|
||||
|
||||
let chaos = chaos_nz
|
||||
.get(Vec2::from(wposf * chaos_freq).into_array())
|
||||
.max(0.0)
|
||||
+ 0.5;
|
||||
let chaos = chaos_nz
|
||||
.get(Vec2::from(wposf * chaos_freq).into_array())
|
||||
.max(0.0)
|
||||
+ 0.5;
|
||||
|
||||
let height = perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl * chaos
|
||||
+ perlin_nz.get((wposf * small_freq).into_array())
|
||||
* small_ampl
|
||||
* 3.0
|
||||
* chaos.powf(2.0)
|
||||
+ offs;
|
||||
let temp = (temp_nz.get(Vec2::from(wposf * (1.0 / 64.0)).into_array()) + 1.0) * 0.5;
|
||||
let height = perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl * chaos
|
||||
+ perlin_nz.get((wposf * small_freq).into_array())
|
||||
* small_ampl
|
||||
* 3.0
|
||||
* chaos.powf(2.0)
|
||||
+ offs;
|
||||
let temp = (temp_nz.get(Vec2::from(wposf * (1.0 / 64.0)).into_array()) + 1.0) * 0.5;
|
||||
|
||||
chunk
|
||||
.set(
|
||||
lpos,
|
||||
if wposf.z < height - 4.0 {
|
||||
stone
|
||||
} else if wposf.z < height - 2.0 {
|
||||
dirt
|
||||
} else if wposf.z < height {
|
||||
Block::new(2, Rgb::new(10 + (150.0 * temp) as u8, 150, 0))
|
||||
} else {
|
||||
air
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let _ = chunk
|
||||
.set(
|
||||
lpos,
|
||||
if wposf.z < height - 4.0 {
|
||||
stone
|
||||
} else if wposf.z < height - 2.0 {
|
||||
dirt
|
||||
} else if wposf.z < height {
|
||||
Block::new(2, Rgb::new(10 + (150.0 * temp) as u8, 150, 0))
|
||||
} else {
|
||||
air
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chunk
|
||||
|
Loading…
Reference in New Issue
Block a user