mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'worldgen-structures' into 'master'
Worldgen structures See merge request veloren/veloren!161 Former-commit-id: 0c11ec40ce5ec06e6ce98e33a2a304f8818f8270
This commit is contained in:
commit
c536bfb511
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2707,6 +2707,8 @@ dependencies = [
|
||||
name = "veloren-world"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"minifb 0.11.2 (git+https://github.com/emoon/rust_minifb.git)",
|
||||
"noise 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vek 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
BIN
assets/world/tree/oak/1.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/oak/1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/oak/2.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/oak/2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/oak/3.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/oak/3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/pine/3.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/pine/3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/pine/4.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/pine/4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/pine/5.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/pine/5.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/temperate/1.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/temperate/1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/temperate/2.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/temperate/2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/temperate/3.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/temperate/3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/temperate/4.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/temperate/4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/temperate/5.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/temperate/5.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/temperate/6.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/temperate/6.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -36,6 +36,28 @@ lazy_static! {
|
||||
RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
/// Function used to load assets. Permits manipulating the loaded asset with a mapping function.
|
||||
/// Loaded assets are cached in a global singleton hashmap.
|
||||
/// Example usage:
|
||||
/// ```no_run
|
||||
/// use image::DynamicImage;
|
||||
/// use veloren_common::assets;
|
||||
///
|
||||
/// let my_image = assets::load::<DynamicImage>("core.ui.backgrounds.city").unwrap();
|
||||
/// ```
|
||||
pub fn load_map<A: Asset + 'static, F: FnOnce(A) -> A>(
|
||||
specifier: &str,
|
||||
f: F,
|
||||
) -> Result<Arc<A>, Error> {
|
||||
Ok(ASSETS
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(specifier.to_string())
|
||||
.or_insert(Arc::new(f(A::load(specifier)?)))
|
||||
.clone()
|
||||
.downcast()?)
|
||||
}
|
||||
|
||||
/// Function used to load assets.
|
||||
/// Loaded assets are cached in a global singleton hashmap.
|
||||
/// Example usage:
|
||||
@ -46,13 +68,7 @@ lazy_static! {
|
||||
/// let my_image = assets::load::<DynamicImage>("core.ui.backgrounds.city").unwrap();
|
||||
/// ```
|
||||
pub fn load<A: Asset + 'static>(specifier: &str) -> Result<Arc<A>, Error> {
|
||||
Ok(ASSETS
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(specifier.to_string())
|
||||
.or_insert(Arc::new(A::load(specifier)?))
|
||||
.clone()
|
||||
.downcast()?)
|
||||
load_map(specifier, |x| x)
|
||||
}
|
||||
|
||||
/// Function used to load assets that will panic if the asset is not found.
|
||||
|
@ -4,6 +4,7 @@ use crate::{
|
||||
volumes::chunk::{Chunk, ChunkErr},
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -69,10 +70,18 @@ impl ReadVol for Chonk {
|
||||
match &self.sub_chunks[sub_chunk_idx] {
|
||||
// Can't fail
|
||||
SubChunk::Homogeneous(block) => Ok(block),
|
||||
SubChunk::Hash(cblock, map) => {
|
||||
let rpos = pos
|
||||
- Vec3::unit_z()
|
||||
* (self.z_offset + sub_chunk_idx as i32 * SUB_CHUNK_HEIGHT as i32);
|
||||
|
||||
Ok(map.get(&rpos).unwrap_or(cblock))
|
||||
}
|
||||
SubChunk::Heterogeneous(chunk) => {
|
||||
let rpos = pos
|
||||
- Vec3::unit_z()
|
||||
* (self.z_offset + sub_chunk_idx as i32 * SUB_CHUNK_HEIGHT as i32);
|
||||
|
||||
chunk.get(rpos).map_err(|err| ChonkError::ChunkError(err))
|
||||
}
|
||||
}
|
||||
@ -99,21 +108,42 @@ impl WriteVol for Chonk {
|
||||
// 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| ChonkError::ChunkError(err))
|
||||
{
|
||||
Ok(()) => {
|
||||
self.sub_chunks[sub_chunk_idx] = SubChunk::Heterogeneous(new_chunk);
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
let mut map = HashMap::new();
|
||||
map.insert(rpos, block);
|
||||
|
||||
self.sub_chunks[sub_chunk_idx] = SubChunk::Hash(*cblock, map);
|
||||
Ok(())
|
||||
}
|
||||
SubChunk::Hash(cblock, map) if map.len() < 1024 => {
|
||||
map.insert(rpos, block);
|
||||
Ok(())
|
||||
}
|
||||
SubChunk::Hash(cblock, map) => {
|
||||
let mut new_chunk = Chunk::filled(*cblock, ());
|
||||
new_chunk.set(rpos, block).unwrap(); // Can't fail (I hope)
|
||||
|
||||
for (map_pos, map_block) in map {
|
||||
new_chunk.set(*map_pos, *map_block).unwrap(); // Can't fail (I hope!)
|
||||
}
|
||||
|
||||
self.sub_chunks[sub_chunk_idx] = SubChunk::Heterogeneous(new_chunk);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
SubChunk::Homogeneous(cblock) => {
|
||||
let mut new_chunk = Chunk::filled(*cblock, ());
|
||||
|
||||
new_chunk.set(rpos, block).unwrap(); // Can't fail (I hope!)
|
||||
|
||||
self.sub_chunks[sub_chunk_idx] = SubChunk::Heterogeneous(new_chunk);
|
||||
Ok(())
|
||||
}
|
||||
*/
|
||||
SubChunk::Heterogeneous(chunk) => chunk
|
||||
.set(rpos, block)
|
||||
.map_err(|err| ChonkError::ChunkError(err)),
|
||||
//_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -122,6 +152,7 @@ impl WriteVol for Chonk {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum SubChunk {
|
||||
Homogeneous(Block),
|
||||
Hash(Block, HashMap<Vec3<i32>, Block>),
|
||||
Heterogeneous(Chunk<Block, TerrainChunkSize, ()>),
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
pub mod biome;
|
||||
pub mod block;
|
||||
pub mod chonk;
|
||||
pub mod structure;
|
||||
|
||||
// Reexports
|
||||
pub use self::{biome::BiomeKind, block::Block};
|
||||
pub use self::{biome::BiomeKind, block::Block, structure::Structure};
|
||||
|
||||
use crate::{
|
||||
vol::VolSize,
|
||||
|
81
common/src/terrain/structure.rs
Normal file
81
common/src/terrain/structure.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use super::Block;
|
||||
use crate::{
|
||||
assets::{self, load_from_path, Asset},
|
||||
vol::{BaseVol, ReadVol, Vox, WriteVol},
|
||||
volumes::dyna::{Dyna, DynaErr},
|
||||
};
|
||||
use dot_vox::DotVoxData;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StructureError {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Structure {
|
||||
center: Vec3<i32>,
|
||||
vol: Dyna<Block, ()>,
|
||||
empty: Block,
|
||||
}
|
||||
|
||||
impl Structure {
|
||||
pub fn with_center(mut self, center: Vec3<i32>) -> Self {
|
||||
self.center = center;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BaseVol for Structure {
|
||||
type Vox = Block;
|
||||
type Err = StructureError;
|
||||
}
|
||||
|
||||
impl ReadVol for Structure {
|
||||
#[inline(always)]
|
||||
fn get(&self, pos: Vec3<i32>) -> Result<&Block, StructureError> {
|
||||
match self.vol.get(pos + self.center) {
|
||||
Ok(block) => Ok(block),
|
||||
Err(DynaErr::OutOfBounds) => Ok(&self.empty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Asset for Structure {
|
||||
fn load(specifier: &str) -> Result<Self, assets::Error> {
|
||||
let dot_vox_data = DotVoxData::load(specifier)?;
|
||||
|
||||
if let Some(model) = dot_vox_data.models.get(0) {
|
||||
let palette = dot_vox_data
|
||||
.palette
|
||||
.iter()
|
||||
.map(|col| Rgba::from(col.to_ne_bytes()).into())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut vol = Dyna::filled(
|
||||
Vec3::new(model.size.x, model.size.y, model.size.z),
|
||||
Block::empty(),
|
||||
(),
|
||||
);
|
||||
|
||||
for voxel in &model.voxels {
|
||||
if let Some(&color) = palette.get(voxel.i as usize) {
|
||||
let _ = vol.set(
|
||||
Vec3::new(voxel.x, voxel.y, voxel.z).map(|e| e as i32),
|
||||
Block::new(1, color),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Structure {
|
||||
center: Vec3::zero(),
|
||||
vol,
|
||||
empty: Block::empty(),
|
||||
})
|
||||
} else {
|
||||
Ok(Self {
|
||||
center: Vec3::zero(),
|
||||
vol: Dyna::filled(Vec3::zero(), Block::empty(), ()),
|
||||
empty: Block::empty(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -2,9 +2,17 @@ use crate::ray::{Ray, RayUntil};
|
||||
use vek::*;
|
||||
|
||||
/// A voxel.
|
||||
pub trait Vox {
|
||||
pub trait Vox: Sized {
|
||||
fn empty() -> Self;
|
||||
fn is_empty(&self) -> bool;
|
||||
|
||||
fn or(self, other: Self) -> Self {
|
||||
if self.is_empty() {
|
||||
other
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A volume that contains voxel data.
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Library
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use vek::*;
|
||||
|
||||
// Local
|
||||
@ -13,6 +14,7 @@ pub enum DynaErr {
|
||||
// V = Voxel
|
||||
// S = Size (replace when const generics are a thing)
|
||||
// M = Metadata
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Dyna<V: Vox, M> {
|
||||
vox: Vec<V>,
|
||||
meta: M,
|
||||
|
@ -76,7 +76,7 @@ impl Server {
|
||||
state.ecs_mut().register::<comp::phys::ForceUpdate>();
|
||||
state
|
||||
.ecs_mut()
|
||||
.add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 150.0)));
|
||||
.add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 280.0)));
|
||||
|
||||
let mut this = Self {
|
||||
state,
|
||||
|
@ -166,9 +166,9 @@ void main() {
|
||||
vec4 fxaa_color = fxaa_apply(src_color, uv * screen_res.xy, screen_res.xy);
|
||||
|
||||
vec4 hsva_color = vec4(rgb2hsv(fxaa_color.rgb), fxaa_color.a);
|
||||
hsva_color.y += 0.27;
|
||||
hsva_color.y *= 1.27;
|
||||
hsva_color.x -= 0.015;
|
||||
hsva_color.z = 1.0 - 1.0 / (1.0 * hsva_color.z + 1.0);
|
||||
//hsva_color.z = 1.0 - 1.0 / (1.0 * hsva_color.z + 1.0);
|
||||
vec4 final_color = vec4(hsv2rgb(hsva_color.rgb), hsva_color.a);
|
||||
|
||||
tgt_color = final_color;
|
||||
|
@ -8,6 +8,8 @@ edition = "2018"
|
||||
common = { package = "veloren-common", path = "../common" }
|
||||
vek = "0.9"
|
||||
noise = "0.5"
|
||||
fxhash = "0.2"
|
||||
lazy_static = "1.3"
|
||||
|
||||
[dev-dependencies]
|
||||
minifb = { git = "https://github.com/emoon/rust_minifb.git" }
|
||||
|
@ -23,7 +23,8 @@ fn main() {
|
||||
|
||||
let alt = world
|
||||
.sim()
|
||||
.sample(pos)
|
||||
.sampler()
|
||||
.sample_2d(pos)
|
||||
.map(|sample| sample.alt.sub(64.0).add(gain).mul(0.7).max(0.0).min(255.0) as u8)
|
||||
.unwrap_or(0);
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
#![feature(euclidean_division)]
|
||||
|
||||
mod sim;
|
||||
mod structure;
|
||||
|
||||
use common::{
|
||||
terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||
vol::{SizedVol, VolSize, Vox, WriteVol},
|
||||
};
|
||||
use fxhash::FxHashMap;
|
||||
use noise::{BasicMulti, MultiFractal, NoiseFn, Perlin, Seedable};
|
||||
use std::{
|
||||
hash::Hash,
|
||||
ops::{Add, Div, Mul, Neg, Sub},
|
||||
time::Duration,
|
||||
};
|
||||
@ -40,75 +45,80 @@ impl World {
|
||||
|
||||
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 dirt = Block::new(3, Rgb::new(128, 90, 0));
|
||||
let sand = Block::new(4, Rgb::new(180, 150, 50));
|
||||
let water = Block::new(5, Rgb::new(100, 150, 255));
|
||||
|
||||
let warp_nz = BasicMulti::new().set_octaves(3).set_seed(self.sim.seed + 0);
|
||||
|
||||
let base_z = match self.sim.get_base_z(chunk_pos.map(|e| e as u32)) {
|
||||
Some(base_z) => base_z as i32,
|
||||
None => return TerrainChunk::new(0, air, air, TerrainChunkMeta::void()),
|
||||
None => return TerrainChunk::new(0, water, air, TerrainChunkMeta::void()),
|
||||
};
|
||||
|
||||
let mut chunk = TerrainChunk::new(base_z, stone, air, TerrainChunkMeta::void());
|
||||
|
||||
let mut world_sampler = self.sim.sampler();
|
||||
|
||||
for x in 0..TerrainChunkSize::SIZE.x as i32 {
|
||||
for y in 0..TerrainChunkSize::SIZE.y as i32 {
|
||||
let wpos2d = Vec2::new(x, y)
|
||||
+ Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32);
|
||||
let wposf2d = wpos2d.map(|e| e as f64);
|
||||
|
||||
let sim::Sample {
|
||||
alt,
|
||||
chaos,
|
||||
surface_color,
|
||||
} = if let Some(sample) = self.sim.sample(wpos2d) {
|
||||
sample
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let max_z = self
|
||||
.sim
|
||||
.get_interpolated(wpos2d, |chunk| chunk.get_max_z())
|
||||
.unwrap_or(0.0) as i32;
|
||||
|
||||
for z in base_z..max_z {
|
||||
for z in base_z..max_z.max(sim::SEA_LEVEL as i32) {
|
||||
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 warp = (warp_nz
|
||||
.get((wposf.div(Vec3::new(120.0, 120.0, 150.0))).into_array())
|
||||
as f32)
|
||||
.mul((chaos - 0.1).max(0.0))
|
||||
.mul(90.0);
|
||||
|
||||
let height = alt + warp;
|
||||
let temp = 0.0;
|
||||
|
||||
let z = wposf.z as f32;
|
||||
let _ = chunk.set(
|
||||
lpos,
|
||||
if z < height - 4.0 {
|
||||
stone
|
||||
} else if z < height {
|
||||
Block::new(1, surface_color.map(|e| (e * 255.0) as u8))
|
||||
} else if z < sim::SEA_LEVEL {
|
||||
water
|
||||
let sim::Sample3d { block } =
|
||||
if let Some(sample) = world_sampler.sample_3d(wpos) {
|
||||
sample
|
||||
} else {
|
||||
air
|
||||
},
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
let _ = chunk.set(lpos, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chunk
|
||||
|
||||
// */
|
||||
}
|
||||
}
|
||||
|
||||
struct Cache<K: Hash + Eq + Copy, V> {
|
||||
capacity: usize,
|
||||
map: FxHashMap<K, (usize, V)>,
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq + Copy, V> Cache<K, V> {
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self {
|
||||
capacity,
|
||||
map: FxHashMap::default(),
|
||||
counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self) {
|
||||
let (capacity, counter) = (self.capacity, self.counter);
|
||||
self.map.retain(|_, (c, _)| *c + capacity > counter);
|
||||
}
|
||||
|
||||
pub fn get<F: FnOnce(K) -> V>(&mut self, k: K, f: F) -> &V {
|
||||
let mut counter = &mut self.counter;
|
||||
&self
|
||||
.map
|
||||
.entry(k)
|
||||
.or_insert_with(|| {
|
||||
*counter += 1;
|
||||
(*counter, f(k))
|
||||
})
|
||||
.1
|
||||
}
|
||||
}
|
||||
|
319
world/src/sim.rs
319
world/src/sim.rs
@ -1,4 +1,10 @@
|
||||
use common::{terrain::TerrainChunkSize, vol::VolSize};
|
||||
use crate::{structure::StructureGen2d, Cache};
|
||||
use common::{
|
||||
assets,
|
||||
terrain::{Block, Structure, TerrainChunkSize},
|
||||
vol::{ReadVol, VolSize, Vox},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use noise::{
|
||||
BasicMulti, HybridMulti, MultiFractal, NoiseFn, OpenSimplex, RidgedMulti, Seedable,
|
||||
SuperSimplex,
|
||||
@ -6,6 +12,7 @@ use noise::{
|
||||
use std::{
|
||||
f32,
|
||||
ops::{Add, Div, Mul, Neg, Sub},
|
||||
sync::Arc,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
@ -15,6 +22,7 @@ pub struct WorldSim {
|
||||
pub seed: u32,
|
||||
chunks: Vec<SimChunk>,
|
||||
gen_ctx: GenCtx,
|
||||
tree_gen: StructureGen2d,
|
||||
}
|
||||
|
||||
impl WorldSim {
|
||||
@ -31,6 +39,11 @@ impl WorldSim {
|
||||
temp_nz: SuperSimplex::new().set_seed(seed + 5),
|
||||
small_nz: BasicMulti::new().set_octaves(2).set_seed(seed + 6),
|
||||
rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(seed + 7),
|
||||
warp_nz: BasicMulti::new().set_octaves(3).set_seed(seed + 8),
|
||||
tree_nz: BasicMulti::new()
|
||||
.set_octaves(8)
|
||||
.set_persistence(0.75)
|
||||
.set_seed(seed + 9),
|
||||
};
|
||||
|
||||
let mut chunks = Vec::new();
|
||||
@ -44,6 +57,7 @@ impl WorldSim {
|
||||
seed,
|
||||
chunks,
|
||||
gen_ctx,
|
||||
tree_gen: StructureGen2d::new(seed, 32, 32),
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,74 +107,231 @@ impl WorldSim {
|
||||
co0 * x2 * x + co1 * x2 + co2 * x + co3
|
||||
};
|
||||
|
||||
let mut y = [T::default(); 4];
|
||||
let mut x = [T::default(); 4];
|
||||
|
||||
for (y_idx, j) in (-1..3).enumerate() {
|
||||
let x0 =
|
||||
f(self.get(pos.map2(Vec2::new(-1, j), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
let x1 = f(self.get(pos.map2(Vec2::new(0, j), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
let x2 = f(self.get(pos.map2(Vec2::new(1, j), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
let x3 = f(self.get(pos.map2(Vec2::new(2, j), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
for (x_idx, j) in (-1..3).enumerate() {
|
||||
let y0 =
|
||||
f(self.get(pos.map2(Vec2::new(j, -1), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
let y1 = f(self.get(pos.map2(Vec2::new(j, 0), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
let y2 = f(self.get(pos.map2(Vec2::new(j, 1), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
let y3 = f(self.get(pos.map2(Vec2::new(j, 2), |e, q| (e.max(0.0) as i32 + q) as u32))?);
|
||||
|
||||
y[y_idx] = cubic(x0, x1, x2, x3, pos.x.fract() as f32);
|
||||
x[x_idx] = cubic(y0, y1, y2, y3, pos.y.fract() as f32);
|
||||
}
|
||||
|
||||
Some(cubic(y[0], y[1], y[2], y[3], pos.y.fract() as f32))
|
||||
Some(cubic(x[0], x[1], x[2], x[3], pos.x.fract() as f32))
|
||||
}
|
||||
|
||||
pub fn sample(&self, pos: Vec2<i32>) -> Option<Sample> {
|
||||
let wposf = pos.map(|e| e as f64);
|
||||
pub fn sampler(&self) -> Sampler {
|
||||
Sampler {
|
||||
sim: self,
|
||||
sample2d_cache: Cache::with_capacity(1024),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*let wposf = wposf + Vec2::new(
|
||||
self.gen_ctx.turb_x_nz.get((wposf.div(200.0)).into_array()) * 250.0,
|
||||
self.gen_ctx.turb_y_nz.get((wposf.div(200.0)).into_array()) * 250.0,
|
||||
);*/
|
||||
pub struct Sampler<'a> {
|
||||
sim: &'a WorldSim,
|
||||
sample2d_cache: Cache<Vec2<i32>, Option<Sample2d>>,
|
||||
}
|
||||
|
||||
let chaos = self.get_interpolated(pos, |chunk| chunk.chaos)?;
|
||||
let temp = self.get_interpolated(pos, |chunk| chunk.temp)?;
|
||||
let rockiness = self.get_interpolated(pos, |chunk| chunk.rockiness)?;
|
||||
impl<'a> Sampler<'a> {
|
||||
fn sample_2d_impl(sim: &WorldSim, wpos: Vec2<i32>) -> Option<Sample2d> {
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
|
||||
let rock = (self.gen_ctx.small_nz.get((wposf.div(100.0)).into_array()) as f32)
|
||||
let alt_base = sim.get_interpolated(wpos, |chunk| chunk.alt_base)?;
|
||||
let chaos = sim.get_interpolated(wpos, |chunk| chunk.chaos)?;
|
||||
let temp = sim.get_interpolated(wpos, |chunk| chunk.temp)?;
|
||||
let rockiness = sim.get_interpolated(wpos, |chunk| chunk.rockiness)?;
|
||||
let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?;
|
||||
|
||||
let rock = (sim.gen_ctx.small_nz.get((wposf.div(100.0)).into_array()) as f32)
|
||||
.mul(rockiness)
|
||||
.sub(0.2)
|
||||
.max(0.0)
|
||||
.mul(2.0);
|
||||
|
||||
let alt = self.get_interpolated(pos, |chunk| chunk.alt)?
|
||||
+ self.gen_ctx.small_nz.get((wposf.div(128.0)).into_array()) as f32
|
||||
* chaos.max(0.15)
|
||||
* 32.0
|
||||
let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?
|
||||
+ sim.gen_ctx.small_nz.get((wposf.div(256.0)).into_array()) as f32
|
||||
* chaos.max(0.2)
|
||||
* 64.0
|
||||
+ rock * 15.0;
|
||||
|
||||
let wposf3d = Vec3::new(wposf.x, wposf.y, alt as f64);
|
||||
|
||||
let marble = (sim.gen_ctx.hill_nz.get((wposf3d.div(64.0)).into_array()) as f32)
|
||||
.mul(0.5)
|
||||
.add(1.0)
|
||||
.mul(0.5);
|
||||
|
||||
// Colours
|
||||
let cold_grass = Rgb::new(0.0, 0.75, 0.25);
|
||||
let warm_grass = Rgb::new(0.55, 0.9, 0.0);
|
||||
let cold_stone = Rgb::new(0.78, 0.86, 1.0);
|
||||
let warm_stone = Rgb::new(0.8, 0.7, 0.55);
|
||||
let sand = Rgb::new(0.93, 0.84, 0.23);
|
||||
let cold_grass = Rgb::new(0.05, 0.5, 0.3);
|
||||
let warm_grass = Rgb::new(0.4, 1.0, 0.05);
|
||||
let cold_stone = Rgb::new(0.55, 0.75, 0.9);
|
||||
let warm_stone = Rgb::new(0.75, 0.6, 0.35);
|
||||
let sand = Rgb::new(0.93, 0.84, 0.33);
|
||||
let snow = Rgb::broadcast(1.0);
|
||||
|
||||
let grass = Rgb::lerp(cold_grass, warm_grass, temp);
|
||||
let ground = Rgb::lerp(grass, warm_stone, rock.mul(5.0).min(0.8));
|
||||
let cliff = Rgb::lerp(cold_stone, warm_stone, temp);
|
||||
let cliff = Rgb::lerp(cold_stone, warm_stone, marble);
|
||||
|
||||
Some(Sample {
|
||||
Some(Sample2d {
|
||||
alt,
|
||||
chaos,
|
||||
surface_color: Rgb::lerp(
|
||||
sand,
|
||||
// Land
|
||||
Rgb::lerp(ground, cliff, (alt - SEA_LEVEL - 100.0) / 150.0),
|
||||
Rgb::lerp(
|
||||
ground,
|
||||
// Mountain
|
||||
Rgb::lerp(
|
||||
cliff,
|
||||
snow,
|
||||
(alt - SEA_LEVEL - 350.0 - alt_base - temp * 48.0) / 12.0,
|
||||
),
|
||||
(alt - SEA_LEVEL - 150.0) / 180.0,
|
||||
),
|
||||
// Beach
|
||||
(alt - SEA_LEVEL - 2.0) / 5.0,
|
||||
),
|
||||
tree_density,
|
||||
close_trees: sim.tree_gen.sample(wpos),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sample_2d(&mut self, wpos2d: Vec2<i32>) -> Option<&Sample2d> {
|
||||
let sim = &self.sim;
|
||||
self.sample2d_cache
|
||||
.get(wpos2d, |wpos2d| Self::sample_2d_impl(sim, wpos2d))
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
pub fn sample_3d(&mut self, wpos: Vec3<i32>) -> Option<Sample3d> {
|
||||
let wpos2d = Vec2::from(wpos);
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
|
||||
// Sample 2D terrain attributes
|
||||
|
||||
let Sample2d {
|
||||
alt,
|
||||
chaos,
|
||||
surface_color,
|
||||
tree_density,
|
||||
close_trees,
|
||||
} = *self.sample_2d(wpos2d)?;
|
||||
|
||||
// Apply warping
|
||||
|
||||
let warp = (self
|
||||
.sim
|
||||
.gen_ctx
|
||||
.warp_nz
|
||||
.get((wposf.div(Vec3::new(120.0, 120.0, 150.0))).into_array())
|
||||
as f32)
|
||||
.mul((chaos - 0.1).max(0.0))
|
||||
.mul(90.0);
|
||||
|
||||
let height = alt + warp;
|
||||
let temp = 0.0;
|
||||
|
||||
// Sample blocks
|
||||
|
||||
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 dirt = Block::new(3, Rgb::new(128, 90, 0));
|
||||
let sand = Block::new(4, Rgb::new(180, 150, 50));
|
||||
let water = Block::new(5, Rgb::new(100, 150, 255));
|
||||
|
||||
let above_ground =
|
||||
(&close_trees)
|
||||
.iter()
|
||||
.fold(air, |block, (tree_pos, tree_seed)| {
|
||||
match self.sample_2d(*tree_pos) {
|
||||
Some(tree_sample)
|
||||
if tree_sample.tree_density
|
||||
> 0.5 + (*tree_seed as f32 / 1000.0).fract() * 0.2 =>
|
||||
{
|
||||
let tree_pos3d =
|
||||
Vec3::new(tree_pos.x, tree_pos.y, tree_sample.alt as i32);
|
||||
block.or(TREES[*tree_seed as usize % TREES.len()]
|
||||
.get(wpos - tree_pos3d)
|
||||
.map(|b| b.clone())
|
||||
.unwrap_or(Block::empty()))
|
||||
}
|
||||
_ => block,
|
||||
}
|
||||
});
|
||||
|
||||
let z = wposf.z as f32;
|
||||
Some(Sample3d {
|
||||
block: if z < height - 4.0 {
|
||||
stone
|
||||
} else if z < height {
|
||||
Block::new(1, surface_color.map(|e| (e * 255.0) as u8))
|
||||
} else if z < SEA_LEVEL {
|
||||
water
|
||||
} else {
|
||||
above_ground
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Sample {
|
||||
lazy_static! {
|
||||
static ref TREES: [Arc<Structure>; 12] = [
|
||||
assets::load_map("world/tree/oak/1.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(15, 18, 14)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/oak/2.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(15, 18, 14)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/oak/3.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(15, 18, 14)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/pine/3.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(15, 15, 14)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/pine/4.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(15, 15, 14)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/pine/5.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(15, 15, 12)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/temperate/1.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(4, 4, 7)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/temperate/2.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(4, 4, 7)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/temperate/3.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(4, 4, 7)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/temperate/4.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(4, 4, 7)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/temperate/5.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(4, 4, 7)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/temperate/6.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(4, 4, 7)))
|
||||
.unwrap(),
|
||||
];
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Sample2d {
|
||||
pub alt: f32,
|
||||
pub chaos: f32,
|
||||
pub surface_color: Rgb<f32>,
|
||||
pub tree_density: f32,
|
||||
pub close_trees: [(Vec2<i32>, u32); 9],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Sample3d {
|
||||
pub block: Block,
|
||||
}
|
||||
|
||||
struct GenCtx {
|
||||
@ -172,50 +343,71 @@ struct GenCtx {
|
||||
temp_nz: SuperSimplex,
|
||||
small_nz: BasicMulti,
|
||||
rock_nz: HybridMulti,
|
||||
warp_nz: BasicMulti,
|
||||
tree_nz: BasicMulti,
|
||||
}
|
||||
|
||||
const Z_TOLERANCE: (f32, f32) = (32.0, 64.0);
|
||||
pub const SEA_LEVEL: f32 = 64.0;
|
||||
const Z_TOLERANCE: (f32, f32) = (48.0, 64.0);
|
||||
pub const SEA_LEVEL: f32 = 128.0;
|
||||
|
||||
pub struct SimChunk {
|
||||
pub chaos: f32,
|
||||
pub alt_base: f32,
|
||||
pub alt: f32,
|
||||
pub temp: f32,
|
||||
pub rockiness: f32,
|
||||
pub tree_density: f32,
|
||||
}
|
||||
|
||||
impl SimChunk {
|
||||
fn generate(pos: Vec2<u32>, gen_ctx: &mut GenCtx) -> Self {
|
||||
let wposf = (pos * Vec2::from(TerrainChunkSize::SIZE)).map(|e| e as f64);
|
||||
|
||||
let hill = (gen_ctx.hill_nz.get((wposf.div(3500.0)).into_array()) as f32).max(0.0);
|
||||
let hill = (0.0
|
||||
+ gen_ctx
|
||||
.hill_nz
|
||||
.get((wposf.div(3_500.0)).into_array())
|
||||
.mul(1.0) as f32
|
||||
+ gen_ctx
|
||||
.hill_nz
|
||||
.get((wposf.div(1_000.0)).into_array())
|
||||
.mul(0.3) as f32)
|
||||
.add(0.3)
|
||||
.max(0.0);
|
||||
|
||||
let chaos = (gen_ctx.chaos_nz.get((wposf.div(3500.0)).into_array()) as f32)
|
||||
let chaos = (gen_ctx.chaos_nz.get((wposf.div(4_000.0)).into_array()) as f32)
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.powf(1.9)
|
||||
.add(0.25 * hill);
|
||||
|
||||
let chaos = chaos + chaos.mul(20.0).sin().mul(0.05);
|
||||
let chaos = chaos + chaos.mul(16.0).sin().mul(0.02);
|
||||
|
||||
let alt_base = gen_ctx.alt_nz.get((wposf.div(5000.0)).into_array()) as f32 * 0.4;
|
||||
let alt_base = gen_ctx.alt_nz.get((wposf.div(6_000.0)).into_array()) as f32;
|
||||
let alt_base = alt_base
|
||||
.mul(0.4)
|
||||
.add(alt_base.mul(128.0).sin().mul(0.004))
|
||||
.mul(600.0);
|
||||
|
||||
let alt_main = gen_ctx.alt_nz.get((wposf.div(750.0)).into_array()) as f32;
|
||||
let alt_main = gen_ctx.alt_nz.get((wposf.div(1_500.0)).into_array()) as f32;
|
||||
|
||||
let alt = SEA_LEVEL
|
||||
+ alt_base
|
||||
+ (0.0
|
||||
+ alt_main
|
||||
+ gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32
|
||||
* alt_main.max(0.05)
|
||||
* chaos
|
||||
* 1.3)
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.mul(chaos)
|
||||
.mul(1200.0);
|
||||
|
||||
Self {
|
||||
chaos,
|
||||
alt: SEA_LEVEL
|
||||
+ (0.0
|
||||
+ alt_main
|
||||
+ gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32
|
||||
* alt_main.max(0.05)
|
||||
* chaos
|
||||
* 1.3)
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.mul(chaos)
|
||||
.add(alt_base)
|
||||
.mul(750.0),
|
||||
alt_base,
|
||||
alt,
|
||||
temp: (gen_ctx.temp_nz.get((wposf.div(48.0)).into_array()) as f32)
|
||||
.add(1.0)
|
||||
.mul(0.5),
|
||||
@ -223,29 +415,20 @@ impl SimChunk {
|
||||
.sub(0.1)
|
||||
.mul(1.2)
|
||||
.max(0.0),
|
||||
tree_density: (gen_ctx.tree_nz.get((wposf.div(1024.0)).into_array()) as f32)
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.mul(1.0 - chaos * 0.8)
|
||||
.add(0.1)
|
||||
.mul(if alt > SEA_LEVEL + 3.0 { 1.0 } else { 0.0 }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_base_z(&self) -> f32 {
|
||||
self.alt - Z_TOLERANCE.0
|
||||
self.alt - Z_TOLERANCE.0 * (self.chaos + 0.1) - 3.0
|
||||
}
|
||||
|
||||
pub fn get_max_z(&self) -> f32 {
|
||||
self.alt + Z_TOLERANCE.1
|
||||
}
|
||||
}
|
||||
|
||||
trait Hsv {
|
||||
fn into_hsv(self) -> Self;
|
||||
fn into_rgb(self) -> Self;
|
||||
}
|
||||
|
||||
impl Hsv for Rgb<f32> {
|
||||
fn into_hsv(mut self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn into_rgb(mut self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
60
world/src/structure.rs
Normal file
60
world/src/structure.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use vek::*;
|
||||
|
||||
pub struct StructureGen2d {
|
||||
seed: u32,
|
||||
freq: u32,
|
||||
spread: u32,
|
||||
}
|
||||
|
||||
impl StructureGen2d {
|
||||
pub fn new(seed: u32, freq: u32, spread: u32) -> Self {
|
||||
Self { seed, freq, spread }
|
||||
}
|
||||
|
||||
fn random(&self, seed: u32, pos: Vec2<i32>) -> u32 {
|
||||
let pos = pos.map(|e| (e * 13 + (1 << 31)) as u32);
|
||||
|
||||
let next = (self.seed + seed)
|
||||
.wrapping_mul(0x168E3D1F)
|
||||
.wrapping_add(0xDEADBEAD);
|
||||
let next = next
|
||||
.rotate_left(13)
|
||||
.wrapping_mul(133227)
|
||||
.wrapping_add(pos.x);
|
||||
let next = next.rotate_left(13).wrapping_mul(318912) ^ 0x42133742;
|
||||
let next = next
|
||||
.rotate_left(13)
|
||||
.wrapping_mul(938219)
|
||||
.wrapping_add(pos.y);
|
||||
let next = next.rotate_left(13).wrapping_mul(313322) ^ 0xDEADBEEF;
|
||||
let next = next.rotate_left(13).wrapping_mul(929009) ^ 0xFF329DE3;
|
||||
let next = next.rotate_left(13).wrapping_mul(422671) ^ 0x42892942;
|
||||
next
|
||||
}
|
||||
|
||||
pub fn sample(&self, sample_pos: Vec2<i32>) -> [(Vec2<i32>, u32); 9] {
|
||||
let mut samples = [(Vec2::zero(), 0); 9];
|
||||
|
||||
let sample_closest = sample_pos.map(|e| e - e.rem_euclid(self.freq as i32));
|
||||
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
let center = sample_closest
|
||||
+ Vec2::new(i, j).map(|e| e as i32 - 1) * self.freq as i32
|
||||
+ self.freq as i32 / 2;
|
||||
samples[i * 3 + j] = (
|
||||
center
|
||||
+ Vec2::new(
|
||||
(self.random(1, center) % (self.spread * 2)) as i32
|
||||
- self.spread as i32,
|
||||
(self.random(2, center) % (self.spread * 2)) as i32
|
||||
- self.spread as i32,
|
||||
),
|
||||
self.random(3, center),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
samples
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user