mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added basic mountain/valley worldgen
Former-commit-id: 5a45f18fcdf523841ee8433ba2ff0b8765eadf3a
This commit is contained in:
parent
2996e3c8aa
commit
a0dfa3fddc
@ -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
|
||||
|
@ -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
|
||||
},
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user