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 c96b475ebc
26 changed files with 528 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 Normal file

Binary file not shown.

BIN
assets/world/tree/oak/2.vox Normal file

Binary file not shown.

BIN
assets/world/tree/oak/3.vox Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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