Basic region generation

This commit is contained in:
Joshua Barretto 2019-06-18 22:22:31 +01:00
parent aa28f0b427
commit 451bbe9921
8 changed files with 191 additions and 46 deletions

View File

@ -41,7 +41,7 @@ fn get_ao_quad<V: ReadVol>(vol: &V, pos: Vec3<i32>, shift: Vec3<i32>, 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
}

View File

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

View File

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

View File

@ -15,7 +15,7 @@ use vek::*;
pub struct BlockGen<'a> {
world: &'a World,
column_cache: HashCache<Vec2<i32>, Option<ColumnSample>>,
column_cache: HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
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);

View File

@ -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<i32>;
type Sample = Option<ColumnSample>;
type Sample = Option<ColumnSample<'a>>;
fn get(&self, wpos: Vec2<i32>) -> Option<ColumnSample> {
fn get(&self, wpos: Vec2<i32>) -> Option<ColumnSample<'a>> {
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<f32>,
@ -164,4 +174,5 @@ pub struct ColumnSample {
pub rock: f32,
pub cliff: f32,
pub temp: f32,
pub location: Option<&'a Arc<Location>>
}

View File

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

View File

@ -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<i32>,
kind: LocationKind,
kingdom: Kingdom,
kingdom: Option<Kingdom>,
}
impl Location {
pub fn generate<R: Rng>(center: Vec2<i32>, 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<R: Rng>(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",

View File

@ -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<usize> = Vec2 { x: 1024, y: 1024 };
@ -35,6 +45,7 @@ pub struct WorldSim {
pub seed: u32,
pub(crate) chunks: Vec<SimChunk>,
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::<i32>() % WORLD_SIZE.x as i32,
self.rng.gen::<i32>() % 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<u32>) -> 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::<i32>(),
rng.gen::<i32>(),
).map(|e| e.abs() % 3 - 1);
if let Some(other) = &mut self.get_mut(pos + rpos) {
if other.location.is_none()
&& rng.gen::<f32>() > other.chaos * 1.5
&& other.alt > CONFIG.sea_level {
other.location = location;
}
}
}
}
}
self.rng = rng;
}
pub fn get(&self, chunk_pos: Vec2<i32>) -> 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<u32>) -> Option<f32> {
pub fn get_mut(&mut self, chunk_pos: Vec2<i32>) -> 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<i32>) -> Option<f32> {
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<u32>, gen_ctx: &mut GenCtx) -> Self {
let wposf = (pos * Vec2::from(TerrainChunkSize::SIZE)).map(|e| e as f64);
fn generate(pos: Vec2<i32>, 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<String> {
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
}
}
}