Added basic mountain/valley worldgen

Former-commit-id: 5a45f18fcdf523841ee8433ba2ff0b8765eadf3a
This commit is contained in:
Joshua Barretto 2019-05-20 03:53:04 +01:00
parent 2996e3c8aa
commit a0dfa3fddc
3 changed files with 127 additions and 56 deletions

View File

@ -12,6 +12,8 @@ pub enum ChonkError {
OutOfBounds,
}
const SUB_CHUNK_HEIGHT: u32 = 16;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Chonk {
z_offset: i32,
@ -37,11 +39,11 @@ impl Chonk {
}
pub fn get_z_max(&self) -> i32 {
self.z_offset + (self.sub_chunks.len() as u32 * TerrainChunkSize::SIZE.z) as i32
self.z_offset + (self.sub_chunks.len() as u32 * SUB_CHUNK_HEIGHT) as i32
}
fn sub_chunk_idx(&self, z: i32) -> usize {
((z - self.z_offset) as u32 / TerrainChunkSize::SIZE.z as u32) as usize
((z - self.z_offset) as u32 / SUB_CHUNK_HEIGHT as u32) as usize
}
}
@ -57,7 +59,7 @@ impl ReadVol for Chonk {
// Below the terrain
Ok(&self.below)
} else if pos.z
>= self.z_offset + TerrainChunkSize::SIZE.z as i32 * self.sub_chunks.len() as i32
>= self.z_offset + SUB_CHUNK_HEIGHT as i32 * self.sub_chunks.len() as i32
{
// Above the terrain
Ok(&self.above)
@ -73,7 +75,7 @@ impl ReadVol for Chonk {
let rpos = pos
- Vec3::unit_z()
* (self.z_offset
+ sub_chunk_idx as i32 * TerrainChunkSize::SIZE.z as i32);
+ sub_chunk_idx as i32 * SUB_CHUNK_HEIGHT as i32);
chunk.get(rpos).map_err(|err| ChonkError::ChunkError(err))
}
}
@ -95,7 +97,7 @@ impl WriteVol for Chonk {
let rpos = pos
- Vec3::unit_z()
* (self.z_offset + sub_chunk_idx as i32 * TerrainChunkSize::SIZE.z as i32);
* (self.z_offset + sub_chunk_idx as i32 * SUB_CHUNK_HEIGHT as i32);
match &mut self.sub_chunks[sub_chunk_idx] {
// Can't fail

View File

@ -1,6 +1,9 @@
mod sim;
use std::time::Duration;
use std::{
ops::{Add, Neg},
time::Duration,
};
use noise::{NoiseFn, Perlin, Seedable};
use vek::*;
use common::{
@ -13,8 +16,6 @@ pub enum Error {
Other(String),
}
pub const WORLD_SIZE: Vec2<usize> = Vec2 { x: 1024, y: 1024 };
pub struct World {
sim: sim::WorldSim,
}
@ -37,66 +38,61 @@ impl World {
let dirt = Block::new(3, Rgb::new(128, 90, 0));
let sand = Block::new(4, Rgb::new(180, 150, 50));
let offset_nz = Perlin::new().set_seed(3);
let perlin_nz = Perlin::new().set_seed(1);
let temp_nz = Perlin::new().set_seed(2);
let chaos_nz = Perlin::new().set_seed(3);
let warp_nz = Perlin::new().set_seed(self.sim.seed + 0);
let temp_nz = Perlin::new().set_seed(self.sim.seed + 1);
let ridge_nz = Perlin::new().set_seed(self.sim.seed + 2);
let get_offset = |pos: Vec2<i32>| {
((offset_nz.get(pos.map(|e| e as f64 / 4000.0).into_array()) + 1.0)
* 1000.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()),
};
let offset_z = if let Some(offset_z) = self.sim
.get(chunk_pos.map(|e| e as u32))
.map(|chunk| chunk.alt as i32)
{
offset_z as i32
} else {
return TerrainChunk::new(0, air, air, TerrainChunkMeta::void());
};
let mut chunk = TerrainChunk::new(offset_z, stone, air, TerrainChunkMeta::void());
let mut chunk = TerrainChunk::new(base_z, stone, air, TerrainChunkMeta::void());
for x in 0..TerrainChunkSize::SIZE.x as i32 {
for y in 0..TerrainChunkSize::SIZE.y as i32 {
for z in offset_z as i32..offset_z as i32 + 256 {
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 chaos = self.sim
.get_interpolated(wpos2d, |chunk| chunk.chaos)
.unwrap_or(0.0);
let ridge_freq = 1.0 / 128.0;
let ridge_ampl = 96.0;
let ridge = ridge_nz
.get((wposf2d * ridge_freq).into_array()).abs().neg().add(1.0) as f32 * ridge_ampl * chaos.powf(8.0);
let height_z = self.sim
.get_interpolated(wpos2d, |chunk| chunk.alt)
.unwrap_or(0.0)
+ ridge;
for z in base_z..base_z + 256 {
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 chaos_freq = 1.0 / 100.0;
let freq = 1.0 / 128.0;
let ampl = 75.0;
let small_freq = 1.0 / 32.0;
let small_ampl = 6.0;
let offs = 128.0;
let warp_freq = 1.0 / 48.0;
let warp_ampl = 24.0;
let chaos = chaos_nz
.get(Vec2::from(wposf * chaos_freq).into_array())
.max(0.0)
+ 0.5;
let height = height_z
+ warp_nz.get((wposf * warp_freq).into_array()) as f32 * warp_ampl * (chaos + 0.05);
let height =
perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl * chaos
+ perlin_nz.get((wposf * small_freq).into_array())
* small_ampl
* 3.0
* chaos.powf(2.0)
+ offs
+ get_offset(Vec2::from(wpos)) as f64;
let temp =
(temp_nz.get(Vec2::from(wposf * (1.0 / 64.0)).into_array()) + 1.0) * 0.5;
(temp_nz.get(Vec2::from(wposf * (1.0 / 64.0)).into_array()) as f32 + 1.0) * 0.5;
let z = wposf.z as f32;
let _ = chunk.set(
lpos,
if wposf.z < height - 4.0 {
if z < height - 4.0 {
stone
} else if wposf.z < height - 2.0 {
} else if z < height - 2.0 {
dirt
} else if wposf.z < height {
Block::new(2, Rgb::new(10 + (150.0 * temp) as u8, 150, 0))
} else if z < height {
Block::new(2, Rgb::new(10 + (75.0 * temp) as u8, 180, 50 - (50.0 * temp) as u8))
} else {
air
},

View File

@ -1,14 +1,18 @@
use std::ops::{Mul, Div};
use std::{
ops::{Add, Mul, Div},
f32,
};
use noise::{NoiseFn, OpenSimplex, Seedable};
use vek::*;
use common::{
terrain::TerrainChunkSize,
vol::VolSize,
};
use crate::WORLD_SIZE;
pub const WORLD_SIZE: Vec2<usize> = Vec2 { x: 1024, y: 1024 };
pub struct WorldSim {
seed: u32,
pub seed: u32,
chunks: Vec<SimChunk>,
}
@ -16,7 +20,9 @@ impl WorldSim {
pub fn generate(seed: u32) -> Self {
let mut gen_ctx = GenCtx {
alt_nz: OpenSimplex::new()
.set_seed(seed),
.set_seed(seed + 0),
chaos_nz: OpenSimplex::new()
.set_seed(seed + 1),
};
let mut chunks = Vec::new();
@ -39,13 +45,65 @@ impl WorldSim {
None
}
}
pub fn get_base_z(&self, chunk_pos: Vec2<u32>) -> Option<f32> {
self
.get(chunk_pos)
.and_then(|_| (0..2)
.map(|i| (0..2)
.map(move |j| (i, j)))
.flatten()
.map(|(i, j)| self
.get(chunk_pos + Vec2::new(i, j))
.map(|c| c.get_base_z()))
.flatten()
.fold(None, |a: Option<f32>, x| a.map(|a| a.min(x)).or(Some(x))))
}
pub fn get_interpolated<F: FnMut(&SimChunk) -> f32>(&self, pos: Vec2<i32>, mut f: F) -> Option<f32> {
let pos = pos.map2(TerrainChunkSize::SIZE.into(), |e, sz: u32| e as f64 / sz as f64);
fn cubic(a: f32, b: f32, c: f32, d: f32, x: f32) -> f32 {
let x2 = x * x;
// Catmull-Rom splines
let co0 = -0.5 * a + 1.5 * b - 1.5 * c + 0.5 * d;
let co1 = a - 2.5 * b + 2.0 * c - 0.5 * d;
let co2 = -0.5 * a + 0.5 * c;
let co3 = b;
co0 * x2 * x + co1 * x2 + co2 * x + co3
}
let mut y = [0.0; 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))?);
y[y_idx] = cubic(x0, x1, x2, x3, pos.x.fract() as f32);
}
/*
fn cosine_interp (a: f32, b: f32, x: f32) -> f32 {
let x2 = x;//(1.0 - (x * f32::consts::PI).cos()) / 2.0;
a * (1.0 - x2) + b * x2
}
*/
Some(cubic(y[0], y[1], y[2], y[3], pos.y.fract() as f32))
}
}
struct GenCtx {
chaos_nz: OpenSimplex,
alt_nz: OpenSimplex,
}
pub struct SimChunk {
pub chaos: f32,
pub alt: f32,
}
@ -53,10 +111,25 @@ impl SimChunk {
fn generate(pos: Vec2<u32>, gen_ctx: &mut GenCtx) -> Self {
let wposf = (pos * Vec2::from(TerrainChunkSize::SIZE)).map(|e| e as f64);
let chaos = (gen_ctx.chaos_nz
.get((wposf.div(400.0)).into_array()) as f32)
.max(0.0)
.add(0.15)
.powf(2.0);
Self {
alt: gen_ctx.alt_nz
.get((wposf.div(2048.0)).into_array())
.mul(512.0) as f32,
chaos,
alt: (gen_ctx.alt_nz
.get((wposf.div(125.0)).into_array()) as f32)
.add(1.0).mul(0.5)
.mul(750.0)
.mul(chaos.max(0.05)),
}
}
pub fn get_base_z(&self) -> f32 {
const BASE_Z_TOLERANCE: f32 = 32.0;
self.alt - BASE_Z_TOLERANCE
}
}