mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Biome distribution tweaks
This commit is contained in:
parent
81c2b7e969
commit
46af9ee489
@ -1,6 +1,5 @@
|
||||
use common::{spiral::Spiral2d, terrain::CoordinateConversions};
|
||||
use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use rand::{seq::IteratorRandom, thread_rng};
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use veloren_world::{
|
||||
layer,
|
||||
@ -11,7 +10,7 @@ use veloren_world::{
|
||||
fn cave(c: &mut Criterion) {
|
||||
let pool = ThreadPoolBuilder::new().build().unwrap();
|
||||
let (world, index) = World::generate(
|
||||
123,
|
||||
230,
|
||||
WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
||||
@ -22,12 +21,11 @@ fn cave(c: &mut Criterion) {
|
||||
);
|
||||
let land = Land::from_sim(world.sim());
|
||||
let mut group = c.benchmark_group("cave");
|
||||
group.sample_size(25);
|
||||
group.sample_size(10);
|
||||
group.bench_function("generate_entrances", |b| {
|
||||
b.iter(|| {
|
||||
let entrances = black_box(layer::cave::surface_entrances(&land))
|
||||
.step_by(10)
|
||||
.map(|e| e.wpos_to_cpos());
|
||||
let entrances =
|
||||
black_box(layer::cave::surface_entrances(&land)).map(|e| e.wpos_to_cpos());
|
||||
for entrance in entrances {
|
||||
_ = black_box(world.generate_chunk(
|
||||
index.as_index_ref(),
|
||||
@ -40,15 +38,14 @@ 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)
|
||||
group.bench_function("generate_multiple_tunnels", |b| {
|
||||
b.iter(|| {
|
||||
let entrances = layer::cave::surface_entrances(&land)
|
||||
.map(|e| e.wpos_to_cpos())
|
||||
.step_by(6);
|
||||
for entrance in entrances {
|
||||
let chunk = Spiral2d::new()
|
||||
.step_by(16)
|
||||
.find(|p| {
|
||||
CanvasInfo::with_mock_canvas_info(
|
||||
index.as_index_ref(),
|
||||
@ -57,13 +54,12 @@ fn cave(c: &mut Criterion) {
|
||||
let land = &info.land();
|
||||
let tunnels =
|
||||
layer::cave::tunnel_bounds_at(entrance + p, &info, land);
|
||||
tunnels.count() > 2
|
||||
tunnels.count() > 1
|
||||
},
|
||||
)
|
||||
})
|
||||
.map_or(entrance, |p| entrance + p)
|
||||
},
|
||||
|chunk| {
|
||||
.map_or(entrance, |p| entrance + p);
|
||||
|
||||
_ = black_box(world.generate_chunk(
|
||||
index.as_index_ref(),
|
||||
chunk,
|
||||
@ -71,9 +67,8 @@ fn cave(c: &mut Criterion) {
|
||||
|| false,
|
||||
None,
|
||||
));
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ use veloren_world::{
|
||||
fn main() {
|
||||
let pool = ThreadPoolBuilder::new().build().unwrap();
|
||||
let (world, index) = World::generate(
|
||||
123,
|
||||
230,
|
||||
WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
||||
|
@ -174,14 +174,14 @@ impl Tunnel {
|
||||
}
|
||||
}
|
||||
|
||||
// #[inline_tweak::tweak_fn]
|
||||
pub fn biome_at(&self, wpos: Vec3<i32>, info: &CanvasInfo) -> Biome {
|
||||
let Some(col) = info.col_or_gen(wpos.xy()) else {
|
||||
return Biome::default();
|
||||
};
|
||||
|
||||
// Below the ground
|
||||
let below = ((col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * LAYERS as f32 * 0.5))
|
||||
.clamped(0.0, 1.0);
|
||||
let below = ((col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * 2.0)).clamped(0.0, 1.0);
|
||||
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);
|
||||
|
||||
@ -198,13 +198,13 @@ impl Tunnel {
|
||||
let temp = Lerp::lerp_unclamped(
|
||||
col.temp * 2.0,
|
||||
FastNoise2d::new(42)
|
||||
.get(wpos.xy().map(|e| e as f64 / 1536.0))
|
||||
.get(wpos.xy().map(|e| e as f64 / 2048.0))
|
||||
.mul(1.15)
|
||||
.mul(2.0)
|
||||
.sub(1.0)
|
||||
.sub(0.5)
|
||||
.add(
|
||||
((col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * LAYERS as f32 * 0.6))
|
||||
.clamped(0.0, 2.0),
|
||||
((col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * LAYERS as f32))
|
||||
.clamped(0.0, 1.0),
|
||||
),
|
||||
below,
|
||||
);
|
||||
@ -215,7 +215,7 @@ impl Tunnel {
|
||||
.mul(0.5)
|
||||
.add(
|
||||
((col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * LAYERS as f32))
|
||||
.clamped(0.0, 1.5),
|
||||
.clamped(0.0, 1.0),
|
||||
);
|
||||
|
||||
let [
|
||||
@ -230,44 +230,45 @@ impl Tunnel {
|
||||
sandy,
|
||||
] = {
|
||||
// Default biome, no other conditions apply
|
||||
let barren = 0.005;
|
||||
let barren = 0.01;
|
||||
// Mushrooms grow underground and thrive in a humid environment with moderate
|
||||
// temperatures
|
||||
let mushroom = underground
|
||||
* close(humidity, 1.0, 0.7, 4)
|
||||
* close(temp, 1.5, 1.2, 4)
|
||||
* close(depth, 0.9, 0.65, 4);
|
||||
* close(temp, 2.0, 1.8, 4)
|
||||
* close(depth, 0.9, 0.7, 4);
|
||||
// Extremely hot and dry areas deep underground
|
||||
let fire = underground
|
||||
* close(humidity, 0.0, 0.6, 4)
|
||||
* close(temp, 2.0, 1.4, 4)
|
||||
* close(depth, 1.0, 0.45, 4);
|
||||
* close(temp, 2.5, 2.2, 4)
|
||||
* close(depth, 1.0, 0.4, 4);
|
||||
// Overgrown with plants that need a moderate climate to survive
|
||||
let leafy = underground
|
||||
* close(humidity, 0.8, 0.9, 4)
|
||||
* close(temp, 0.8, 1.0, 4)
|
||||
* close(humidity, 0.8, 0.8, 4)
|
||||
* close(temp, 1.2 + depth, 1.5, 4)
|
||||
* close(depth, 0.1, 0.7, 4);
|
||||
// Cool temperature, dry and devoid of value
|
||||
let dusty = close(humidity, 0.0, 0.5, 4)
|
||||
* close(temp, -0.3, 0.7, 4)
|
||||
* close(temp, -0.3, 0.6, 4)
|
||||
* close(depth, 0.5, 0.5, 4);
|
||||
// Deep underground and freezing cold
|
||||
let icy = underground
|
||||
* close(temp, -1.5, 2.0, 4)
|
||||
* close(depth, 0.9, 0.55, 4)
|
||||
* close(humidity, 1.0, 0.75, 4);
|
||||
* close(temp, -2.3 + (depth - 0.5) * 0.5, 2.0, 4)
|
||||
* close(depth, 0.8, 0.6, 4)
|
||||
* close(humidity, 1.0, 0.85, 4);
|
||||
// Rocky cold cave that appear near the surface
|
||||
let snowy = close(temp, -0.7, 0.4, 4) * close(depth, 0.0, 0.4, 4);
|
||||
let snowy = close(temp, -1.8, 1.3, 4) * close(depth, 0.0, 0.6, 4);
|
||||
// Crystals grow deep underground in areas rich with minerals. They are present
|
||||
// in areas with colder temperatures and low humidity
|
||||
let crystal = underground
|
||||
* close(humidity, 0.0, 0.5, 4)
|
||||
* close(temp, -0.9, 0.9, 4)
|
||||
* close(depth, 1.0, 0.55, 4)
|
||||
* close(mineral, 2.0, 1.25, 4);
|
||||
* close(humidity, 0.0, 0.6, 4)
|
||||
* close(temp, -1.6, 1.3, 4)
|
||||
* close(depth, 1.0, 0.5, 4)
|
||||
* close(mineral, 1.5, 1.0, 4);
|
||||
// Hot, dry and shallow
|
||||
let sandy =
|
||||
close(humidity, 0.0, 0.5, 4) * close(temp, 1.0, 0.9, 4) * close(depth, 0.0, 0.6, 4);
|
||||
let sandy = close(humidity, 0.0, 0.4, 4)
|
||||
* close(temp, 0.7, 0.8, 4)
|
||||
* close(depth, 0.0, 0.65, 4);
|
||||
|
||||
let biomes = [
|
||||
barren, mushroom, fire, leafy, dusty, icy, snowy, crystal, sandy,
|
||||
@ -471,6 +472,9 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
.map(|(_, z_range, _, _, _, _)| z_range.clone())
|
||||
.collect_vec();
|
||||
|
||||
// Compute structure samples only once for each column.
|
||||
// TODO Use iter function in StructureGen2d to compute samples for the whole
|
||||
// chunk once
|
||||
let structure_seeds = StructureGen2d::new(34537, 24, 8).get(wpos2d);
|
||||
for (level, z_range, horizontal, vertical, dist, tunnel) in tunnel_bounds {
|
||||
write_column(
|
||||
@ -492,7 +496,6 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Biome {
|
||||
humidity: f32,
|
||||
@ -552,6 +555,7 @@ struct Flower {
|
||||
// rotation: Mat3<f32>,
|
||||
}
|
||||
|
||||
// #[inline_tweak::tweak_fn]
|
||||
fn write_column<R: Rng>(
|
||||
canvas: &mut Canvas,
|
||||
col: &ColumnSample,
|
||||
@ -638,12 +642,12 @@ fn write_column<R: Rng>(
|
||||
|
||||
let basalt = if biome.fire > 0.5 {
|
||||
FastNoise2d::new(36)
|
||||
.get(wpos2d.map(|e| e as f64 / 40.0))
|
||||
.mul(1.1)
|
||||
.sub(0.5)
|
||||
.get(wpos2d.map(|e| e as f64 / 16.0))
|
||||
.mul(1.25)
|
||||
.sub(0.75)
|
||||
.max(0.0)
|
||||
.mul(((cave_width + max_height) / 48.0).clamped(0.0, 1.0))
|
||||
.mul(6.0 + cavern_height * 0.6)
|
||||
.mul(((cave_width + max_height) / 64.0).clamped(0.0, 1.0))
|
||||
.mul(6.0 + cavern_height * 0.5)
|
||||
.mul((biome.fire - 0.5).powi(3) * 8.0)
|
||||
} else {
|
||||
0.0
|
||||
@ -658,7 +662,6 @@ fn write_column<R: Rng>(
|
||||
.min(0.0)
|
||||
// .mul((biome.temp as f64 - 1.5).mul(30.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((biome.fire - 0.5).mul(30.0).clamped(0.0, 1.0))
|
||||
.mul(64.0)
|
||||
.max(-32.0)
|
||||
@ -671,7 +674,7 @@ fn write_column<R: Rng>(
|
||||
.max(biome.dusty)
|
||||
.max(biome.leafy)
|
||||
.max(biome.barren)
|
||||
> 0.5
|
||||
> 0.0
|
||||
{
|
||||
FastNoise2d::new(38)
|
||||
.get(wpos2d.map(|e| e as f64 / 4.0))
|
||||
@ -748,7 +751,7 @@ fn write_column<R: Rng>(
|
||||
};
|
||||
|
||||
let ceiling_drip = ceiling
|
||||
- if !void_above && !sky_above {
|
||||
- if !void_above && !sky_above && overlap.is_none() {
|
||||
let c = if biome.mushroom > 0.9 && ceiling_cover > 0.0 {
|
||||
Some((0.07, 7.0))
|
||||
} else if biome.icy > 0.9 {
|
||||
@ -865,10 +868,9 @@ fn write_column<R: Rng>(
|
||||
&& vertical > 16.0
|
||||
&& horizontal > 16.0
|
||||
&& rng.gen_bool(
|
||||
0.125
|
||||
* (close(vertical, MAX_RADIUS, MAX_RADIUS - 16.0, 2)
|
||||
* close(horizontal, MAX_RADIUS, MAX_RADIUS - 16.0, 2)
|
||||
* biome.leafy) as f64,
|
||||
0.25 * (close(vertical, MAX_RADIUS, MAX_RADIUS - 16.0, 2)
|
||||
* close(horizontal, MAX_RADIUS, MAX_RADIUS - 16.0, 2)
|
||||
* biome.leafy) as f64,
|
||||
)
|
||||
{
|
||||
if tunnel_intersection() {
|
||||
@ -908,23 +910,22 @@ fn write_column<R: Rng>(
|
||||
}
|
||||
});
|
||||
|
||||
// TODO Some way to not clone here?
|
||||
structure
|
||||
.as_ref()
|
||||
.map(|structure| (*seed, structure.clone()))
|
||||
})
|
||||
.collect_vec();
|
||||
let get_structure = |wpos: Vec3<i32>, dynamic_rng: &mut R| {
|
||||
let warp = |wposf: Vec3<f64>, freq: f64, amp: Vec3<f32>, seed: u32| -> Option<Vec3<f32>> {
|
||||
let warp = |wposf: Vec3<f64>, freq: f64, amp: Vec3<f32>, seed: u32| -> Vec3<f32> {
|
||||
let xy = wposf.xy();
|
||||
let xz = Vec2::new(wposf.x, wposf.z);
|
||||
let yz = Vec2::new(wposf.y, wposf.z);
|
||||
Some(
|
||||
Vec3::new(
|
||||
FastNoise2d::new(seed).get(yz * freq),
|
||||
FastNoise2d::new(seed).get(xz * freq),
|
||||
FastNoise2d::new(seed).get(xy * freq),
|
||||
) * amp,
|
||||
)
|
||||
Vec3::new(
|
||||
FastNoise2d::new(seed).get(yz * freq),
|
||||
FastNoise2d::new(seed).get(xz * freq),
|
||||
FastNoise2d::new(seed).get(xy * freq),
|
||||
) * amp
|
||||
};
|
||||
for (seed, structure) in structures.iter() {
|
||||
let seed = *seed;
|
||||
@ -933,7 +934,7 @@ fn write_column<R: Rng>(
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
let warp_freq = 1.0 / 32.0;
|
||||
let warp_amp = Vec3::new(12.0, 12.0, 12.0);
|
||||
let warp_offset = warp(wposf, warp_freq, warp_amp, seed)?;
|
||||
let warp_offset = warp(wposf, warp_freq, warp_amp, seed);
|
||||
let wposf_warped = wposf.map(|e| e as f32)
|
||||
+ warp_offset
|
||||
* (wposf.z as f32 - mushroom.pos.z as f32)
|
||||
@ -1058,7 +1059,7 @@ fn write_column<R: Rng>(
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
let warp_freq = 1.0 / 16.0;
|
||||
let warp_amp = Vec3::new(8.0, 8.0, 8.0);
|
||||
let warp_offset = warp(wposf, warp_freq, warp_amp, seed)?;
|
||||
let warp_offset = warp(wposf, warp_freq, warp_amp, seed);
|
||||
let wposf_warped = wposf.map(|e| e as f32)
|
||||
+ warp_offset
|
||||
* (wposf.z as f32 - flower.pos.z as f32)
|
||||
@ -1149,7 +1150,7 @@ fn write_column<R: Rng>(
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
let warp_freq = 1.0 / 16.0;
|
||||
let warp_amp = Vec3::new(8.0, 8.0, 8.0);
|
||||
let warp_offset = warp(wposf, warp_freq, warp_amp, seed)?;
|
||||
let warp_offset = warp(wposf, warp_freq, warp_amp, seed);
|
||||
let wposf_warped = wposf.map(|e| e as f32) + warp_offset;
|
||||
let rpos = wposf_warped - pos.map(|e| e as f32);
|
||||
let dist_sq = rpos.xy().magnitude_squared();
|
||||
@ -1381,7 +1382,6 @@ fn write_column<R: Rng>(
|
||||
[
|
||||
(SpriteKind::GlowMushroom, 0.5),
|
||||
(SpriteKind::Mushroom, 0.25),
|
||||
(SpriteKind::GrassBlue, 0.0),
|
||||
(SpriteKind::GrassBlueMedium, 1.5),
|
||||
(SpriteKind::GrassBlueLong, 2.0),
|
||||
(SpriteKind::Moonbell, 0.01),
|
||||
@ -1394,7 +1394,7 @@ fn write_column<R: Rng>(
|
||||
&& biome.leafy > 0.4
|
||||
&& rand.chance(
|
||||
wpos2d.with_z(15),
|
||||
biome.leafy.powi(2) * 0.25 * col.marble_mid,
|
||||
biome.leafy.powi(2) * col.marble_mid * (biome.humidity * 1.3) * 0.25,
|
||||
)
|
||||
{
|
||||
let mixed = col.marble.add(col.marble_small.sub(0.5).mul(0.25));
|
||||
@ -1402,7 +1402,6 @@ fn write_column<R: Rng>(
|
||||
return [
|
||||
(SpriteKind::LongGrass, 1.0),
|
||||
(SpriteKind::MediumGrass, 2.0),
|
||||
(SpriteKind::ShortGrass, 0.0),
|
||||
(SpriteKind::JungleFern, 0.5),
|
||||
(SpriteKind::JungleRedGrass, 0.35),
|
||||
(SpriteKind::Fern, 0.75),
|
||||
@ -1429,7 +1428,6 @@ fn write_column<R: Rng>(
|
||||
return [
|
||||
(SpriteKind::LongGrass, 1.0),
|
||||
(SpriteKind::MediumGrass, 2.0),
|
||||
(SpriteKind::ShortGrass, 0.0),
|
||||
(SpriteKind::JungleFern, 0.5),
|
||||
(SpriteKind::JungleLeafyPlant, 0.5),
|
||||
(SpriteKind::JungleRedGrass, 0.35),
|
||||
@ -1551,14 +1549,18 @@ fn write_column<R: Rng>(
|
||||
Block::air(sprite)
|
||||
} else if let Some(sprite) = (z == ceiling - 1 && !void_above)
|
||||
.then(|| {
|
||||
if biome.mushroom > 0.5 && rand.chance(wpos2d.with_z(3), biome.mushroom * 0.01)
|
||||
if biome.mushroom > 0.5
|
||||
&& rand.chance(wpos2d.with_z(3), biome.mushroom.powi(2) * 0.01)
|
||||
{
|
||||
[(SpriteKind::MycelBlue, 0.75), (SpriteKind::Mold, 1.0)]
|
||||
.choose_weighted(rng, |(_, w)| *w)
|
||||
.ok()
|
||||
.map(|s| s.0)
|
||||
} else if biome.leafy > 0.4
|
||||
&& rand.chance(wpos2d.with_z(4), biome.leafy * 0.015)
|
||||
&& rand.chance(
|
||||
wpos2d.with_z(4),
|
||||
biome.leafy * (biome.humidity * 1.3) * 0.015,
|
||||
)
|
||||
{
|
||||
[
|
||||
(SpriteKind::Liana, 1.5),
|
||||
@ -1593,6 +1595,7 @@ fn write_column<R: Rng>(
|
||||
}
|
||||
}
|
||||
|
||||
// #[inline_tweak::tweak_fn]
|
||||
fn apply_entity_spawns<R: Rng>(canvas: &mut Canvas, wpos: Vec3<i32>, biome: &Biome, rng: &mut R) {
|
||||
if RandomField::new(canvas.info().index().seed).chance(wpos, 0.035) {
|
||||
if let Some(entity_asset) = [
|
||||
|
Loading…
Reference in New Issue
Block a user