Biome blending

Dithering to biome edges
This commit is contained in:
2024-07-07 10:51:02 -04:00
parent 8e92df8008
commit ba2dcf7129
7 changed files with 68 additions and 19 deletions

View File

@@ -64,7 +64,7 @@ pub fn generate_biome_chunk(
let moisture = sample_point( let moisture = sample_point(
x as f64 + chunk_x as f64 * Chunk::SIZE as f64, x as f64 + chunk_x as f64 * Chunk::SIZE as f64,
z as f64 + chunk_y as f64 * Chunk::SIZE as f64, z as f64 + chunk_y as f64 * Chunk::SIZE as f64,
&cfg.moisture_layer, &cfg.moisture_noise,
&noise_m, &noise_m,
cfg.size.as_vec2(), cfg.size.as_vec2(),
cfg.border_size, cfg.border_size,
@@ -72,7 +72,7 @@ pub fn generate_biome_chunk(
let temperature = sample_point( let temperature = sample_point(
x as f64 + chunk_x as f64 * Chunk::SIZE as f64, x as f64 + chunk_x as f64 * Chunk::SIZE as f64,
z as f64 + chunk_y as f64 * Chunk::SIZE as f64, z as f64 + chunk_y as f64 * Chunk::SIZE as f64,
&cfg.temperature_layer, &cfg.temperature_noise,
&noise_t, &noise_t,
cfg.size.as_vec2(), cfg.size.as_vec2(),
cfg.border_size, cfg.border_size,
@@ -80,7 +80,7 @@ pub fn generate_biome_chunk(
let continentality = sample_point( let continentality = sample_point(
x as f64 + chunk_x as f64 * Chunk::SIZE as f64, x as f64 + chunk_x as f64 * Chunk::SIZE as f64,
z as f64 + chunk_y as f64 * Chunk::SIZE as f64, z as f64 + chunk_y as f64 * Chunk::SIZE as f64,
&cfg.continent_layer, &cfg.continent_noise,
&noise_c, &noise_c,
cfg.size.as_vec2(), cfg.size.as_vec2(),
cfg.border_size, cfg.border_size,
@@ -109,8 +109,9 @@ pub fn generate_chunk(
biome_chunk: &BiomeChunk, biome_chunk: &BiomeChunk,
biome_painter: &BiomePainter, biome_painter: &BiomePainter,
) -> Chunk { ) -> Chunk {
let mut result: [f32; Chunk::SIZE * Chunk::SIZE] = [0.; Chunk::SIZE * Chunk::SIZE]; let mut result: [f32; Chunk::SIZE * Chunk::SIZE] = [0.; Chunk::AREA];
let mut data = [BiomeData::default(); Chunk::SIZE * Chunk::SIZE]; let mut data = [BiomeData::default(); Chunk::AREA];
let mut biome_ids = [0; Chunk::AREA];
let noise = SuperSimplex::new(seed); let noise = SuperSimplex::new(seed);
for z in 0..Chunk::SIZE { for z in 0..Chunk::SIZE {
for x in 0..Chunk::SIZE { for x in 0..Chunk::SIZE {
@@ -132,13 +133,16 @@ pub fn generate_chunk(
cfg.border_size, cfg.border_size,
) * blend; ) * blend;
} }
result[x + z * Chunk::SIZE] = sample; let idx = x + z * Chunk::SIZE;
data[x + z * Chunk::SIZE] = biome_data.clone(); biome_ids[idx] = biome_chunk.get_biome_id_dithered(x, z, &noise, cfg.biome_dither);
result[idx] = sample;
data[idx] = biome_data.clone();
} }
} }
return Chunk { return Chunk {
heights: result, heights: result,
biome_data: data, biome_data: data,
biome_id: biome_ids,
chunk_offset: IVec2::new(chunk_x as i32, chunk_z as i32), chunk_offset: IVec2::new(chunk_x as i32, chunk_z as i32),
..default() ..default()
}; };

View File

@@ -1,4 +1,5 @@
use bevy::math::{UVec2, Vec3}; use bevy::math::{UVec2, Vec3};
use noise::NoiseFn;
use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon::iter::{IntoParallelIterator, ParallelIterator};
use super::chunk::Chunk; use super::chunk::Chunk;
@@ -120,6 +121,15 @@ impl BiomeMap {
return Some(chunk.get_biome(x as usize - cx * Chunk::SIZE, y as usize - cy * Chunk::SIZE)); return Some(chunk.get_biome(x as usize - cx * Chunk::SIZE, y as usize - cy * Chunk::SIZE));
} }
pub fn get_biome_id(&self, x: usize, y: usize) -> usize {
let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize;
let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize;
let chunk = &self.chunks[cx + cy * self.size.x as usize];
return chunk.get_biome_id(x - (cx * Chunk::SIZE), y - (cy * Chunk::SIZE));
}
pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData { pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData {
let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize; let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize;
let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize; let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize;
@@ -145,6 +155,37 @@ impl BiomeChunk {
pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData { pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData {
return &self.data[x + y * Chunk::SIZE]; return &self.data[x + y * Chunk::SIZE];
} }
pub fn get_biome_id(&self, x: usize, y: usize) -> usize {
let b = self.get_biome(x, y);
let mut max = 0.;
let mut idx = 0;
for i in 0..b.len() {
let blend = b[i];
if blend > max {
max = blend;
idx = i;
}
}
return idx;
}
pub fn get_biome_id_dithered(&self, x: usize, y: usize, noise: &impl NoiseFn<f64, 2>, scale: f64) -> usize {
let cur_id = self.get_biome_id(x, y);
let b = self.get_biome(x, y);
let n = (noise.get([x as f64 / scale, y as f64 / scale]) as f32) * b[cur_id];
for i in 0..b.len() {
let blend = b[i];
if blend == 0. {
continue;
}
if n < blend {
return i;
}
}
return cur_id;
}
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -8,6 +8,7 @@ pub struct Chunk {
pub heights: [f32; Chunk::AREA], pub heights: [f32; Chunk::AREA],
pub textures: [[u32; 2]; Chunk::AREA], pub textures: [[u32; 2]; Chunk::AREA],
pub biome_data: [BiomeData; Chunk::AREA], pub biome_data: [BiomeData; Chunk::AREA],
pub biome_id: [usize; Chunk::AREA],
pub chunk_offset: IVec2, pub chunk_offset: IVec2,
} }
@@ -17,6 +18,7 @@ impl Default for Chunk {
heights: [0.; Chunk::AREA], heights: [0.; Chunk::AREA],
textures: [[0; 2]; Chunk::AREA], textures: [[0; 2]; Chunk::AREA],
biome_data: [BiomeData::default(); Chunk::AREA], biome_data: [BiomeData::default(); Chunk::AREA],
biome_id: [0; Chunk::AREA],
chunk_offset: Default::default(), chunk_offset: Default::default(),
} }
} }

View File

@@ -10,9 +10,10 @@ pub struct GenerationConfig {
pub sea_level: f64, pub sea_level: f64,
pub border_size: f32, pub border_size: f32,
pub biome_blend: usize, pub biome_blend: usize,
pub moisture_layer: NoiseConfig, pub biome_dither: f64,
pub temperature_layer: NoiseConfig, pub moisture_noise: NoiseConfig,
pub continent_layer: NoiseConfig, pub temperature_noise: NoiseConfig,
pub continent_noise: NoiseConfig,
pub size: UVec2, pub size: UVec2,
} }

View File

@@ -207,8 +207,9 @@ fn create_heightmap(
) { ) {
let config = GenerationConfig { let config = GenerationConfig {
biome_blend: 16, biome_blend: 16,
continent_layer: NoiseConfig { biome_dither: 16.,
scale: 450., continent_noise: NoiseConfig {
scale: 500.,
layers: vec![GeneratorLayer { layers: vec![GeneratorLayer {
base_roughness: 2.14, base_roughness: 2.14,
roughness: 0.87, roughness: 0.87,
@@ -222,8 +223,8 @@ fn create_heightmap(
first_layer_mask: false, first_layer_mask: false,
}], }],
}, },
moisture_layer: NoiseConfig { moisture_noise: NoiseConfig {
scale: 450., scale: 500.,
layers: vec![GeneratorLayer { layers: vec![GeneratorLayer {
base_roughness: 2.14, base_roughness: 2.14,
roughness: 0.87, roughness: 0.87,
@@ -237,8 +238,8 @@ fn create_heightmap(
first_layer_mask: false, first_layer_mask: false,
}], }],
}, },
temperature_layer: NoiseConfig { temperature_noise: NoiseConfig {
scale: 450., scale: 500.,
layers: vec![GeneratorLayer { layers: vec![GeneratorLayer {
base_roughness: 2.14, base_roughness: 2.14,
roughness: 0.87, roughness: 0.87,

View File

@@ -38,8 +38,8 @@ pub fn paint_chunk(
for x in 0..Chunk::SIZE { for x in 0..Chunk::SIZE {
let idx = x + z * Chunk::SIZE; let idx = x + z * Chunk::SIZE;
let height = chunk.heights[idx]; let height = chunk.heights[idx];
let biome_data = &chunk.biome_data[idx]; let biome_id = chunk.biome_id[idx];
let biome = painter.sample_biome(biome_data); let biome = &painter.biomes[biome_id];
let mapper = mappers.get(biome.tile_mapper.clone()); let mapper = mappers.get(biome.tile_mapper.clone());
let tile_handle = mapper.unwrap().sample_tile(height); let tile_handle = mapper.unwrap().sample_tile(height);
let tile = tiles.get(tile_handle).unwrap(); let tile = tiles.get(tile_handle).unwrap();