Merge branch 'worldgen-structures' into 'master'

Worldgen structures

See merge request veloren/veloren!161

Former-commit-id: 0c11ec40ce5ec06e6ce98e33a2a304f8818f8270
This commit is contained in:
Joshua Barretto 2019-05-25 08:45:19 +00:00
commit c536bfb511
26 changed files with 564 additions and 131 deletions

2
Cargo.lock generated
View File

@ -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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

BIN
assets/world/tree/temperate/6.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -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.

View File

@ -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, ()>),
}

View File

@ -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,

View 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(),
})
}
}
}

View File

@ -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.

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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" }

View File

@ -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);

View File

@ -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
}
}

View File

@ -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
View 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
}
}