diff --git a/voxygen/src/mesh/vol.rs b/voxygen/src/mesh/vol.rs index 50d66a9135..bb866297a6 100644 --- a/voxygen/src/mesh/vol.rs +++ b/voxygen/src/mesh/vol.rs @@ -41,7 +41,7 @@ fn get_ao_quad(vol: &V, pos: Vec3, shift: Vec3, dirs: &[Ve .unwrap_or(false); // Map both 1 and 2 neighbors to 0.5 occlusion. if s1 || s2 || corner { - 0.5 + 0.3 } else { 1.0 } diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 1c46ddd2a1..c78d8bf874 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -58,12 +58,10 @@ impl SessionState { } // TODO: Get rid of this when we're done with it (we're not yet) - /* match self.client.borrow().current_chunk() { Some(chunk) => println!("Chunk location: {:?}", chunk.meta().name()), None => {} } - */ Ok(()) } diff --git a/world/examples/view.rs b/world/examples/view.rs index e358924625..9fd167e327 100644 --- a/world/examples/view.rs +++ b/world/examples/view.rs @@ -15,35 +15,44 @@ fn main() { let mut focus = Vec2::zero(); let mut gain = 1.0; + let mut scale = 4; while win.is_open() { let mut buf = vec![0; W * H]; for i in 0..W { for j in 0..H { - let pos = focus + Vec2::new(i as i32, j as i32) * 4; + let pos = focus + Vec2::new(i as i32, j as i32) * scale; - let alt = sampler + let (alt, location) = sampler .get(pos) - .map(|sample| sample.alt.sub(64.0).add(gain).mul(0.7).max(0.0).min(255.0) as u8) - .unwrap_or(0); + .map(|sample| ( + sample.alt.sub(64.0).add(gain).mul(0.7).max(0.0).min(255.0) as u8, + sample.location, + )) + .unwrap_or((0, None)); - buf[j * W + i] = u32::from_le_bytes([alt; 4]); + let loc_color = location.map(|l| ( + l.name().bytes().nth(0).unwrap() * 17, + l.name().bytes().nth(1).unwrap() * 17, + )).unwrap_or((0, 0)); + + buf[j * W + i] = u32::from_le_bytes([loc_color.0, loc_color.1, alt, alt]); } } let spd = 32; if win.is_key_down(minifb::Key::W) { - focus.y -= spd; + focus.y -= spd * scale; } if win.is_key_down(minifb::Key::A) { - focus.x -= spd; + focus.x -= spd * scale; } if win.is_key_down(minifb::Key::S) { - focus.y += spd; + focus.y += spd * scale; } if win.is_key_down(minifb::Key::D) { - focus.x += spd; + focus.x += spd * scale; } if win.is_key_down(minifb::Key::Q) { gain += 10.0; @@ -51,6 +60,12 @@ fn main() { if win.is_key_down(minifb::Key::E) { gain -= 10.0; } + if win.is_key_down(minifb::Key::R) { + scale += 1; + } + if win.is_key_down(minifb::Key::F) { + scale -= 1; + } win.update_with_buffer(&buf).unwrap(); } diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 06c9ddc0bd..e4e2025783 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -15,7 +15,7 @@ use vek::*; pub struct BlockGen<'a> { world: &'a World, - column_cache: HashCache, Option>, + column_cache: HashCache, Option>>, column_gen: ColumnGen<'a>, } @@ -53,6 +53,7 @@ impl<'a> SamplerMut for BlockGen<'a> { rock, cliff, temp, + .. } = self.sample_column(Vec2::from(wpos))?; let wposf = wpos.map(|e| e as f64); diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 551267964c..41c9b4809b 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1,10 +1,19 @@ -use crate::{all::ForestKind, util::Sampler, World, CONFIG}; +use crate::{ + all::ForestKind, + util::Sampler, + sim::Location, + World, + CONFIG, +}; use common::{ terrain::{Block, TerrainChunkSize}, vol::{VolSize, Vox}, }; use noise::NoiseFn; -use std::ops::{Add, Div, Mul, Neg, Sub}; +use std::{ + ops::{Add, Div, Mul, Neg, Sub}, + sync::Arc, +}; use vek::*; pub struct ColumnGen<'a> { @@ -19,12 +28,12 @@ impl<'a> ColumnGen<'a> { impl<'a> Sampler for ColumnGen<'a> { type Index = Vec2; - type Sample = Option; + type Sample = Option>; - fn get(&self, wpos: Vec2) -> Option { + fn get(&self, wpos: Vec2) -> Option> { let wposf = wpos.map(|e| e as f64); let chunk_pos = wpos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| { - e as u32 / sz + e / sz as i32 }); let sim = self.world.sim(); @@ -36,7 +45,7 @@ impl<'a> Sampler for ColumnGen<'a> { let cliffiness = sim.get_interpolated(wpos, |chunk| chunk.cliffiness)?; let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?; - let forest_kind = sim.get(chunk_pos)?.forest_kind; + let sim_chunk = sim.get(chunk_pos)?; let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)? + (sim.gen_ctx.small_nz.get((wposf.div(256.0)).into_array()) as f32) @@ -140,19 +149,20 @@ impl<'a> Sampler for ColumnGen<'a> { (alt - CONFIG.sea_level - 2.0) / 5.0, ), tree_density, - forest_kind, + forest_kind: sim_chunk.forest_kind, close_trees: sim.gen_ctx.tree_gen.get(wpos), cave_xy, cave_alt, rock, cliff: cliffiness, temp, + location: sim_chunk.location.as_ref(), }) } } #[derive(Clone)] -pub struct ColumnSample { +pub struct ColumnSample<'a> { pub alt: f32, pub chaos: f32, pub surface_color: Rgb, @@ -164,4 +174,5 @@ pub struct ColumnSample { pub rock: f32, pub cliff: f32, pub temp: f32, + pub location: Option<&'a Arc> } diff --git a/world/src/lib.rs b/world/src/lib.rs index e3528616c4..e2a0831fb1 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -4,7 +4,7 @@ mod all; mod block; mod column; pub mod config; -mod sim; +pub mod sim; pub mod util; // Reexports @@ -62,15 +62,20 @@ impl World { let water = Block::new(5, Rgb::new(100, 150, 255)); let chunk_size2d = Vec2::from(TerrainChunkSize::SIZE); - let base_z = match self.sim.get_interpolated( + let (base_z, sim_chunk) = match self.sim.get_interpolated( chunk_pos.map2(chunk_size2d, |e, sz: u32| e * sz as i32 + sz as i32 / 2), |chunk| chunk.get_base_z(), - ) { - Some(base_z) => base_z as i32, + ).and_then(|base_z| self.sim.get(chunk_pos).map(|sim_chunk| (base_z, sim_chunk))) { + Some((base_z, sim_chunk)) => (base_z as i32, sim_chunk), None => return TerrainChunk::new(0, water, air, TerrainChunkMeta::void()), }; - let mut chunk = TerrainChunk::new(base_z - 8, stone, air, TerrainChunkMeta::void()); + let meta = TerrainChunkMeta::new( + sim_chunk.get_name(), + sim_chunk.get_biome(), + ); + + let mut chunk = TerrainChunk::new(base_z - 8, stone, air, meta); let mut sampler = self.sample_blocks(); diff --git a/world/src/sim/location.rs b/world/src/sim/location.rs index 892e8e62a8..34eb7d02a3 100644 --- a/world/src/sim/location.rs +++ b/world/src/sim/location.rs @@ -1,17 +1,37 @@ use rand::Rng; +use vek::*; #[derive(Copy, Clone, Debug)] pub enum LocationKind { Settlement, - Mountain, - Forest, + Wildnerness, } #[derive(Clone, Debug)] pub struct Location { name: String, + center: Vec2, kind: LocationKind, - kingdom: Kingdom, + kingdom: Option, +} + +impl Location { + pub fn generate(center: Vec2, rng: &mut R) -> Self { + Self { + name: generate_name(rng), + center, + kind: LocationKind::Wildnerness, + kingdom: None, + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn kingdom(&self) -> Option<&Kingdom> { + self.kingdom.as_ref() + } } #[derive(Clone, Debug)] @@ -19,7 +39,7 @@ pub struct Kingdom { name: String, } -fn generate_name() -> String { +fn generate_name(rng: &mut R) -> String { let consts = [ "st", "tr", "b", "n", "p", "ph", "cr", "g", "c", "d", "k", "kr", "kl", "gh", "sl", "st", "cr", "sp", "th", "dr", "pr", "dr", "gr", "br", "ryth", "rh", "sl", "f", "fr", "p", "pr", diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 501e95f5dd..6cb1241a97 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -1,14 +1,24 @@ mod location; -use self::location::Location; +// Reexports +pub use self::location::Location; + use crate::{all::ForestKind, util::StructureGen2d, CONFIG}; -use common::{terrain::TerrainChunkSize, vol::VolSize}; +use common::{ + terrain::{BiomeKind, TerrainChunkSize}, + vol::VolSize, +}; use noise::{BasicMulti, HybridMulti, MultiFractal, NoiseFn, RidgedMulti, Seedable, SuperSimplex}; use std::{ ops::{Add, Div, Mul, Neg, Sub}, sync::Arc, }; use vek::*; +use rand::{ + Rng, + SeedableRng, + prng::XorShiftRng, +}; pub const WORLD_SIZE: Vec2 = Vec2 { x: 1024, y: 1024 }; @@ -35,6 +45,7 @@ pub struct WorldSim { pub seed: u32, pub(crate) chunks: Vec, pub(crate) gen_ctx: GenCtx, + pub rng: XorShiftRng, } impl WorldSim { @@ -64,8 +75,8 @@ impl WorldSim { }; let mut chunks = Vec::new(); - for x in 0..WORLD_SIZE.x as u32 { - for y in 0..WORLD_SIZE.y as u32 { + for x in 0..WORLD_SIZE.x as i32 { + for y in 0..WORLD_SIZE.y as i32 { chunks.push(SimChunk::generate(Vec2::new(x, y), &mut gen_ctx)); } } @@ -74,20 +85,70 @@ impl WorldSim { seed, chunks, gen_ctx, + rng: XorShiftRng::from_seed([ + (seed >> 0) as u8, 0, 0, 0, + (seed >> 8) as u8, 0, 0, 0, + (seed >> 16) as u8, 0, 0, 0, + (seed >> 24) as u8, 0, 0, 0, + ]), }; - this.simulate(100); + this.seed_elements(); + this.simulate(200); this } - pub fn simulate(&mut self, cycles: usize) { - // TODO + /// Prepare the world for simulation + pub fn seed_elements(&mut self) { + let mut rng = self.rng.clone(); + + for _ in 0..250 { + let loc_center = Vec2::new( + self.rng.gen::() % WORLD_SIZE.x as i32, + self.rng.gen::() % WORLD_SIZE.y as i32, + ); + + if let Some(chunk) = self.get_mut(loc_center) { + chunk.location = Some(Location::generate(loc_center, &mut rng).into()); + } + } + + self.rng = rng; } - pub fn get(&self, chunk_pos: Vec2) -> Option<&SimChunk> { + pub fn simulate(&mut self, cycles: usize) { + let mut rng = self.rng.clone(); + + for _ in 0..cycles { + for i in 0..WORLD_SIZE.x as i32 { + for j in 0..WORLD_SIZE.y as i32 { + let pos = Vec2::new(i, j); + + let location = self.get(pos).unwrap().location.clone(); + + let rpos = Vec2::new( + rng.gen::(), + rng.gen::(), + ).map(|e| e.abs() % 3 - 1); + + if let Some(other) = &mut self.get_mut(pos + rpos) { + if other.location.is_none() + && rng.gen::() > other.chaos * 1.5 + && other.alt > CONFIG.sea_level { + other.location = location; + } + } + } + } + } + + self.rng = rng; + } + + pub fn get(&self, chunk_pos: Vec2) -> Option<&SimChunk> { if chunk_pos - .map2(WORLD_SIZE, |e, sz| e < sz as u32) + .map2(WORLD_SIZE, |e, sz| e >= 0 && e < sz as i32) .reduce_and() { Some(&self.chunks[chunk_pos.y as usize * WORLD_SIZE.x + chunk_pos.x as usize]) @@ -96,7 +157,18 @@ impl WorldSim { } } - pub fn get_base_z(&self, chunk_pos: Vec2) -> Option { + pub fn get_mut(&mut self, chunk_pos: Vec2) -> Option<&mut SimChunk> { + if chunk_pos + .map2(WORLD_SIZE, |e, sz| e >= 0 && e < sz as i32) + .reduce_and() + { + Some(&mut self.chunks[chunk_pos.y as usize * WORLD_SIZE.x + chunk_pos.x as usize]) + } else { + None + } + } + + pub fn get_base_z(&self, chunk_pos: Vec2) -> Option { self.get(chunk_pos).and_then(|_| { (0..2) .map(|i| (0..2).map(move |j| (i, j))) @@ -135,10 +207,10 @@ impl WorldSim { 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))?); + f(self.get(pos.map2(Vec2::new(j, -1), |e, q| e.max(0.0) as i32 + q))?); + let y1 = f(self.get(pos.map2(Vec2::new(j, 0), |e, q| e.max(0.0) as i32 + q))?); + let y2 = f(self.get(pos.map2(Vec2::new(j, 1), |e, q| e.max(0.0) as i32 + q))?); + let y3 = f(self.get(pos.map2(Vec2::new(j, 2), |e, q| e.max(0.0) as i32 + q))?); x[x_idx] = cubic(y0, y1, y2, y3, pos.y.fract() as f32); } @@ -162,8 +234,8 @@ pub struct SimChunk { } impl SimChunk { - fn generate(pos: Vec2, gen_ctx: &mut GenCtx) -> Self { - let wposf = (pos * Vec2::from(TerrainChunkSize::SIZE)).map(|e| e as f64); + fn generate(pos: Vec2, gen_ctx: &mut GenCtx) -> Self { + let wposf = (pos * TerrainChunkSize::SIZE.map(|e| e as i32)).map(|e| e as f64); let hill = (0.0 + gen_ctx @@ -180,6 +252,7 @@ impl SimChunk { let chaos = (gen_ctx.chaos_nz.get((wposf.div(4_000.0)).into_array()) as f32) .add(1.0) .mul(0.5) + .mul((gen_ctx.chaos_nz.get((wposf.div(8_000.0)).into_array()) as f32).powf(2.0).add(0.5).min(1.0)) .powf(1.4) .add(0.1 * hill); @@ -263,4 +336,26 @@ impl SimChunk { pub fn get_max_z(&self) -> f32 { (self.alt + Z_TOLERANCE.1).max(CONFIG.sea_level + 1.0) } + + pub fn get_name(&self) -> Option { + self.location + .as_ref() + .map(|l| l.name().to_string()) + } + + pub fn get_biome(&self) -> BiomeKind { + if self.alt < CONFIG.sea_level { + BiomeKind::Ocean + } else if self.chaos > 0.6 { + BiomeKind::Mountain + } else if self.temp > CONFIG.desert_temp { + BiomeKind::Desert + } else if self.temp < CONFIG.snow_temp { + BiomeKind::Snowlands + } else if self.tree_density > 0.65 { + BiomeKind::Forest + } else { + BiomeKind::Grassland + } + } }