mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Several tweaks to biome distribution
This commit is contained in:
parent
87c847b108
commit
81c2b7e969
@ -1,9 +1,11 @@
|
|||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
use common::{spiral::Spiral2d, terrain::CoordinateConversions};
|
||||||
|
use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
|
||||||
|
use rand::{seq::IteratorRandom, thread_rng};
|
||||||
use rayon::ThreadPoolBuilder;
|
use rayon::ThreadPoolBuilder;
|
||||||
use veloren_world::{
|
use veloren_world::{
|
||||||
layer,
|
layer,
|
||||||
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP},
|
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP},
|
||||||
Land, World,
|
CanvasInfo, Land, World,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn cave(c: &mut Criterion) {
|
fn cave(c: &mut Criterion) {
|
||||||
@ -19,12 +21,13 @@ fn cave(c: &mut Criterion) {
|
|||||||
&|_| {},
|
&|_| {},
|
||||||
);
|
);
|
||||||
let land = Land::from_sim(world.sim());
|
let land = Land::from_sim(world.sim());
|
||||||
|
let mut group = c.benchmark_group("cave");
|
||||||
c.bench_function("generate", |b| {
|
group.sample_size(25);
|
||||||
|
group.bench_function("generate_entrances", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let entrances = black_box(layer::cave::surface_entrances(&land))
|
let entrances = black_box(layer::cave::surface_entrances(&land))
|
||||||
.step_by(5)
|
.step_by(10)
|
||||||
.map(|e| e / 32);
|
.map(|e| e.wpos_to_cpos());
|
||||||
for entrance in entrances {
|
for entrance in entrances {
|
||||||
_ = black_box(world.generate_chunk(
|
_ = black_box(world.generate_chunk(
|
||||||
index.as_index_ref(),
|
index.as_index_ref(),
|
||||||
@ -36,6 +39,42 @@ fn cave(c: &mut Criterion) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group.bench_function("generate_hard", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| {
|
||||||
|
let entrance = layer::cave::surface_entrances(&land)
|
||||||
|
.choose(&mut thread_rng())
|
||||||
|
.unwrap()
|
||||||
|
.wpos_to_cpos();
|
||||||
|
Spiral2d::new()
|
||||||
|
.step_by(8)
|
||||||
|
.find(|p| {
|
||||||
|
CanvasInfo::with_mock_canvas_info(
|
||||||
|
index.as_index_ref(),
|
||||||
|
world.sim(),
|
||||||
|
|&info| {
|
||||||
|
let land = &info.land();
|
||||||
|
let tunnels =
|
||||||
|
layer::cave::tunnel_bounds_at(entrance + p, &info, land);
|
||||||
|
tunnels.count() > 2
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map_or(entrance, |p| entrance + p)
|
||||||
|
},
|
||||||
|
|chunk| {
|
||||||
|
_ = black_box(world.generate_chunk(
|
||||||
|
index.as_index_ref(),
|
||||||
|
chunk,
|
||||||
|
None,
|
||||||
|
|| false,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!(benches, cave);
|
criterion_group!(benches, cave);
|
||||||
|
67
world/examples/cave_biomes.rs
Normal file
67
world/examples/cave_biomes.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use common::terrain::CoordinateConversions;
|
||||||
|
use rayon::ThreadPoolBuilder;
|
||||||
|
use vek::Vec2;
|
||||||
|
use veloren_world::{
|
||||||
|
layer::{
|
||||||
|
self,
|
||||||
|
cave::{Biome, LAYERS},
|
||||||
|
},
|
||||||
|
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP},
|
||||||
|
CanvasInfo, Land, World,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let pool = ThreadPoolBuilder::new().build().unwrap();
|
||||||
|
let (world, index) = World::generate(
|
||||||
|
123,
|
||||||
|
WorldOpts {
|
||||||
|
seed_elements: true,
|
||||||
|
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
||||||
|
..WorldOpts::default()
|
||||||
|
},
|
||||||
|
&pool,
|
||||||
|
&|_| {},
|
||||||
|
);
|
||||||
|
let land = Land::from_sim(world.sim());
|
||||||
|
|
||||||
|
let mut biomes: Vec<(Biome, u32)> = vec![(Biome::default(), 0); LAYERS as usize];
|
||||||
|
for x in 0..land.size().x {
|
||||||
|
for y in 0..land.size().y {
|
||||||
|
let wpos = Vec2::new(x as i32, y as i32).cpos_to_wpos();
|
||||||
|
CanvasInfo::with_mock_canvas_info(index.as_index_ref(), world.sim(), |&info| {
|
||||||
|
let land = &info.land();
|
||||||
|
let tunnels = layer::cave::tunnel_bounds_at(wpos, &info, land);
|
||||||
|
for (level, z_range, _, _, _, tunnel) in tunnels {
|
||||||
|
let biome = tunnel.biome_at(wpos.with_z(z_range.start), &info);
|
||||||
|
let (ref mut current, ref mut total) = &mut biomes[level as usize - 1];
|
||||||
|
current.barren += biome.barren;
|
||||||
|
current.mushroom += biome.mushroom;
|
||||||
|
current.fire += biome.fire;
|
||||||
|
current.leafy += biome.leafy;
|
||||||
|
current.dusty += biome.dusty;
|
||||||
|
current.icy += biome.icy;
|
||||||
|
current.snowy += biome.snowy;
|
||||||
|
current.crystal += biome.crystal;
|
||||||
|
current.sandy += biome.sandy;
|
||||||
|
*total += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (level, (biome, total)) in biomes.iter().enumerate() {
|
||||||
|
let total = *total as f32;
|
||||||
|
println!("--- LEVEL {} ---", level);
|
||||||
|
println!("TOTAL {}", total);
|
||||||
|
println!("BARREN {:.3}", biome.barren / total);
|
||||||
|
println!("MUSHROOM {:.3}", biome.mushroom / total);
|
||||||
|
println!("FIRE {:.3}", biome.fire / total);
|
||||||
|
println!("LEAFY {:.3}", biome.leafy / total);
|
||||||
|
println!("DUSTY {:.3}", biome.dusty / total);
|
||||||
|
println!("ICY {:.3}", biome.icy / total);
|
||||||
|
println!("SNOWY {:.3}", biome.snowy / total);
|
||||||
|
println!("CRYSTAL {:.3}", biome.crystal / total);
|
||||||
|
println!("SANDY {:.3}", biome.sandy / total);
|
||||||
|
println!("\n");
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ fn to_wpos(cell: Vec2<i32>, level: u32) -> Vec2<i32> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AVG_LEVEL_DEPTH: i32 = 120;
|
const AVG_LEVEL_DEPTH: i32 = 120;
|
||||||
const LAYERS: u32 = 4;
|
pub const LAYERS: u32 = 5;
|
||||||
const MIN_RADIUS: f32 = 8.0;
|
const MIN_RADIUS: f32 = 8.0;
|
||||||
const MAX_RADIUS: f32 = 64.0;
|
const MAX_RADIUS: f32 = 64.0;
|
||||||
|
|
||||||
@ -174,13 +174,14 @@ impl Tunnel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn biome_at(&self, wpos: Vec3<i32>, info: &CanvasInfo) -> Biome {
|
pub fn biome_at(&self, wpos: Vec3<i32>, info: &CanvasInfo) -> Biome {
|
||||||
let Some(col) = info.col_or_gen(wpos.xy()) else {
|
let Some(col) = info.col_or_gen(wpos.xy()) else {
|
||||||
return Biome::default();
|
return Biome::default();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Below the ground
|
// Below the ground
|
||||||
let below = ((col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * 1.5)).clamped(0.0, 1.0);
|
let below = ((col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * LAYERS as f32 * 0.5))
|
||||||
|
.clamped(0.0, 1.0);
|
||||||
let depth = (col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * LAYERS as f32);
|
let depth = (col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * LAYERS as f32);
|
||||||
let underground = ((col.alt - wpos.z as f32) / 80.0 - 1.0).clamped(0.0, 1.0);
|
let underground = ((col.alt - wpos.z as f32) / 80.0 - 1.0).clamped(0.0, 1.0);
|
||||||
|
|
||||||
@ -195,7 +196,7 @@ impl Tunnel {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let temp = Lerp::lerp_unclamped(
|
let temp = Lerp::lerp_unclamped(
|
||||||
col.temp,
|
col.temp * 2.0,
|
||||||
FastNoise2d::new(42)
|
FastNoise2d::new(42)
|
||||||
.get(wpos.xy().map(|e| e as f64 / 1536.0))
|
.get(wpos.xy().map(|e| e as f64 / 1536.0))
|
||||||
.mul(1.15)
|
.mul(1.15)
|
||||||
@ -229,42 +230,44 @@ impl Tunnel {
|
|||||||
sandy,
|
sandy,
|
||||||
] = {
|
] = {
|
||||||
// Default biome, no other conditions apply
|
// Default biome, no other conditions apply
|
||||||
let barren = 0.01;
|
let barren = 0.005;
|
||||||
// Mushrooms grow underground and thrive in a humid environment with moderate
|
// Mushrooms grow underground and thrive in a humid environment with moderate
|
||||||
// temperatures
|
// temperatures
|
||||||
let mushroom = underground
|
let mushroom = underground
|
||||||
* close(humidity, 1.0, 0.6, 4)
|
* close(humidity, 1.0, 0.7, 4)
|
||||||
* close(temp, 1.5, 0.9, 4)
|
* close(temp, 1.5, 1.2, 4)
|
||||||
* close(depth, 1.0, 0.55, 4);
|
* close(depth, 0.9, 0.65, 4);
|
||||||
// Extremely hot and dry areas deep underground
|
// Extremely hot and dry areas deep underground
|
||||||
let fire = underground
|
let fire = underground
|
||||||
* close(humidity, 0.0, 0.6, 4)
|
* close(humidity, 0.0, 0.6, 4)
|
||||||
* close(temp, 2.0, 1.3, 4)
|
* close(temp, 2.0, 1.4, 4)
|
||||||
* close(depth, 1.0, 0.5, 4);
|
* close(depth, 1.0, 0.45, 4);
|
||||||
// Overgrown with plants that need a moderate climate to survive
|
// Overgrown with plants that need a moderate climate to survive
|
||||||
let leafy = underground
|
let leafy = underground
|
||||||
* close(humidity, 0.9, 0.65, 4)
|
* close(humidity, 0.8, 0.9, 4)
|
||||||
* close(temp, 1.15, 0.85, 4)
|
* close(temp, 0.8, 1.0, 4)
|
||||||
* close(depth, 0.0, 0.65, 4);
|
* close(depth, 0.1, 0.7, 4);
|
||||||
// Cool temperature, dry and devoid of value
|
// Cool temperature, dry and devoid of value
|
||||||
let dusty = close(humidity, 0.0, 0.5, 4) * close(temp, -0.3, 0.7, 4);
|
let dusty = close(humidity, 0.0, 0.5, 4)
|
||||||
|
* close(temp, -0.3, 0.7, 4)
|
||||||
|
* close(depth, 0.5, 0.5, 4);
|
||||||
// Deep underground and freezing cold
|
// Deep underground and freezing cold
|
||||||
let icy = underground
|
let icy = underground
|
||||||
* close(temp, -1.5, 1.0, 4)
|
* close(temp, -1.5, 2.0, 4)
|
||||||
* close(depth, 1.0, 0.6, 4)
|
* close(depth, 0.9, 0.55, 4)
|
||||||
* close(humidity, 1.0, 0.7, 4);
|
* close(humidity, 1.0, 0.75, 4);
|
||||||
// Rocky cold cave that appear near the surface
|
// Rocky cold cave that appear near the surface
|
||||||
let snowy = close(temp, -0.8, 0.5, 4) * close(depth, 0.0, 0.45, 4);
|
let snowy = close(temp, -0.7, 0.4, 4) * close(depth, 0.0, 0.4, 4);
|
||||||
// Crystals grow deep underground in areas rich with minerals. They are present
|
// Crystals grow deep underground in areas rich with minerals. They are present
|
||||||
// in areas with colder temperatures and low humidity
|
// in areas with colder temperatures and low humidity
|
||||||
let crystal = underground
|
let crystal = underground
|
||||||
* close(humidity, 0.0, 0.5, 4)
|
* close(humidity, 0.0, 0.5, 4)
|
||||||
* close(temp, -0.9, 0.7, 4)
|
* close(temp, -0.9, 0.9, 4)
|
||||||
* close(depth, 1.0, 0.55, 4)
|
* close(depth, 1.0, 0.55, 4)
|
||||||
* close(mineral, 2.0, 1.25, 4);
|
* close(mineral, 2.0, 1.25, 4);
|
||||||
// Hot, dry and shallow
|
// Hot, dry and shallow
|
||||||
let sandy =
|
let sandy =
|
||||||
close(humidity, 0.0, 0.4, 4) * close(temp, 1.1, 0.6, 4) * close(depth, 0.0, 0.6, 4);
|
close(humidity, 0.0, 0.5, 4) * close(temp, 1.0, 0.9, 4) * close(depth, 0.0, 0.6, 4);
|
||||||
|
|
||||||
let biomes = [
|
let biomes = [
|
||||||
barren, mushroom, fire, leafy, dusty, icy, snowy, crystal, sandy,
|
barren, mushroom, fire, leafy, dusty, icy, snowy, crystal, sandy,
|
||||||
@ -463,6 +466,12 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let z_ranges = &tunnel_bounds
|
||||||
|
.iter()
|
||||||
|
.map(|(_, z_range, _, _, _, _)| z_range.clone())
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
let structure_seeds = StructureGen2d::new(34537, 24, 8).get(wpos2d);
|
||||||
for (level, z_range, horizontal, vertical, dist, tunnel) in tunnel_bounds {
|
for (level, z_range, horizontal, vertical, dist, tunnel) in tunnel_bounds {
|
||||||
write_column(
|
write_column(
|
||||||
canvas,
|
canvas,
|
||||||
@ -470,10 +479,12 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
|||||||
level,
|
level,
|
||||||
wpos2d,
|
wpos2d,
|
||||||
z_range.clone(),
|
z_range.clone(),
|
||||||
|
z_ranges,
|
||||||
tunnel,
|
tunnel,
|
||||||
(horizontal, vertical, dist),
|
(horizontal, vertical, dist),
|
||||||
giant_tree_dist,
|
giant_tree_dist,
|
||||||
&mut structure_cache,
|
&mut structure_cache,
|
||||||
|
&structure_seeds,
|
||||||
rng,
|
rng,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -482,19 +493,19 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone)]
|
||||||
struct Biome {
|
pub struct Biome {
|
||||||
humidity: f32,
|
humidity: f32,
|
||||||
barren: f32,
|
pub barren: f32,
|
||||||
mineral: f32,
|
mineral: f32,
|
||||||
mushroom: f32,
|
pub mushroom: f32,
|
||||||
fire: f32,
|
pub fire: f32,
|
||||||
leafy: f32,
|
pub leafy: f32,
|
||||||
dusty: f32,
|
pub dusty: f32,
|
||||||
icy: f32,
|
pub icy: f32,
|
||||||
snowy: f32,
|
pub snowy: f32,
|
||||||
crystal: f32,
|
pub crystal: f32,
|
||||||
sandy: f32,
|
pub sandy: f32,
|
||||||
depth: f32,
|
depth: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,10 +558,12 @@ fn write_column<R: Rng>(
|
|||||||
level: u32,
|
level: u32,
|
||||||
wpos2d: Vec2<i32>,
|
wpos2d: Vec2<i32>,
|
||||||
z_range: Range<i32>,
|
z_range: Range<i32>,
|
||||||
|
z_ranges: &[Range<i32>],
|
||||||
tunnel: Tunnel,
|
tunnel: Tunnel,
|
||||||
dimensions: (f32, f32, f32),
|
dimensions: (f32, f32, f32),
|
||||||
giant_tree_dist: f32,
|
giant_tree_dist: f32,
|
||||||
structure_cache: &mut SmallCache<Vec3<i32>, Option<CaveStructure>>,
|
structure_cache: &mut SmallCache<Vec3<i32>, Option<CaveStructure>>,
|
||||||
|
structure_seeds: &[(Vec2<i32>, u32); 9],
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) {
|
) {
|
||||||
let info = canvas.info();
|
let info = canvas.info();
|
||||||
@ -564,6 +577,19 @@ fn write_column<R: Rng>(
|
|||||||
let (cave_width, max_height, dist_cave_center) = dimensions;
|
let (cave_width, max_height, dist_cave_center) = dimensions;
|
||||||
let biome = tunnel.biome_at(wpos2d.with_z(z_range.start), &info);
|
let biome = tunnel.biome_at(wpos2d.with_z(z_range.start), &info);
|
||||||
|
|
||||||
|
// Get the range, if there is any, where the current cave overlaps with other
|
||||||
|
// caves. Right now this is only used to prevent ceiling cover from being
|
||||||
|
// place
|
||||||
|
let overlap = z_ranges.iter().find_map(|other_z_range| {
|
||||||
|
if *other_z_range == z_range {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let start = z_range.start.max(other_z_range.start);
|
||||||
|
let end = z_range.end.min(other_z_range.end);
|
||||||
|
let min = z_range.start.min(other_z_range.start);
|
||||||
|
if start < end { Some(min..end) } else { None }
|
||||||
|
});
|
||||||
|
|
||||||
let stalactite = {
|
let stalactite = {
|
||||||
FastNoise2d::new(35)
|
FastNoise2d::new(35)
|
||||||
.get(wpos2d.map(|e| e as f64 / 8.0))
|
.get(wpos2d.map(|e| e as f64 / 8.0))
|
||||||
@ -631,7 +657,7 @@ fn write_column<R: Rng>(
|
|||||||
.sub(0.2)
|
.sub(0.2)
|
||||||
.min(0.0)
|
.min(0.0)
|
||||||
// .mul((biome.temp as f64 - 1.5).mul(30.0).clamped(0.0, 1.0))
|
// .mul((biome.temp as f64 - 1.5).mul(30.0).clamped(0.0, 1.0))
|
||||||
.mul((cave_width / 16.0).clamped(0.0, 1.0))
|
.mul((cave_width / 16.0).clamped(0.5, 1.0))
|
||||||
.mul((cave_width / (MAX_RADIUS - 16.0)).clamped(1.0, 1.25))
|
.mul((cave_width / (MAX_RADIUS - 16.0)).clamped(1.0, 1.25))
|
||||||
.mul((biome.fire - 0.5).mul(30.0).clamped(0.0, 1.0))
|
.mul((biome.fire - 0.5).mul(30.0).clamped(0.0, 1.0))
|
||||||
.mul(64.0)
|
.mul(64.0)
|
||||||
@ -743,9 +769,7 @@ fn write_column<R: Rng>(
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
let structures = StructureGen2d::new(34537, 24, 8)
|
let structures = structure_seeds
|
||||||
.get(wpos2d)
|
|
||||||
.as_slice()
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(wpos2d, seed)| {
|
.filter_map(|(wpos2d, seed)| {
|
||||||
let structure = structure_cache.get(wpos2d.with_z(tunnel.a.depth), |_| {
|
let structure = structure_cache.get(wpos2d.with_z(tunnel.a.depth), |_| {
|
||||||
@ -758,6 +782,7 @@ fn write_column<R: Rng>(
|
|||||||
tunnel_bounds_at(pos.xy(), &info, &info.land())
|
tunnel_bounds_at(pos.xy(), &info, &info.land())
|
||||||
.any(|(_, z_range, _, _, _, _)| z_range.contains(&(z_range.start - 1)))
|
.any(|(_, z_range, _, _, _, _)| z_range.contains(&(z_range.start - 1)))
|
||||||
};
|
};
|
||||||
|
|
||||||
if biome.mushroom > 0.7
|
if biome.mushroom > 0.7
|
||||||
&& vertical > 16.0
|
&& vertical > 16.0
|
||||||
&& rng.gen_bool(
|
&& rng.gen_bool(
|
||||||
@ -1164,7 +1189,10 @@ fn write_column<R: Rng>(
|
|||||||
&& !void_below
|
&& !void_below
|
||||||
{
|
{
|
||||||
Block::new(BlockKind::Rock, Rgb::new(50, 35, 75))
|
Block::new(BlockKind::Rock, Rgb::new(50, 35, 75))
|
||||||
} else if (z < base && !void_below) || (z >= ceiling && !void_above) {
|
} else if (z < base && !void_below)
|
||||||
|
|| ((z >= ceiling && !void_above)
|
||||||
|
&& !(ceiling_cover > 0.0 && overlap.as_ref().map_or(false, |o| o.contains(&z))))
|
||||||
|
{
|
||||||
let stalactite: Rgb<i16> = Lerp::lerp_unclamped(
|
let stalactite: Rgb<i16> = Lerp::lerp_unclamped(
|
||||||
Lerp::lerp_unclamped(
|
Lerp::lerp_unclamped(
|
||||||
Lerp::lerp_unclamped(
|
Lerp::lerp_unclamped(
|
||||||
|
Loading…
Reference in New Issue
Block a user