From 79d2d11d711b78e4eb87cd77f77cbb7b840582e3 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 22 May 2019 10:42:19 +0100 Subject: [PATCH 01/20] Improved cache-coherency of world chunk interpolation Former-commit-id: 2f45cf47916970185c65c701dfc63ae838f05734 --- world/src/lib.rs | 2 +- world/src/sim.rs | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/world/src/lib.rs b/world/src/lib.rs index 38ab296511..6559e6cdbd 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -75,7 +75,7 @@ impl World { .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); diff --git a/world/src/sim.rs b/world/src/sim.rs index fcdee446e2..d0a0284317 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -93,19 +93,18 @@ 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) -> Option { @@ -132,16 +131,22 @@ impl WorldSim { * 32.0 + rock * 15.0; + let wposf3d = Vec3::new(wposf.x, wposf.y, alt as f64); + + let marble = (self.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 warm_stone = Rgb::new(0.72, 0.7, 0.33); let sand = Rgb::new(0.93, 0.84, 0.23); 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 { alt, From 55d5c9519777d431789e541d88fc6654b961fb08 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 22 May 2019 14:22:54 +0100 Subject: [PATCH 02/20] Small worldgen fixes and hash chunk optimisation Former-commit-id: f1cb9a010f5d0882d4bc21ede1f405a8a7fa867c --- common/src/terrain/chonk.rs | 36 +++++++++++++++++++++++++++--------- world/src/structure.rs | 4 ++++ 2 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 world/src/structure.rs diff --git a/common/src/terrain/chonk.rs b/common/src/terrain/chonk.rs index 229e68da6e..797ccedafa 100644 --- a/common/src/terrain/chonk.rs +++ b/common/src/terrain/chonk.rs @@ -5,6 +5,7 @@ use crate::{ }; use serde_derive::{Deserialize, Serialize}; use vek::*; +use std::collections::HashMap; #[derive(Debug)] pub enum ChonkError { @@ -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,17 +108,25 @@ impl WriteVol for Chonk { // Can't fail SubChunk::Homogeneous(cblock) if *cblock == block => Ok(()), SubChunk::Homogeneous(cblock) => { + 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() < 4096 => { + map.insert(rpos, block); + Ok(()) + }, + SubChunk::Hash(cblock, map) => { 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), + + 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::Heterogeneous(chunk) => chunk .set(rpos, block) @@ -122,6 +139,7 @@ impl WriteVol for Chonk { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum SubChunk { Homogeneous(Block), + Hash(Block, HashMap, Block>), Heterogeneous(Chunk), } diff --git a/world/src/structure.rs b/world/src/structure.rs new file mode 100644 index 0000000000..ded63c8597 --- /dev/null +++ b/world/src/structure.rs @@ -0,0 +1,4 @@ +pub struct StructureGen { + freq: f32, + +} From bd1baa063d1c41eb2a4ed1c78796c0c43dd8df28 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 22 May 2019 17:24:08 +0100 Subject: [PATCH 03/20] Added snow to mountains, fixed hash sub-chunk allocation failure Former-commit-id: 45bb3063172b980f7cd7aa5bd2b50a1a53480120 --- common/src/terrain/chonk.rs | 15 ++++++++++++++- voxygen/shaders/postprocess.frag | 4 ++-- world/src/sim.rs | 19 +++++++++++++++---- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/common/src/terrain/chonk.rs b/common/src/terrain/chonk.rs index 797ccedafa..a15e521e83 100644 --- a/common/src/terrain/chonk.rs +++ b/common/src/terrain/chonk.rs @@ -114,12 +114,13 @@ impl WriteVol for Chonk { self.sub_chunks[sub_chunk_idx] = SubChunk::Hash(*cblock, map); Ok(()) }, - SubChunk::Hash(cblock, map) if map.len() < 4096 => { + 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!) @@ -128,9 +129,21 @@ impl WriteVol for Chonk { 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!(), } } } diff --git a/voxygen/shaders/postprocess.frag b/voxygen/shaders/postprocess.frag index 7602776032..304a0de91c 100644 --- a/voxygen/shaders/postprocess.frag +++ b/voxygen/shaders/postprocess.frag @@ -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; diff --git a/world/src/sim.rs b/world/src/sim.rs index d0a0284317..50eac139c2 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -142,7 +142,8 @@ impl WorldSim { 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.72, 0.7, 0.33); - let sand = Rgb::new(0.93, 0.84, 0.23); + 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)); @@ -154,7 +155,16 @@ impl WorldSim { 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 - 215.0 - temp * 24.0) / 4.0, + ), + (alt - SEA_LEVEL - 100.0) / 100.0 + ), // Beach (alt - SEA_LEVEL - 2.0) / 5.0, ), @@ -203,7 +213,8 @@ impl SimChunk { let chaos = chaos + chaos.mul(20.0).sin().mul(0.05); - 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(5000.0)).into_array()) as f32; + let alt_base = alt_base * 0.4 + alt_base.mul(16.0).sin().mul(0.01); let alt_main = gen_ctx.alt_nz.get((wposf.div(750.0)).into_array()) as f32; @@ -232,7 +243,7 @@ impl SimChunk { } pub fn get_base_z(&self) -> f32 { - self.alt - Z_TOLERANCE.0 + self.alt - Z_TOLERANCE.0 * (self.chaos + 0.1) } pub fn get_max_z(&self) -> f32 { From bc0c20fe3e586d5f8ae96e2e06565328e4d72044 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 22 May 2019 20:19:54 +0100 Subject: [PATCH 04/20] Made mountain rock darker Former-commit-id: 2da60a1e6b69d2522773ee3aae5cf2d83a1d0396 --- world/src/sim.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/world/src/sim.rs b/world/src/sim.rs index 50eac139c2..a9ad3d3eb4 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -140,8 +140,8 @@ impl WorldSim { // 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.72, 0.7, 0.33); + let cold_stone = Rgb::new(0.65, 0.7, 0.85); + let warm_stone = Rgb::new(0.8, 0.6, 0.28); let sand = Rgb::new(0.93, 0.84, 0.33); let snow = Rgb::broadcast(1.0); From aa023f4fede53bb9d32740fc220eb4398ad0ea22 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 23 May 2019 09:19:19 +0100 Subject: [PATCH 05/20] Adjusted snow Former-commit-id: d30f3f8bb4e59c436023ccb38f65e868935d62f0 --- world/src/sim.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/world/src/sim.rs b/world/src/sim.rs index a9ad3d3eb4..80708108df 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -110,11 +110,7 @@ impl WorldSim { pub fn sample(&self, pos: Vec2) -> Option { let wposf = pos.map(|e| e as f64); - /*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, - );*/ - + let alt_base = self.get_interpolated(pos, |chunk| chunk.alt_base)?; 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)?; @@ -161,7 +157,7 @@ impl WorldSim { Rgb::lerp( cliff, snow, - (alt - SEA_LEVEL - 215.0 - temp * 24.0) / 4.0, + (alt - SEA_LEVEL - 180.0 - alt_base - temp * 48.0) / 8.0, ), (alt - SEA_LEVEL - 100.0) / 100.0 ), @@ -194,6 +190,7 @@ pub const SEA_LEVEL: f32 = 64.0; pub struct SimChunk { pub chaos: f32, + pub alt_base: f32, pub alt: f32, pub temp: f32, pub rockiness: f32, @@ -214,13 +211,17 @@ impl SimChunk { let chaos = chaos + chaos.mul(20.0).sin().mul(0.05); let alt_base = gen_ctx.alt_nz.get((wposf.div(5000.0)).into_array()) as f32; - let alt_base = alt_base * 0.4 + alt_base.mul(16.0).sin().mul(0.01); + let alt_base = alt_base + .mul(0.4) + .add(alt_base.mul(16.0).sin().mul(0.01)) + .mul(750.0); let alt_main = gen_ctx.alt_nz.get((wposf.div(750.0)).into_array()) as f32; Self { chaos, - alt: SEA_LEVEL + alt_base, + alt: SEA_LEVEL + alt_base + (0.0 + alt_main + gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32 @@ -230,7 +231,6 @@ impl SimChunk { .add(1.0) .mul(0.5) .mul(chaos) - .add(alt_base) .mul(750.0), temp: (gen_ctx.temp_nz.get((wposf.div(48.0)).into_array()) as f32) .add(1.0) From f9680c092d3e8c43f9e36e0ade71f5a78a3fd922 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 23 May 2019 18:35:47 +0100 Subject: [PATCH 06/20] Changed snow altitude Former-commit-id: 0a673564d854e535574381874127254e83f92585 --- world/src/sim.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/world/src/sim.rs b/world/src/sim.rs index 80708108df..948a813806 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -136,7 +136,7 @@ impl WorldSim { // 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.65, 0.7, 0.85); + let cold_stone = Rgb::new(0.55, 0.75, 0.9); let warm_stone = Rgb::new(0.8, 0.6, 0.28); let sand = Rgb::new(0.93, 0.84, 0.33); let snow = Rgb::broadcast(1.0); @@ -157,7 +157,7 @@ impl WorldSim { Rgb::lerp( cliff, snow, - (alt - SEA_LEVEL - 180.0 - alt_base - temp * 48.0) / 8.0, + (alt - SEA_LEVEL - 200.0 - alt_base - temp * 48.0) / 8.0, ), (alt - SEA_LEVEL - 100.0) / 100.0 ), From 0ef85d69f5db7e2743e879d3a57b5f7c6afc932b Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 23 May 2019 21:41:01 +0100 Subject: [PATCH 07/20] Added basic structure generation Former-commit-id: 88a6bf2e18ac4f8f9b560797c8aac82ca318afb0 --- Cargo.lock | 7 ++++++ world/Cargo.toml | 1 + world/src/lib.rs | 38 ++++++++++++++++++++++++++--- world/src/sim.rs | 55 +++++++++++++++++++++++------------------- world/src/structure.rs | 50 ++++++++++++++++++++++++++++++++++++-- 5 files changed, 120 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 769da0e156..a714721806 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1129,6 +1129,11 @@ dependencies = [ "svg_fmt 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hashbrown" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "hibitset" version = "0.5.4" @@ -2707,6 +2712,7 @@ dependencies = [ name = "veloren-world" version = "0.2.0" dependencies = [ + "hashbrown 0.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)", @@ -3030,6 +3036,7 @@ dependencies = [ "checksum gtk 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d695d6be4110618a97c19cd068e8a00e53e33b87e3c65cdc5397667498b1bc24" "checksum gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d9554cf5b3a85a13fb39258c65b04b262989c1d7a758f8f555b77a478621a91" "checksum guillotiere 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "182af928b4435d8fbef910535586ecdca95ab4068493769c090e6573477f5e35" +"checksum hashbrown 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "570178d5e4952010d138b0f1d581271ff3a02406d990f887d1e87e3d6e43b0ac" "checksum hibitset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6527bc88f32e0d3926c7572874b2bf17a19b36978aacd0aacf75f7d27a5992d0" "checksum hound 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" diff --git a/world/Cargo.toml b/world/Cargo.toml index dc627a90f6..ce0e8b4c93 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" common = { package = "veloren-common", path = "../common" } vek = "0.9" noise = "0.5" +hashbrown = "0.3.0" [dev-dependencies] minifb = { git = "https://github.com/emoon/rust_minifb.git" } diff --git a/world/src/lib.rs b/world/src/lib.rs index 6559e6cdbd..4f76695699 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -1,4 +1,7 @@ +#![feature(euclidean_division)] + mod sim; +mod structure; use common::{ terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, @@ -6,6 +9,7 @@ use common::{ }; use noise::{BasicMulti, MultiFractal, NoiseFn, Perlin, Seedable}; use std::{ + hash::Hash, ops::{Add, Div, Mul, Neg, Sub}, time::Duration, }; @@ -64,7 +68,8 @@ impl World { alt, chaos, surface_color, - } = if let Some(sample) = self.sim.sample(wpos2d) { + close_trees, + } = if let Some(sample) = self.sim.sampler().sample(wpos2d) { sample } else { continue; @@ -90,6 +95,15 @@ impl World { let height = alt + warp; let temp = 0.0; + let above_ground = if (&close_trees) + .iter() + .any(|tree| tree.distance_squared(wpos.into()) < 36) + { + grass + } else { + air + }; + let z = wposf.z as f32; let _ = chunk.set( lpos, @@ -100,7 +114,7 @@ impl World { } else if z < sim::SEA_LEVEL { water } else { - air + above_ground }, ); } @@ -108,7 +122,23 @@ impl World { } chunk - - // */ + } +} + +struct Cache { + map: hashbrown::HashMap, +} + +impl Cache { + pub fn new() -> Self { + Self { + map: hashbrown::HashMap::new(), + } + } + + pub fn get V>(&mut self, k: K, f: F) -> &V { + self.map + .entry(k) + .or_insert_with(|| f(k)) } } diff --git a/world/src/sim.rs b/world/src/sim.rs index 948a813806..6abcc2b934 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -1,3 +1,7 @@ +use crate::{ + structure::StructureGen2d, + Cache, +}; use common::{terrain::TerrainChunkSize, vol::VolSize}; use noise::{ BasicMulti, HybridMulti, MultiFractal, NoiseFn, OpenSimplex, RidgedMulti, Seedable, @@ -15,6 +19,7 @@ pub struct WorldSim { pub seed: u32, chunks: Vec, gen_ctx: GenCtx, + tree_gen: StructureGen2d, } impl WorldSim { @@ -44,6 +49,7 @@ impl WorldSim { seed, chunks, gen_ctx, + tree_gen: StructureGen2d::new(seed, 64, 32), } } @@ -107,29 +113,41 @@ impl WorldSim { Some(cubic(x[0], x[1], x[2], x[3], pos.x.fract() as f32)) } - pub fn sample(&self, pos: Vec2) -> Option { - let wposf = pos.map(|e| e as f64); + pub fn sampler(&self) -> Sampler { + Sampler { + sim: self, + } + } +} - let alt_base = self.get_interpolated(pos, |chunk| chunk.alt_base)?; - 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)?; +pub struct Sampler<'a> { + sim: &'a WorldSim, +} - let rock = (self.gen_ctx.small_nz.get((wposf.div(100.0)).into_array()) as f32) +impl<'a> Sampler<'a> { + pub fn sample(&self, wpos: Vec2) -> Option { + let wposf = wpos.map(|e| e as f64); + + let alt_base = self.sim.get_interpolated(wpos, |chunk| chunk.alt_base)?; + let chaos = self.sim.get_interpolated(wpos, |chunk| chunk.chaos)?; + let temp = self.sim.get_interpolated(wpos, |chunk| chunk.temp)?; + let rockiness = self.sim.get_interpolated(wpos, |chunk| chunk.rockiness)?; + + let rock = (self.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 + let alt = self.sim.get_interpolated(wpos, |chunk| chunk.alt)? + + self.sim.gen_ctx.small_nz.get((wposf.div(128.0)).into_array()) as f32 * chaos.max(0.15) * 32.0 + rock * 15.0; let wposf3d = Vec3::new(wposf.x, wposf.y, alt as f64); - let marble = (self.gen_ctx.hill_nz.get((wposf3d.div(64.0)).into_array()) as f32) + let marble = (self.sim.gen_ctx.hill_nz.get((wposf3d.div(64.0)).into_array()) as f32) .mul(0.5) .add(1.0).mul(0.5); @@ -164,6 +182,7 @@ impl WorldSim { // Beach (alt - SEA_LEVEL - 2.0) / 5.0, ), + close_trees: self.sim.tree_gen.sample(wpos), }) } } @@ -172,6 +191,7 @@ pub struct Sample { pub alt: f32, pub chaos: f32, pub surface_color: Rgb, + pub close_trees: [Vec2; 9], } struct GenCtx { @@ -250,18 +270,3 @@ impl SimChunk { self.alt + Z_TOLERANCE.1 } } - -trait Hsv { - fn into_hsv(self) -> Self; - fn into_rgb(self) -> Self; -} - -impl Hsv for Rgb { - fn into_hsv(mut self) -> Self { - unimplemented!() - } - - fn into_rgb(mut self) -> Self { - unimplemented!() - } -} diff --git a/world/src/structure.rs b/world/src/structure.rs index ded63c8597..54c3a886ac 100644 --- a/world/src/structure.rs +++ b/world/src/structure.rs @@ -1,4 +1,50 @@ -pub struct StructureGen { - freq: f32, +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) -> 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 & 0xFFFF + } + + pub fn sample(&self, sample_pos: Vec2) -> [Vec2; 9] { + let mut samples = [Vec2::zero(); 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, + ); + } + } + + samples + } } From b2cbce8475c43df8e0e08a3260b1be4ba9cad3b4 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 24 May 2019 12:08:38 +0100 Subject: [PATCH 08/20] Refactored worldgen sampling, added cache layer Former-commit-id: 3ad5277ed0c871d6678a42629145cb722d4c76c5 --- Cargo.lock | 8 +-- common/src/terrain/mod.rs | 1 + common/src/terrain/structure.rs | 80 +++++++++++++++++++++++++ common/src/vol.rs | 10 +++- world/Cargo.toml | 2 +- world/src/lib.rs | 68 +++++++++------------ world/src/sim.rs | 103 +++++++++++++++++++++++++++----- 7 files changed, 209 insertions(+), 63 deletions(-) create mode 100644 common/src/terrain/structure.rs diff --git a/Cargo.lock b/Cargo.lock index a714721806..1fc9905ab0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1129,11 +1129,6 @@ dependencies = [ "svg_fmt 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "hashbrown" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "hibitset" version = "0.5.4" @@ -2712,7 +2707,7 @@ dependencies = [ name = "veloren-world" version = "0.2.0" dependencies = [ - "hashbrown 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash 0.2.1 (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)", @@ -3036,7 +3031,6 @@ dependencies = [ "checksum gtk 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d695d6be4110618a97c19cd068e8a00e53e33b87e3c65cdc5397667498b1bc24" "checksum gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d9554cf5b3a85a13fb39258c65b04b262989c1d7a758f8f555b77a478621a91" "checksum guillotiere 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "182af928b4435d8fbef910535586ecdca95ab4068493769c090e6573477f5e35" -"checksum hashbrown 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "570178d5e4952010d138b0f1d581271ff3a02406d990f887d1e87e3d6e43b0ac" "checksum hibitset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6527bc88f32e0d3926c7572874b2bf17a19b36978aacd0aacf75f7d27a5992d0" "checksum hound 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index ed5bb72ed8..bcaa6b7d6b 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -1,6 +1,7 @@ pub mod biome; pub mod block; pub mod chonk; +pub mod structure; // Reexports pub use self::{biome::BiomeKind, block::Block}; diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs new file mode 100644 index 0000000000..6806b06b2b --- /dev/null +++ b/common/src/terrain/structure.rs @@ -0,0 +1,80 @@ +use dot_vox::DotVoxData; +use vek::*; +use crate::{ + assets::{self, Asset, load_from_path}, + volumes::dyna::{Dyna, DynaErr}, + vol::{Vox, BaseVol, ReadVol, WriteVol}, +}; +use super::Block; + +#[derive(Debug)] +pub enum StructureError {} + +pub struct Structure { + center: Vec3, + vol: Dyna, + empty: Block, +} + +impl Structure { + pub fn with_center(mut self, center: Vec3) -> 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) -> 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 { + 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::>(); + + 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(), + }) + } + } +} diff --git a/common/src/vol.rs b/common/src/vol.rs index c9630c8a8c..3a063a6de5 100644 --- a/common/src/vol.rs +++ b/common/src/vol.rs @@ -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. diff --git a/world/Cargo.toml b/world/Cargo.toml index ce0e8b4c93..48c6b0f045 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" common = { package = "veloren-common", path = "../common" } vek = "0.9" noise = "0.5" -hashbrown = "0.3.0" +fxhash = "0.2" [dev-dependencies] minifb = { git = "https://github.com/emoon/rust_minifb.git" } diff --git a/world/src/lib.rs b/world/src/lib.rs index 4f76695699..da718b3c2e 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -14,6 +14,7 @@ use std::{ time::Duration, }; use vek::*; +use fxhash::FxHashMap; #[derive(Debug)] pub enum Error { @@ -44,32 +45,31 @@ 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 { + let sim::Sample2d { alt, chaos, surface_color, close_trees, - } = if let Some(sample) = self.sim.sampler().sample(wpos2d) { + } = if let Some(sample) = world_sampler.sample_2d(wpos2d) { sample } else { continue; @@ -84,39 +84,14 @@ impl World { 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 above_ground = if (&close_trees) - .iter() - .any(|tree| tree.distance_squared(wpos.into()) < 36) - { - grass + let sim::Sample3d { block } = if let Some(sample) = world_sampler.sample_3d(wpos) { + sample } else { - air + continue }; - 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 - } else { - above_ground - }, - ); + let _ = chunk.set(lpos, block); } } } @@ -126,19 +101,32 @@ impl World { } struct Cache { - map: hashbrown::HashMap, + capacity: usize, + map: FxHashMap, + counter: usize, } impl Cache { - pub fn new() -> Self { + pub fn with_capacity(capacity: usize) -> Self { Self { - map: hashbrown::HashMap::new(), + 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 V>(&mut self, k: K, f: F) -> &V { - self.map + let mut counter = &mut self.counter; + &self.map .entry(k) - .or_insert_with(|| f(k)) + .or_insert_with(|| { + *counter += 1; + (*counter, f(k)) + }).1 } } diff --git a/world/src/sim.rs b/world/src/sim.rs index 6abcc2b934..ba5b46720b 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -1,8 +1,11 @@ use crate::{ - structure::StructureGen2d, +structure::StructureGen2d, Cache, }; -use common::{terrain::TerrainChunkSize, vol::VolSize}; +use common::{ + terrain::{Block, TerrainChunkSize}, + vol::{Vox, VolSize}, +}; use noise::{ BasicMulti, HybridMulti, MultiFractal, NoiseFn, OpenSimplex, RidgedMulti, Seedable, SuperSimplex, @@ -36,6 +39,7 @@ 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), }; let mut chunks = Vec::new(); @@ -116,38 +120,40 @@ impl WorldSim { pub fn sampler(&self) -> Sampler { Sampler { sim: self, + sample2d_cache: Cache::with_capacity(1024), } } } pub struct Sampler<'a> { sim: &'a WorldSim, + sample2d_cache: Cache, Option>, } impl<'a> Sampler<'a> { - pub fn sample(&self, wpos: Vec2) -> Option { + fn sample_2d_impl(sim: &WorldSim, wpos: Vec2) -> Option { let wposf = wpos.map(|e| e as f64); - let alt_base = self.sim.get_interpolated(wpos, |chunk| chunk.alt_base)?; - let chaos = self.sim.get_interpolated(wpos, |chunk| chunk.chaos)?; - let temp = self.sim.get_interpolated(wpos, |chunk| chunk.temp)?; - let rockiness = self.sim.get_interpolated(wpos, |chunk| chunk.rockiness)?; + 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 rock = (self.sim.gen_ctx.small_nz.get((wposf.div(100.0)).into_array()) as f32) + 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.sim.get_interpolated(wpos, |chunk| chunk.alt)? - + self.sim.gen_ctx.small_nz.get((wposf.div(128.0)).into_array()) as f32 + let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)? + + sim.gen_ctx.small_nz.get((wposf.div(128.0)).into_array()) as f32 * chaos.max(0.15) * 32.0 + rock * 15.0; let wposf3d = Vec3::new(wposf.x, wposf.y, alt as f64); - let marble = (self.sim.gen_ctx.hill_nz.get((wposf3d.div(64.0)).into_array()) as f32) + 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); @@ -163,7 +169,7 @@ impl<'a> Sampler<'a> { let ground = Rgb::lerp(grass, warm_stone, rock.mul(5.0).min(0.8)); let cliff = Rgb::lerp(cold_stone, warm_stone, marble); - Some(Sample { + Some(Sample2d { alt, chaos, surface_color: Rgb::lerp( @@ -182,18 +188,86 @@ impl<'a> Sampler<'a> { // Beach (alt - SEA_LEVEL - 2.0) / 5.0, ), - close_trees: self.sim.tree_gen.sample(wpos), + close_trees: sim.tree_gen.sample(wpos), + }) + } + + pub fn sample_2d(&mut self, wpos2d: Vec2) -> 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) -> Option { + let wpos2d = Vec2::from(wpos); + let wposf = wpos.map(|e| e as f64); + + // Sample 2D terrain attributes + + let Sample2d { + alt, + chaos, + surface_color, + 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 = if (&close_trees) + .iter() + .any(|tree| { + tree.distance_squared(wpos.into()) < 36 + }) + { + grass + } else { + air + }; + + 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 { +#[derive(Copy, Clone)] +pub struct Sample2d { pub alt: f32, pub chaos: f32, pub surface_color: Rgb, pub close_trees: [Vec2; 9], } +pub struct Sample3d { + pub block: Block, +} + struct GenCtx { turb_x_nz: BasicMulti, turb_y_nz: BasicMulti, @@ -203,6 +277,7 @@ struct GenCtx { temp_nz: SuperSimplex, small_nz: BasicMulti, rock_nz: HybridMulti, + warp_nz: BasicMulti, } const Z_TOLERANCE: (f32, f32) = (32.0, 64.0); From 6e563dcb94647ac70cf535b2a317321b2d6c3ec8 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 24 May 2019 13:25:31 +0100 Subject: [PATCH 09/20] Basic pine tree spawning Former-commit-id: e58374724f6c9cb4a54ae96abd3420b02207fd30 --- Cargo.lock | 1 + assets/world/tree/pine/3.vox | Bin 0 -> 50630 bytes common/src/assets/mod.rs | 18 ++++++++------ common/src/terrain/mod.rs | 6 ++++- common/src/terrain/structure.rs | 3 ++- common/src/volumes/dyna.rs | 2 ++ world/Cargo.toml | 1 + world/src/sim.rs | 42 +++++++++++++++++++++----------- 8 files changed, 50 insertions(+), 23 deletions(-) create mode 100644 assets/world/tree/pine/3.vox diff --git a/Cargo.lock b/Cargo.lock index 1fc9905ab0..d113f9fc0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2708,6 +2708,7 @@ 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)", diff --git a/assets/world/tree/pine/3.vox b/assets/world/tree/pine/3.vox new file mode 100644 index 0000000000000000000000000000000000000000..94170360b77e7c7854857f72448dfc42d7c51059 GIT binary patch literal 50630 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 f6}l?<0Y6>p@0{9pRH A>(specifier: &str, f: F) -> Result, 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 +56,7 @@ lazy_static! { /// let my_image = assets::load::("core.ui.backgrounds.city").unwrap(); /// ``` pub fn load(specifier: &str) -> Result, 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. diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index bcaa6b7d6b..27a34abaa8 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -4,7 +4,11 @@ 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, diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs index 6806b06b2b..d38207d68c 100644 --- a/common/src/terrain/structure.rs +++ b/common/src/terrain/structure.rs @@ -10,6 +10,7 @@ use super::Block; #[derive(Debug)] pub enum StructureError {} +#[derive(Clone)] pub struct Structure { center: Vec3, vol: Dyna, @@ -31,7 +32,7 @@ impl BaseVol for Structure { impl ReadVol for Structure { #[inline(always)] fn get(&self, pos: Vec3) -> Result<&Block, StructureError> { - match self.vol.get(pos - self.center) { + match self.vol.get(pos + self.center) { Ok(block) => Ok(block), Err(DynaErr::OutOfBounds) => Ok(&self.empty), } diff --git a/common/src/volumes/dyna.rs b/common/src/volumes/dyna.rs index fbbbb87d3d..41cf7a446b 100644 --- a/common/src/volumes/dyna.rs +++ b/common/src/volumes/dyna.rs @@ -1,5 +1,6 @@ // Library use vek::*; +use serde_derive::{Deserialize, Serialize}; // Local use crate::vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol}; @@ -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 { vox: Vec, meta: M, diff --git a/world/Cargo.toml b/world/Cargo.toml index 48c6b0f045..dc07295de9 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -9,6 +9,7 @@ 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" } diff --git a/world/src/sim.rs b/world/src/sim.rs index ba5b46720b..d6f9262308 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -3,16 +3,19 @@ structure::StructureGen2d, Cache, }; use common::{ - terrain::{Block, TerrainChunkSize}, - vol::{Vox, VolSize}, + assets, + terrain::{Block, TerrainChunkSize, Structure}, + vol::{Vox, VolSize, ReadVol}, }; use noise::{ BasicMulti, HybridMulti, MultiFractal, NoiseFn, OpenSimplex, RidgedMulti, Seedable, SuperSimplex, }; +use lazy_static::lazy_static; use std::{ f32, ops::{Add, Div, Mul, Neg, Sub}, + sync::Arc, }; use vek::*; @@ -53,7 +56,7 @@ impl WorldSim { seed, chunks, gen_ctx, - tree_gen: StructureGen2d::new(seed, 64, 32), + tree_gen: StructureGen2d::new(seed, 48, 32), } } @@ -208,7 +211,7 @@ impl<'a> Sampler<'a> { chaos, surface_color, close_trees, - } = self.sample_2d(wpos2d)?; + } = *self.sample_2d(wpos2d)?; // Apply warping @@ -230,16 +233,19 @@ impl<'a> Sampler<'a> { let sand = Block::new(4, Rgb::new(180, 150, 50)); let water = Block::new(5, Rgb::new(100, 150, 255)); - let above_ground = if (&close_trees) + let above_ground = (&close_trees) .iter() - .any(|tree| { - tree.distance_squared(wpos.into()) < 36 - }) - { - grass - } else { - air - }; + .fold(air, |block, tree| { + match self.sample_2d(*tree) { + Some(tree_sample) => { + let tree_pos = Vec3::new(tree.x, tree.y, tree_sample.alt as i32); + block.or(TREE.get(wpos - tree_pos) + .map(|b| b.clone()) + .unwrap_or(Block::empty())) + }, + None => block, + } + }); let z = wposf.z as f32; Some(Sample3d { @@ -256,6 +262,13 @@ impl<'a> Sampler<'a> { } } +lazy_static! { + static ref TREE: Arc = assets::load_map( + "world/tree/pine/3.vox", + |s: Structure| s.with_center(Vec3::new(15, 15, 14)), + ).unwrap(); +} + #[derive(Copy, Clone)] pub struct Sample2d { pub alt: f32, @@ -264,6 +277,7 @@ pub struct Sample2d { pub close_trees: [Vec2; 9], } +#[derive(Copy, Clone)] pub struct Sample3d { pub block: Block, } @@ -280,7 +294,7 @@ struct GenCtx { warp_nz: BasicMulti, } -const Z_TOLERANCE: (f32, f32) = (32.0, 64.0); +const Z_TOLERANCE: (f32, f32) = (48.0, 64.0); pub const SEA_LEVEL: f32 = 64.0; pub struct SimChunk { From adecc4e87d9c210206bfac2a09fa7fd7cdece9f5 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 24 May 2019 13:54:21 +0100 Subject: [PATCH 10/20] Made mountains bigger Former-commit-id: c6ef0a8dba599e70177c7446fe282f357d33a66b --- world/src/sim.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/world/src/sim.rs b/world/src/sim.rs index d6f9262308..0d15b41106 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -309,9 +309,9 @@ impl SimChunk { fn generate(pos: Vec2, 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 = (gen_ctx.hill_nz.get((wposf.div(3_500.0)).into_array()) as f32).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(5_000.0)).into_array()) as f32) .add(1.0) .mul(0.5) .powf(1.9) @@ -319,13 +319,13 @@ impl SimChunk { let chaos = chaos + chaos.mul(20.0).sin().mul(0.05); - let alt_base = gen_ctx.alt_nz.get((wposf.div(5000.0)).into_array()) as f32; + let alt_base = gen_ctx.alt_nz.get((wposf.div(12_000.0)).into_array()) as f32; let alt_base = alt_base .mul(0.4) .add(alt_base.mul(16.0).sin().mul(0.01)) - .mul(750.0); + .mul(1_200.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; Self { chaos, From 2977a5d5e7354cb1433a06478a08d10acdbc0af0 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 24 May 2019 15:32:37 +0100 Subject: [PATCH 11/20] Much bigger terrain Former-commit-id: f83f281a1ec1d18079a574580d5d963416fccde2 --- world/src/sim.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/world/src/sim.rs b/world/src/sim.rs index 0d15b41106..466168dac6 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -56,7 +56,7 @@ impl WorldSim { seed, chunks, gen_ctx, - tree_gen: StructureGen2d::new(seed, 48, 32), + tree_gen: StructureGen2d::new(seed, 96, 128), } } @@ -149,9 +149,9 @@ impl<'a> Sampler<'a> { .mul(2.0); let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)? - + sim.gen_ctx.small_nz.get((wposf.div(128.0)).into_array()) as f32 - * chaos.max(0.15) - * 32.0 + + 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); @@ -161,8 +161,8 @@ impl<'a> Sampler<'a> { .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_grass = Rgb::new(0.0, 0.6, 0.2); + let warm_grass = Rgb::new(0.45, 0.9, 0.0); let cold_stone = Rgb::new(0.55, 0.75, 0.9); let warm_stone = Rgb::new(0.8, 0.6, 0.28); let sand = Rgb::new(0.93, 0.84, 0.33); @@ -184,9 +184,9 @@ impl<'a> Sampler<'a> { Rgb::lerp( cliff, snow, - (alt - SEA_LEVEL - 200.0 - alt_base - temp * 48.0) / 8.0, + (alt - SEA_LEVEL - 320.0 - alt_base - temp * 48.0) / 12.0, ), - (alt - SEA_LEVEL - 100.0) / 100.0 + (alt - SEA_LEVEL - 150.0) / 180.0 ), // Beach (alt - SEA_LEVEL - 2.0) / 5.0, @@ -295,7 +295,7 @@ struct GenCtx { } const Z_TOLERANCE: (f32, f32) = (48.0, 64.0); -pub const SEA_LEVEL: f32 = 64.0; +pub const SEA_LEVEL: f32 = 128.0; pub struct SimChunk { pub chaos: f32, @@ -309,21 +309,23 @@ impl SimChunk { fn generate(pos: Vec2, 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(3_500.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(5_000.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(12_000.0)).into_array()) as f32; + 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(16.0).sin().mul(0.01)) - .mul(1_200.0); + .mul(600.0); let alt_main = gen_ctx.alt_nz.get((wposf.div(1_500.0)).into_array()) as f32; @@ -340,7 +342,7 @@ impl SimChunk { .add(1.0) .mul(0.5) .mul(chaos) - .mul(750.0), + .mul(1200.0), temp: (gen_ctx.temp_nz.get((wposf.div(48.0)).into_array()) as f32) .add(1.0) .mul(0.5), From d7b668cccff76428e90e7c72be0e3a13921925a5 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 25 May 2019 00:00:44 +0100 Subject: [PATCH 12/20] Improved sea-like slopes Former-commit-id: 4b3ba56dcf6d99db7e48c169d42e042811872fd3 --- world/src/sim.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/world/src/sim.rs b/world/src/sim.rs index 466168dac6..035c083bbd 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -184,7 +184,7 @@ impl<'a> Sampler<'a> { Rgb::lerp( cliff, snow, - (alt - SEA_LEVEL - 320.0 - alt_base - temp * 48.0) / 12.0, + (alt - SEA_LEVEL - 350.0 - alt_base - temp * 48.0) / 12.0, ), (alt - SEA_LEVEL - 150.0) / 180.0 ), @@ -325,6 +325,7 @@ impl SimChunk { 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(1_500.0)).into_array()) as f32; @@ -354,7 +355,7 @@ impl SimChunk { } pub fn get_base_z(&self) -> f32 { - self.alt - Z_TOLERANCE.0 * (self.chaos + 0.1) + self.alt - Z_TOLERANCE.0 * (self.chaos + 0.1) - 3.0 } pub fn get_max_z(&self) -> f32 { From 32f7d496d1ad28329fa0b2f31dddd17e25d2a1df Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 25 May 2019 06:38:40 +0100 Subject: [PATCH 13/20] Better tree spawning, more trees Former-commit-id: abf4ff8a2eb005c2cd1832f5a3ae7182c4fc31f6 --- assets/world/tree/oak/1.vox | Bin 0 -> 80890 bytes assets/world/tree/oak/2.vox | Bin 0 -> 89306 bytes assets/world/tree/oak/3.vox | Bin 0 -> 114974 bytes assets/world/tree/pine/4.vox | Bin 0 -> 54202 bytes assets/world/tree/pine/5.vox | Bin 0 -> 56194 bytes assets/world/tree/temperate/1.vox | Bin 0 -> 45199 bytes assets/world/tree/temperate/2.vox | Bin 0 -> 45415 bytes assets/world/tree/temperate/3.vox | Bin 0 -> 45395 bytes assets/world/tree/temperate/4.vox | Bin 0 -> 45063 bytes assets/world/tree/temperate/5.vox | Bin 0 -> 45055 bytes assets/world/tree/temperate/6.vox | Bin 0 -> 45231 bytes world/src/lib.rs | 11 -------- world/src/sim.rs | 45 ++++++++++++++++++++++-------- world/src/structure.rs | 10 +++---- 14 files changed, 38 insertions(+), 28 deletions(-) create mode 100644 assets/world/tree/oak/1.vox create mode 100644 assets/world/tree/oak/2.vox create mode 100644 assets/world/tree/oak/3.vox create mode 100644 assets/world/tree/pine/4.vox create mode 100644 assets/world/tree/pine/5.vox create mode 100644 assets/world/tree/temperate/1.vox create mode 100644 assets/world/tree/temperate/2.vox create mode 100644 assets/world/tree/temperate/3.vox create mode 100644 assets/world/tree/temperate/4.vox create mode 100644 assets/world/tree/temperate/5.vox create mode 100644 assets/world/tree/temperate/6.vox diff --git a/assets/world/tree/oak/1.vox b/assets/world/tree/oak/1.vox new file mode 100644 index 0000000000000000000000000000000000000000..55a51ce1818e4f35eb2265cfe97294747ea71c9e GIT binary patch literal 80890 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 literal 0 HcmV?d00001 diff --git a/assets/world/tree/oak/2.vox b/assets/world/tree/oak/2.vox new file mode 100644 index 0000000000000000000000000000000000000000..dbf256f4e15303e4e542833c56f1d4ac5a8c081c GIT binary patch literal 89306 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 UkhyK#k!_yaEu3SH9`^6|0Ux$|#sB~S literal 0 HcmV?d00001 diff --git a/assets/world/tree/oak/3.vox b/assets/world/tree/oak/3.vox new file mode 100644 index 0000000000000000000000000000000000000000..c5c3841aa4ff2eaaa6a022aff6cca42bba89a786 GIT binary patch literal 114974 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 RZ-uX`;4DU1Jhe{#`ys7^jNbqN literal 0 HcmV?d00001 diff --git a/assets/world/tree/pine/4.vox b/assets/world/tree/pine/4.vox new file mode 100644 index 0000000000000000000000000000000000000000..17f1257ec7edfa7a58b637680b4096094081f93e GIT binary patch literal 54202 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 literal 0 HcmV?d00001 diff --git a/assets/world/tree/pine/5.vox b/assets/world/tree/pine/5.vox new file mode 100644 index 0000000000000000000000000000000000000000..1a6c8afbd83c9a2c54a7ef5b49901e5b48e45f88 GIT binary patch literal 56194 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 literal 0 HcmV?d00001 diff --git a/assets/world/tree/temperate/1.vox b/assets/world/tree/temperate/1.vox new file mode 100644 index 0000000000000000000000000000000000000000..9c890f12832574ffd6fb03075f7bf5f17e810787 GIT binary patch literal 45199 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 X&{oyOtGj8|FKRjM5vxxuf?xjyoi+f{ literal 0 HcmV?d00001 diff --git a/assets/world/tree/temperate/4.vox b/assets/world/tree/temperate/4.vox new file mode 100644 index 0000000000000000000000000000000000000000..de1120f1258dbb93013443f6d0dd93a7cd0c3bb0 GIT binary patch literal 45063 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 literal 0 HcmV?d00001 diff --git a/assets/world/tree/temperate/5.vox b/assets/world/tree/temperate/5.vox new file mode 100644 index 0000000000000000000000000000000000000000..a6f0b26004970313538b568c6b4fdfff4bd0fe45 GIT binary patch literal 45055 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 literal 0 HcmV?d00001 diff --git a/assets/world/tree/temperate/6.vox b/assets/world/tree/temperate/6.vox new file mode 100644 index 0000000000000000000000000000000000000000..6c4e9ebb711d20428ecf51201dd8691d82e8ca8a GIT binary patch literal 45231 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 literal 0 HcmV?d00001 diff --git a/world/src/lib.rs b/world/src/lib.rs index da718b3c2e..8909f25e6d 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -64,17 +64,6 @@ impl World { + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32); let wposf2d = wpos2d.map(|e| e as f64); - let sim::Sample2d { - alt, - chaos, - surface_color, - close_trees, - } = if let Some(sample) = world_sampler.sample_2d(wpos2d) { - sample - } else { - continue; - }; - let max_z = self .sim .get_interpolated(wpos2d, |chunk| chunk.get_max_z()) diff --git a/world/src/sim.rs b/world/src/sim.rs index 035c083bbd..9bc7377373 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -43,6 +43,7 @@ impl WorldSim { 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(6).set_seed(seed + 9), }; let mut chunks = Vec::new(); @@ -56,7 +57,7 @@ impl WorldSim { seed, chunks, gen_ctx, - tree_gen: StructureGen2d::new(seed, 96, 128), + tree_gen: StructureGen2d::new(seed, 32, 32), } } @@ -141,6 +142,7 @@ impl<'a> Sampler<'a> { 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) @@ -191,6 +193,7 @@ impl<'a> Sampler<'a> { // Beach (alt - SEA_LEVEL - 2.0) / 5.0, ), + tree_density, close_trees: sim.tree_gen.sample(wpos), }) } @@ -210,6 +213,7 @@ impl<'a> Sampler<'a> { alt, chaos, surface_color, + tree_density, close_trees, } = *self.sample_2d(wpos2d)?; @@ -235,15 +239,15 @@ impl<'a> Sampler<'a> { let above_ground = (&close_trees) .iter() - .fold(air, |block, tree| { - match self.sample_2d(*tree) { - Some(tree_sample) => { - let tree_pos = Vec3::new(tree.x, tree.y, tree_sample.alt as i32); - block.or(TREE.get(wpos - tree_pos) + .fold(air, |block, (tree_pos, tree_seed)| { + match self.sample_2d(*tree_pos) { + Some(tree_sample) if tree_sample.tree_density > 0.5 => { + 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())) }, - None => block, + _ => block, } }); @@ -263,10 +267,20 @@ impl<'a> Sampler<'a> { } lazy_static! { - static ref TREE: Arc = assets::load_map( - "world/tree/pine/3.vox", - |s: Structure| s.with_center(Vec3::new(15, 15, 14)), - ).unwrap(); + static ref TREES: [Arc; 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)] @@ -274,7 +288,8 @@ pub struct Sample2d { pub alt: f32, pub chaos: f32, pub surface_color: Rgb, - pub close_trees: [Vec2; 9], + pub tree_density: f32, + pub close_trees: [(Vec2, u32); 9], } #[derive(Copy, Clone)] @@ -292,6 +307,7 @@ struct GenCtx { small_nz: BasicMulti, rock_nz: HybridMulti, warp_nz: BasicMulti, + tree_nz: BasicMulti, } const Z_TOLERANCE: (f32, f32) = (48.0, 64.0); @@ -303,6 +319,7 @@ pub struct SimChunk { pub alt: f32, pub temp: f32, pub rockiness: f32, + pub tree_density: f32, } impl SimChunk { @@ -351,6 +368,10 @@ 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.75), } } diff --git a/world/src/structure.rs b/world/src/structure.rs index 54c3a886ac..55e5a5a532 100644 --- a/world/src/structure.rs +++ b/world/src/structure.rs @@ -25,11 +25,11 @@ impl StructureGen2d { 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 & 0xFFFF + next } - pub fn sample(&self, sample_pos: Vec2) -> [Vec2; 9] { - let mut samples = [Vec2::zero(); 9]; + pub fn sample(&self, sample_pos: Vec2) -> [(Vec2, u32); 9] { + let mut samples = [(Vec2::zero(), 0); 9]; let sample_closest = sample_pos.map(|e| e - e.rem_euclid(self.freq as i32)); @@ -38,10 +38,10 @@ impl StructureGen2d { 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( + 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)); } } From 9ed836747c0476cdf8502d94e0bbd8225b41c0f7 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 25 May 2019 06:42:26 +0100 Subject: [PATCH 14/20] Better spawn point Former-commit-id: 31b4df016aea512494d419515043baa288fa9ab5 --- server/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/lib.rs b/server/src/lib.rs index 33267b2584..3b98f194ce 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -76,7 +76,7 @@ impl Server { state.ecs_mut().register::(); 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, From 4891da411deee68281ab42a337c521b1393b6e9c Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 25 May 2019 06:47:11 +0100 Subject: [PATCH 15/20] Improved grass and tree colour Former-commit-id: 9372612047df95ec9588d35a32161248df85178a --- world/src/sim.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/world/src/sim.rs b/world/src/sim.rs index 9bc7377373..ffd6a5abf5 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -163,10 +163,10 @@ impl<'a> Sampler<'a> { .add(1.0).mul(0.5); // Colours - let cold_grass = Rgb::new(0.0, 0.6, 0.2); - let warm_grass = Rgb::new(0.45, 0.9, 0.0); + 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.8, 0.6, 0.28); + 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); From 6ded009f5a95513fd40c6f4e311eb076de0a06d8 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 25 May 2019 06:49:55 +0100 Subject: [PATCH 16/20] Marginally increased tree spawn frequency Former-commit-id: c23a172e4796317bafdf4a6661330ed86d4eb248 --- world/src/sim.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/world/src/sim.rs b/world/src/sim.rs index ffd6a5abf5..00bebb6d3a 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -371,7 +371,7 @@ impl SimChunk { 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.75), + .mul(1.0 - chaos), } } From 6ac6b7fd86a0b5a2d04cd4c4575f166db53255f7 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 25 May 2019 06:54:47 +0100 Subject: [PATCH 17/20] fmt Former-commit-id: 689464ed1009ebfd74769fa113fc7b433ea62bbd --- common/src/assets/mod.rs | 5 +- common/src/terrain/chonk.rs | 8 +- common/src/terrain/mod.rs | 6 +- common/src/terrain/structure.rs | 22 +++--- common/src/volumes/dyna.rs | 2 +- world/src/lib.rs | 19 +++-- world/src/sim.rs | 127 +++++++++++++++++++++----------- world/src/structure.rs | 34 ++++++--- 8 files changed, 137 insertions(+), 86 deletions(-) diff --git a/common/src/assets/mod.rs b/common/src/assets/mod.rs index e8e8c5bd28..00c65adfb3 100644 --- a/common/src/assets/mod.rs +++ b/common/src/assets/mod.rs @@ -36,7 +36,10 @@ lazy_static! { RwLock::new(HashMap::new()); } -pub fn load_map A>(specifier: &str, f: F) -> Result, Error> { +pub fn load_map A>( + specifier: &str, + f: F, +) -> Result, Error> { Ok(ASSETS .write() .unwrap() diff --git a/common/src/terrain/chonk.rs b/common/src/terrain/chonk.rs index a15e521e83..e5c4de955b 100644 --- a/common/src/terrain/chonk.rs +++ b/common/src/terrain/chonk.rs @@ -4,8 +4,8 @@ use crate::{ volumes::chunk::{Chunk, ChunkErr}, }; use serde_derive::{Deserialize, Serialize}; -use vek::*; use std::collections::HashMap; +use vek::*; #[derive(Debug)] pub enum ChonkError { @@ -76,7 +76,7 @@ impl ReadVol for Chonk { * (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() @@ -113,11 +113,11 @@ impl WriteVol for Chonk { 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) diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index 27a34abaa8..c60c7ec99a 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -4,11 +4,7 @@ pub mod chonk; pub mod structure; // Reexports -pub use self::{ - biome::BiomeKind, - block::Block, - structure::Structure, -}; +pub use self::{biome::BiomeKind, block::Block, structure::Structure}; use crate::{ vol::VolSize, diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs index d38207d68c..7e455851ec 100644 --- a/common/src/terrain/structure.rs +++ b/common/src/terrain/structure.rs @@ -1,11 +1,11 @@ +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::*; -use crate::{ - assets::{self, Asset, load_from_path}, - volumes::dyna::{Dyna, DynaErr}, - vol::{Vox, BaseVol, ReadVol, WriteVol}, -}; -use super::Block; #[derive(Debug)] pub enum StructureError {} @@ -50,11 +50,11 @@ impl Asset for Structure { .map(|col| Rgba::from(col.to_ne_bytes()).into()) .collect::>(); - let mut vol = Dyna::filled(Vec3::new( - model.size.x, - model.size.y, - model.size.z, - ), Block::empty(), ()); + 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) { diff --git a/common/src/volumes/dyna.rs b/common/src/volumes/dyna.rs index 41cf7a446b..9c0968dbc7 100644 --- a/common/src/volumes/dyna.rs +++ b/common/src/volumes/dyna.rs @@ -1,6 +1,6 @@ // Library -use vek::*; use serde_derive::{Deserialize, Serialize}; +use vek::*; // Local use crate::vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol}; diff --git a/world/src/lib.rs b/world/src/lib.rs index 8909f25e6d..16a3acb68b 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -7,6 +7,7 @@ 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, @@ -14,7 +15,6 @@ use std::{ time::Duration, }; use vek::*; -use fxhash::FxHashMap; #[derive(Debug)] pub enum Error { @@ -74,11 +74,12 @@ impl World { let wpos = lpos + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32); - let sim::Sample3d { block } = if let Some(sample) = world_sampler.sample_3d(wpos) { - sample - } else { - continue - }; + let sim::Sample3d { block } = + if let Some(sample) = world_sampler.sample_3d(wpos) { + sample + } else { + continue; + }; let _ = chunk.set(lpos, block); } @@ -111,11 +112,13 @@ impl Cache { pub fn get V>(&mut self, k: K, f: F) -> &V { let mut counter = &mut self.counter; - &self.map + &self + .map .entry(k) .or_insert_with(|| { *counter += 1; (*counter, f(k)) - }).1 + }) + .1 } } diff --git a/world/src/sim.rs b/world/src/sim.rs index 00bebb6d3a..e2d375895a 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -1,17 +1,14 @@ -use crate::{ -structure::StructureGen2d, - Cache, -}; +use crate::{structure::StructureGen2d, Cache}; use common::{ assets, - terrain::{Block, TerrainChunkSize, Structure}, - vol::{Vox, VolSize, ReadVol}, + terrain::{Block, Structure, TerrainChunkSize}, + vol::{ReadVol, VolSize, Vox}, }; +use lazy_static::lazy_static; use noise::{ BasicMulti, HybridMulti, MultiFractal, NoiseFn, OpenSimplex, RidgedMulti, Seedable, SuperSimplex, }; -use lazy_static::lazy_static; use std::{ f32, ops::{Add, Div, Mul, Neg, Sub}, @@ -110,10 +107,11 @@ impl WorldSim { let mut x = [T::default(); 4]; 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))?); + 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))?); x[x_idx] = cubic(y0, y1, y2, y3, pos.y.fract() as f32); } @@ -160,7 +158,8 @@ impl<'a> Sampler<'a> { 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); + .add(1.0) + .mul(0.5); // Colours let cold_grass = Rgb::new(0.05, 0.5, 0.3); @@ -188,7 +187,7 @@ impl<'a> Sampler<'a> { snow, (alt - SEA_LEVEL - 350.0 - alt_base - temp * 48.0) / 12.0, ), - (alt - SEA_LEVEL - 150.0) / 180.0 + (alt - SEA_LEVEL - 150.0) / 180.0, ), // Beach (alt - SEA_LEVEL - 2.0) / 5.0, @@ -200,7 +199,9 @@ impl<'a> Sampler<'a> { pub fn sample_2d(&mut self, wpos2d: Vec2) -> Option<&Sample2d> { let sim = &self.sim; - self.sample2d_cache.get(wpos2d, |wpos2d| Self::sample_2d_impl(sim, wpos2d)).as_ref() + self.sample2d_cache + .get(wpos2d, |wpos2d| Self::sample_2d_impl(sim, wpos2d)) + .as_ref() } pub fn sample_3d(&mut self, wpos: Vec3) -> Option { @@ -219,7 +220,10 @@ impl<'a> Sampler<'a> { // Apply warping - let warp = (self.sim.gen_ctx.warp_nz + 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)) @@ -237,19 +241,22 @@ impl<'a> Sampler<'a> { 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 => { - 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 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 => { + 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 { @@ -268,18 +275,42 @@ impl<'a> Sampler<'a> { lazy_static! { static ref TREES: [Arc; 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(), + 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(), ]; } @@ -327,9 +358,16 @@ impl SimChunk { let wposf = (pos * Vec2::from(TerrainChunkSize::SIZE)).map(|e| e as f64); 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); + + 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(4_000.0)).into_array()) as f32) .add(1.0) @@ -350,7 +388,8 @@ impl SimChunk { Self { chaos, alt_base, - alt: SEA_LEVEL + alt_base + alt: SEA_LEVEL + + alt_base + (0.0 + alt_main + gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32 diff --git a/world/src/structure.rs b/world/src/structure.rs index 55e5a5a532..4e51c0f206 100644 --- a/world/src/structure.rs +++ b/world/src/structure.rs @@ -8,20 +8,24 @@ pub struct StructureGen2d { impl StructureGen2d { pub fn new(seed: u32, freq: u32, spread: u32) -> Self { - Self { - seed, - freq, - spread, - } + Self { seed, freq, spread } } fn random(&self, seed: u32, pos: Vec2) -> 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 = (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(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; @@ -38,10 +42,16 @@ impl StructureGen2d { 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[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), + ); } } From 6c03de56aaddd9648781a790c57d35f8970e2eee Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 25 May 2019 07:00:24 +0100 Subject: [PATCH 18/20] Fixed world viewer Former-commit-id: 03113582d1bd9bce6848c66b9c73a2ba2939162e --- world/examples/view.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/world/examples/view.rs b/world/examples/view.rs index cc5ed772ae..5f30cc097e 100644 --- a/world/examples/view.rs +++ b/world/examples/view.rs @@ -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); From 41c084f28b5cebfcd1511516e14565fcb074dd55 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 25 May 2019 08:36:11 +0100 Subject: [PATCH 19/20] Stopped trees spawning in water Former-commit-id: 1cc73b0eea4126da4501e19a695b572efc436935 --- world/src/sim.rs | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/world/src/sim.rs b/world/src/sim.rs index e2d375895a..dafa16745a 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -40,7 +40,10 @@ impl WorldSim { 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(6).set_seed(seed + 9), + tree_nz: BasicMulti::new() + .set_octaves(8) + .set_persistence(0.75) + .set_seed(seed + 9), }; let mut chunks = Vec::new(); @@ -246,7 +249,10 @@ impl<'a> Sampler<'a> { .iter() .fold(air, |block, (tree_pos, tree_seed)| { match self.sample_2d(*tree_pos) { - Some(tree_sample) if tree_sample.tree_density > 0.5 => { + 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()] @@ -385,21 +391,23 @@ impl SimChunk { 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_base, - 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), + alt, temp: (gen_ctx.temp_nz.get((wposf.div(48.0)).into_array()) as f32) .add(1.0) .mul(0.5), @@ -410,7 +418,9 @@ impl SimChunk { 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), + .mul(1.0 - chaos * 0.8) + .add(0.1) + .mul(if alt > SEA_LEVEL + 3.0 { 1.0 } else { 0.0 }), } } From 20648f8aa7cff67e2bd52fbd5e920f4e914abbb2 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 25 May 2019 09:38:30 +0100 Subject: [PATCH 20/20] Added docs to assets::load_map Former-commit-id: 50397d3c5d8c30ea392b784d9309d0e2ecb28f73 --- common/src/assets/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/common/src/assets/mod.rs b/common/src/assets/mod.rs index 00c65adfb3..f691f296fe 100644 --- a/common/src/assets/mod.rs +++ b/common/src/assets/mod.rs @@ -36,6 +36,15 @@ 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::("core.ui.backgrounds.city").unwrap(); +/// ``` pub fn load_map A>( specifier: &str, f: F,