mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Cave performance improvements
This commit is contained in:
parent
3c846d4d17
commit
767731ecb0
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -3166,8 +3166,22 @@ version = "1.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6acddbefae08bfba73e27f55513f491f35c365d84bf3002bf85ba9b916c5e5f"
|
checksum = "c6acddbefae08bfba73e27f55513f491f35c365d84bf3002bf85ba9b916c5e5f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"inline_tweak_derive",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"proc-macro2 1.0.78",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inline_tweak_derive"
|
||||||
|
version = "3.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46d62a0a3b6af04d4eee8e7251cd758ce74b0ed86253d3e4ac8a1b297a75f4a0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.78",
|
||||||
|
"quote 1.0.35",
|
||||||
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -7442,6 +7456,7 @@ dependencies = [
|
|||||||
"hashbrown 0.13.2",
|
"hashbrown 0.13.2",
|
||||||
"image",
|
"image",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
|
"inline_tweak",
|
||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
"kiddo",
|
"kiddo",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
@ -40,7 +40,7 @@ packed_simd = { version = "0.3.9", optional = true }
|
|||||||
rayon = { workspace = true }
|
rayon = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
ron = { workspace = true }
|
ron = { workspace = true }
|
||||||
# inline_tweak = { workspace = true, features = ["derive"] }
|
inline_tweak = { workspace = true, features = ["derive"] }
|
||||||
kiddo = "0.2"
|
kiddo = "0.2"
|
||||||
strum = { workspace = true }
|
strum = { workspace = true }
|
||||||
|
|
||||||
@ -69,6 +69,10 @@ svg_fmt = "0.4"
|
|||||||
harness = false
|
harness = false
|
||||||
name = "tree"
|
name = "tree"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
harness = false
|
||||||
|
name = "cave"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "chunk_compression_benchmarks"
|
name = "chunk_compression_benchmarks"
|
||||||
required-features = ["bin_compression"]
|
required-features = ["bin_compression"]
|
||||||
|
67
world/benches/cave.rs
Normal file
67
world/benches/cave.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
use rayon::ThreadPoolBuilder;
|
||||||
|
use vek::Vec2;
|
||||||
|
use veloren_world::{
|
||||||
|
layer,
|
||||||
|
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP},
|
||||||
|
Land, World,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn cave(c: &mut Criterion) {
|
||||||
|
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());
|
||||||
|
|
||||||
|
c.bench_function("generate", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let entrances = black_box(layer::cave::surface_entrances(&land))
|
||||||
|
.step_by(5)
|
||||||
|
.map(|e| e / 32);
|
||||||
|
for entrance in entrances {
|
||||||
|
_ = black_box(world.generate_chunk(
|
||||||
|
index.as_index_ref(),
|
||||||
|
entrance,
|
||||||
|
None,
|
||||||
|
|| false,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function("generate_specific", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let base_positions = vec![
|
||||||
|
Vec2::new(600, 650),
|
||||||
|
Vec2::new(630, 300),
|
||||||
|
Vec2::new(809, 141),
|
||||||
|
];
|
||||||
|
for base_pos in base_positions {
|
||||||
|
for i in 0..=4 {
|
||||||
|
for j in 0..=4 {
|
||||||
|
let pos = base_pos + Vec2::new(i as i32, j as i32) - 2;
|
||||||
|
_ = black_box(world.generate_chunk(
|
||||||
|
index.as_index_ref(),
|
||||||
|
pos,
|
||||||
|
None,
|
||||||
|
|| false,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, cave);
|
||||||
|
criterion_main!(benches);
|
@ -174,6 +174,7 @@ impl Tunnel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline_tweak::tweak_fn]
|
||||||
fn biome_at(&self, wpos: Vec3<i32>, info: &CanvasInfo) -> Biome {
|
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();
|
||||||
@ -202,7 +203,7 @@ impl Tunnel {
|
|||||||
.mul(2.0)
|
.mul(2.0)
|
||||||
.sub(1.0)
|
.sub(1.0)
|
||||||
.add(
|
.add(
|
||||||
((col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * LAYERS as f32 * 0.75))
|
((col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * LAYERS as f32 * 0.6))
|
||||||
.clamped(0.0, 2.5),
|
.clamped(0.0, 2.5),
|
||||||
),
|
),
|
||||||
below,
|
below,
|
||||||
@ -233,38 +234,38 @@ impl Tunnel {
|
|||||||
// 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.7, 3)
|
* close(humidity, 1.0, 0.7, 4)
|
||||||
* close(temp, 1.5, 0.9, 3)
|
* close(temp, 1.5, 0.9, 4)
|
||||||
* close(depth, 1.0, 0.6, 3);
|
* close(depth, 1.0, 0.6, 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, 3)
|
* close(humidity, 0.0, 0.6, 4)
|
||||||
* close(temp, 2.0, 1.3, 3)
|
* close(temp, 2.0, 1.3, 4)
|
||||||
* close(depth, 1.0, 0.55, 3);
|
* close(depth, 1.0, 0.55, 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.8, 0.8, 3)
|
* close(humidity, 0.8, 0.8, 4)
|
||||||
* close(temp, 0.95, 0.85, 3)
|
* close(temp, 0.75, 1.25, 4)
|
||||||
* close(depth, 0.0, 0.6, 3);
|
* close(depth, 0.0, 0.75, 4);
|
||||||
// Cool temperature, dry and devoid of value
|
// Cool temperature, dry and devoid of value
|
||||||
let dusty = close(humidity, 0.0, 0.5, 3) * close(temp, -0.1, 0.6, 3);
|
let dusty = close(humidity, 0.0, 0.5, 4) * close(temp, -0.1, 0.6, 4);
|
||||||
// Deep underground and freezing cold
|
// Deep underground and freezing cold
|
||||||
let icy = underground
|
let icy = underground
|
||||||
* close(temp, -1.5, 1.3, 3)
|
* close(temp, -1.5, 1.3, 4)
|
||||||
* close(depth, 1.0, 0.65, 3)
|
* close(depth, 1.0, 0.6, 4)
|
||||||
* close(humidity, 1.0, 0.7, 3);
|
* close(humidity, 1.0, 0.7, 4);
|
||||||
// Rocky cold cave that appear near the surface
|
// Rocky cold cave that appear near the surface
|
||||||
let snowy = close(temp, -0.6, 0.5, 3) * close(depth, 0.0, 0.45, 3);
|
let snowy = close(temp, -0.6, 0.5, 4) * close(depth, 0.0, 0.45, 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, 3)
|
* close(humidity, 0.0, 0.5, 4)
|
||||||
* close(temp, -0.6, 0.75, 3)
|
* close(temp, -0.6, 0.75, 4)
|
||||||
* close(depth, 1.0, 0.55, 3)
|
* close(depth, 1.0, 0.55, 4)
|
||||||
* close(mineral, 2.0, 1.25, 3);
|
* close(mineral, 2.0, 1.25, 4);
|
||||||
// Hot, dry and shallow
|
// Hot, dry and shallow
|
||||||
let sandy =
|
let sandy =
|
||||||
close(humidity, 0.0, 0.3, 3) * close(temp, 0.7, 0.9, 3) * close(depth, 0.0, 0.6, 3);
|
close(humidity, 0.0, 0.3, 4) * close(temp, 0.7, 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,
|
||||||
@ -434,6 +435,7 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if !tunnels.is_empty() {
|
if !tunnels.is_empty() {
|
||||||
let giant_tree_dist = info
|
let giant_tree_dist = info
|
||||||
.chunk
|
.chunk
|
||||||
@ -497,6 +499,7 @@ struct Biome {
|
|||||||
depth: f32,
|
depth: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
enum CaveStructure {
|
enum CaveStructure {
|
||||||
Mushroom(Mushroom),
|
Mushroom(Mushroom),
|
||||||
Crystal(CrystalCluster),
|
Crystal(CrystalCluster),
|
||||||
@ -508,24 +511,28 @@ enum CaveStructure {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Mushroom {
|
struct Mushroom {
|
||||||
pos: Vec3<i32>,
|
pos: Vec3<i32>,
|
||||||
stalk: f32,
|
stalk: f32,
|
||||||
head_color: Rgb<u8>,
|
head_color: Rgb<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Crystal {
|
struct Crystal {
|
||||||
dir: Vec3<f32>,
|
dir: Vec3<f32>,
|
||||||
length: f32,
|
length: f32,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct CrystalCluster {
|
struct CrystalCluster {
|
||||||
pos: Vec3<i32>,
|
pos: Vec3<i32>,
|
||||||
crystals: Vec<Crystal>,
|
crystals: Vec<Crystal>,
|
||||||
color: Rgb<u8>,
|
color: Rgb<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Flower {
|
struct Flower {
|
||||||
pos: Vec3<i32>,
|
pos: Vec3<i32>,
|
||||||
stalk: f32,
|
stalk: f32,
|
||||||
@ -535,6 +542,7 @@ struct Flower {
|
|||||||
// rotation: Mat3<f32>,
|
// rotation: Mat3<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline_tweak::tweak_fn]
|
||||||
fn write_column<R: Rng>(
|
fn write_column<R: Rng>(
|
||||||
canvas: &mut Canvas,
|
canvas: &mut Canvas,
|
||||||
col: &ColumnSample,
|
col: &ColumnSample,
|
||||||
@ -543,7 +551,7 @@ fn write_column<R: Rng>(
|
|||||||
z_range: Range<i32>,
|
z_range: Range<i32>,
|
||||||
tunnel: Tunnel,
|
tunnel: Tunnel,
|
||||||
dimensions: (f32, f32, f32),
|
dimensions: (f32, f32, f32),
|
||||||
giant_tree_factor: f32,
|
giant_tree_dist: f32,
|
||||||
structure_cache: &mut SmallCache<Vec3<i32>, Option<CaveStructure>>,
|
structure_cache: &mut SmallCache<Vec3<i32>, Option<CaveStructure>>,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) {
|
) {
|
||||||
@ -741,154 +749,173 @@ fn write_column<R: Rng>(
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut get_structure = |wpos: Vec3<i32>, dynamic_rng: &mut R| {
|
let structures = StructureGen2d::new(34537, 24, 8)
|
||||||
for (wpos2d, seed) in StructureGen2d::new(34537, 24, 8).get(wpos.xy()) {
|
.get(wpos2d)
|
||||||
let structure = if let Some(structure) =
|
.as_slice()
|
||||||
structure_cache.get(wpos2d.with_z(tunnel.a.depth), |_| {
|
.iter()
|
||||||
let mut rng = RandomPerm::new(seed);
|
.filter_map(|(wpos2d, seed)| {
|
||||||
let (z_range, horizontal, vertical, _) =
|
let structure = structure_cache.get(wpos2d.with_z(tunnel.a.depth), |_| {
|
||||||
tunnel.z_range_at(wpos2d.map(|e| e as f64 + 0.5), info)?;
|
let mut rng = RandomPerm::new(*seed);
|
||||||
let pos = wpos2d.with_z(z_range.start);
|
let (z_range, horizontal, vertical, _) =
|
||||||
|
tunnel.z_range_at(wpos2d.map(|e| e as f64 + 0.5), info)?;
|
||||||
let biome = tunnel.biome_at(pos, &info);
|
let pos = wpos2d.with_z(z_range.start);
|
||||||
let ground_below = !tunnel_bounds_at(pos.xy(), &info, &info.land())
|
let biome = tunnel.biome_at(pos, &info);
|
||||||
.any(|(_, z_range, _, _, _, _)| z_range.contains(&(z_range.start - 1)));
|
let tunnel_intersection = || {
|
||||||
if !ground_below {
|
tunnel_bounds_at(pos.xy(), &info, &info.land())
|
||||||
|
.any(|(_, z_range, _, _, _, _)| z_range.contains(&(z_range.start - 1)))
|
||||||
|
};
|
||||||
|
if biome.mushroom > 0.7
|
||||||
|
&& vertical > 16.0
|
||||||
|
&& rng.gen_bool(
|
||||||
|
0.5 * close(vertical, MAX_RADIUS, MAX_RADIUS - 16.0, 2) as f64
|
||||||
|
* close(biome.mushroom, 1.0, 0.7, 1) as f64,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if tunnel_intersection() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let purp = rng.gen_range(0..50);
|
||||||
if biome.mushroom > 0.7
|
Some(CaveStructure::Mushroom(Mushroom {
|
||||||
&& vertical > 16.0
|
pos,
|
||||||
&& rng.gen_bool(
|
stalk: 8.0
|
||||||
0.5 * close(vertical, MAX_RADIUS, MAX_RADIUS - 16.0, 2) as f64
|
+ rng.gen::<f32>().powf(2.0)
|
||||||
* close(biome.mushroom, 1.0, 0.7, 1) as f64,
|
* (z_range.end - z_range.start - 8) as f32
|
||||||
)
|
* 0.75,
|
||||||
{
|
head_color: Rgb::new(
|
||||||
let purp = rng.gen_range(0..50);
|
40 + purp,
|
||||||
Some(CaveStructure::Mushroom(Mushroom {
|
rng.gen_range(60..120),
|
||||||
pos,
|
rng.gen_range(80..200) + purp,
|
||||||
stalk: 8.0
|
),
|
||||||
+ rng.gen::<f32>().powf(2.0)
|
}))
|
||||||
* (z_range.end - z_range.start - 8) as f32
|
} else if biome.crystal > 0.5
|
||||||
* 0.75,
|
&& rng.gen_bool(0.4 * close(biome.crystal, 1.0, 0.7, 2) as f64)
|
||||||
head_color: Rgb::new(
|
{
|
||||||
40 + purp,
|
if tunnel_intersection() {
|
||||||
rng.gen_range(60..120),
|
return None;
|
||||||
rng.gen_range(80..200) + purp,
|
|
||||||
),
|
|
||||||
}))
|
|
||||||
} else if biome.crystal > 0.5
|
|
||||||
&& rng.gen_bool(0.4 * close(biome.crystal, 1.0, 0.7, 2) as f64)
|
|
||||||
{
|
|
||||||
let on_ground = rng.gen_bool(0.6);
|
|
||||||
let pos = wpos2d.with_z(if on_ground {
|
|
||||||
z_range.start
|
|
||||||
} else {
|
|
||||||
z_range.end
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut crystals: Vec<Crystal> = Vec::new();
|
|
||||||
let max_length =
|
|
||||||
(48.0 * close(vertical, MAX_RADIUS, MAX_RADIUS, 1)).max(12.0);
|
|
||||||
let length = rng.gen_range(8.0..max_length);
|
|
||||||
let radius =
|
|
||||||
Lerp::lerp(2.0, 4.5, length / max_length + rng.gen_range(-0.1..0.1));
|
|
||||||
let dir = Vec3::new(
|
|
||||||
rng.gen_range(-3.0..3.0),
|
|
||||||
rng.gen_range(-3.0..3.0),
|
|
||||||
rng.gen_range(0.5..10.0) * if on_ground { 1.0 } else { -1.0 },
|
|
||||||
)
|
|
||||||
.normalized();
|
|
||||||
|
|
||||||
crystals.push(Crystal {
|
|
||||||
dir,
|
|
||||||
length,
|
|
||||||
radius,
|
|
||||||
});
|
|
||||||
|
|
||||||
(0..4).for_each(|_| {
|
|
||||||
crystals.push(Crystal {
|
|
||||||
dir: Vec3::new(
|
|
||||||
rng.gen_range(-1.0..1.0),
|
|
||||||
rng.gen_range(-1.0..1.0),
|
|
||||||
(dir.z + rng.gen_range(-0.2..0.2)).clamped(0.0, 1.0),
|
|
||||||
),
|
|
||||||
length: length * rng.gen_range(0.3..0.8),
|
|
||||||
radius: (radius * rng.gen_range(0.5..0.8)).max(1.0),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let purple = rng.gen_range(25..75);
|
|
||||||
let blue = (rng.gen_range(45.0..75.0) * biome.icy) as u8;
|
|
||||||
|
|
||||||
Some(CaveStructure::Crystal(CrystalCluster {
|
|
||||||
pos,
|
|
||||||
crystals,
|
|
||||||
color: Rgb::new(
|
|
||||||
255 - blue * 2,
|
|
||||||
255 - blue - purple,
|
|
||||||
200 + rng.gen_range(25..55),
|
|
||||||
),
|
|
||||||
}))
|
|
||||||
} else if biome.leafy > 0.8
|
|
||||||
&& vertical > 16.0
|
|
||||||
&& horizontal > 8.0
|
|
||||||
&& rng.gen_bool(
|
|
||||||
0.25 * (close(vertical, MAX_RADIUS, MAX_RADIUS - 16.0, 2)
|
|
||||||
* close(horizontal, MAX_RADIUS, MAX_RADIUS - 8.0, 2)
|
|
||||||
* biome.leafy) as f64,
|
|
||||||
)
|
|
||||||
{
|
|
||||||
let petal_radius = rng.gen_range(8.0..16.0);
|
|
||||||
Some(CaveStructure::Flower(Flower {
|
|
||||||
pos,
|
|
||||||
stalk: 6.0
|
|
||||||
+ rng.gen::<f32>().powf(2.0)
|
|
||||||
* (z_range.end - z_range.start - 8) as f32
|
|
||||||
* 0.75,
|
|
||||||
petals: rng.gen_range(1..5) * 2 + 1,
|
|
||||||
petal_height: 0.4 * petal_radius * (1.0 + rng.gen::<f32>().powf(2.0)),
|
|
||||||
petal_radius,
|
|
||||||
}))
|
|
||||||
} else if (biome.leafy > 0.7 || giant_tree_factor > 0.0)
|
|
||||||
&& rng.gen_bool(
|
|
||||||
(0.5 * close(biome.leafy, 1.0, 0.5, 1).max(1.0 + giant_tree_factor)
|
|
||||||
as f64)
|
|
||||||
.clamped(0.0, 1.0),
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Some(CaveStructure::GiantRoot {
|
|
||||||
pos,
|
|
||||||
radius: rng.gen_range(
|
|
||||||
1.5..(3.5
|
|
||||||
+ close(vertical, MAX_RADIUS, MAX_RADIUS / 2.0, 2) * 3.0
|
|
||||||
+ close(horizontal, MAX_RADIUS, MAX_RADIUS / 2.0, 2) * 3.0),
|
|
||||||
),
|
|
||||||
height: (z_range.end - z_range.start) as f32,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}) {
|
let on_ground = rng.gen_bool(0.6);
|
||||||
structure
|
let pos = wpos2d.with_z(if on_ground {
|
||||||
} else {
|
z_range.start
|
||||||
continue;
|
} else {
|
||||||
};
|
z_range.end
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut crystals: Vec<Crystal> = Vec::new();
|
||||||
|
let max_length = (48.0 * close(vertical, MAX_RADIUS, MAX_RADIUS, 1)).max(12.0);
|
||||||
|
let length = rng.gen_range(8.0..max_length);
|
||||||
|
let radius =
|
||||||
|
Lerp::lerp(2.0, 4.5, length / max_length + rng.gen_range(-0.1..0.1));
|
||||||
|
let dir = Vec3::new(
|
||||||
|
rng.gen_range(-3.0..3.0),
|
||||||
|
rng.gen_range(-3.0..3.0),
|
||||||
|
rng.gen_range(0.5..10.0) * if on_ground { 1.0 } else { -1.0 },
|
||||||
|
)
|
||||||
|
.normalized();
|
||||||
|
|
||||||
|
crystals.push(Crystal {
|
||||||
|
dir,
|
||||||
|
length,
|
||||||
|
radius,
|
||||||
|
});
|
||||||
|
|
||||||
|
(0..4).for_each(|_| {
|
||||||
|
crystals.push(Crystal {
|
||||||
|
dir: Vec3::new(
|
||||||
|
rng.gen_range(-1.0..1.0),
|
||||||
|
rng.gen_range(-1.0..1.0),
|
||||||
|
(dir.z + rng.gen_range(-0.2..0.2)).clamped(0.0, 1.0),
|
||||||
|
),
|
||||||
|
length: length * rng.gen_range(0.3..0.8),
|
||||||
|
radius: (radius * rng.gen_range(0.5..0.8)).max(1.0),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let purple = rng.gen_range(25..75);
|
||||||
|
let blue = (rng.gen_range(45.0..75.0) * biome.icy) as u8;
|
||||||
|
|
||||||
|
Some(CaveStructure::Crystal(CrystalCluster {
|
||||||
|
pos,
|
||||||
|
crystals,
|
||||||
|
color: Rgb::new(
|
||||||
|
255 - blue * 2,
|
||||||
|
255 - blue - purple,
|
||||||
|
200 + rng.gen_range(25..55),
|
||||||
|
),
|
||||||
|
}))
|
||||||
|
} else if biome.leafy > 0.8
|
||||||
|
&& vertical > 16.0
|
||||||
|
&& horizontal > 8.0
|
||||||
|
&& rng.gen_bool(
|
||||||
|
0.25 * (close(vertical, MAX_RADIUS, MAX_RADIUS - 16.0, 2)
|
||||||
|
* close(horizontal, MAX_RADIUS, MAX_RADIUS - 8.0, 2)
|
||||||
|
* biome.leafy) as f64,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if tunnel_intersection() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let petal_radius = rng.gen_range(8.0..16.0);
|
||||||
|
Some(CaveStructure::Flower(Flower {
|
||||||
|
pos,
|
||||||
|
stalk: 6.0
|
||||||
|
+ rng.gen::<f32>().powf(2.0)
|
||||||
|
* (z_range.end - z_range.start - 8) as f32
|
||||||
|
* 0.75,
|
||||||
|
petals: rng.gen_range(1..5) * 2 + 1,
|
||||||
|
petal_height: 0.4 * petal_radius * (1.0 + rng.gen::<f32>().powf(2.0)),
|
||||||
|
petal_radius,
|
||||||
|
}))
|
||||||
|
} else if (biome.leafy > 0.7 || giant_tree_dist > 0.0)
|
||||||
|
&& rng.gen_bool(
|
||||||
|
(0.5 * close(biome.leafy, 1.0, 0.5, 1).max(1.0 + giant_tree_dist) as f64)
|
||||||
|
.clamped(0.0, 1.0),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if tunnel_intersection() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(CaveStructure::GiantRoot {
|
||||||
|
pos,
|
||||||
|
radius: rng.gen_range(
|
||||||
|
1.5..(3.5
|
||||||
|
+ close(vertical, MAX_RADIUS, MAX_RADIUS / 2.0, 2) * 3.0
|
||||||
|
+ close(horizontal, MAX_RADIUS, MAX_RADIUS / 2.0, 2) * 3.0),
|
||||||
|
),
|
||||||
|
height: (z_range.end - z_range.start) as f32,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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 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,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
for (seed, structure) in structures.iter() {
|
||||||
|
let seed = *seed;
|
||||||
match structure {
|
match structure {
|
||||||
CaveStructure::Mushroom(mushroom) => {
|
CaveStructure::Mushroom(mushroom) => {
|
||||||
let wposf = wpos.map(|e| e as f64);
|
let wposf = wpos.map(|e| e as f64);
|
||||||
let warp_freq = 1.0 / 32.0;
|
let warp_freq = 1.0 / 32.0;
|
||||||
let warp_amp = Vec3::new(12.0, 12.0, 12.0);
|
let warp_amp = Vec3::new(12.0, 12.0, 12.0);
|
||||||
let xy = wposf.xy();
|
let warp_offset = warp(wposf, warp_freq, warp_amp, seed)?;
|
||||||
let xz = Vec2::new(wposf.x, wposf.z);
|
|
||||||
let yz = Vec2::new(wposf.y, wposf.z);
|
|
||||||
let wposf_warped = wposf.map(|e| e as f32)
|
let wposf_warped = wposf.map(|e| e as f32)
|
||||||
+ Vec3::new(
|
+ warp_offset
|
||||||
FastNoise2d::new(seed).get(yz * warp_freq),
|
|
||||||
FastNoise2d::new(seed).get(xz * warp_freq),
|
|
||||||
FastNoise2d::new(seed).get(xy * warp_freq),
|
|
||||||
) * warp_amp
|
|
||||||
* (wposf.z as f32 - mushroom.pos.z as f32)
|
* (wposf.z as f32 - mushroom.pos.z as f32)
|
||||||
.mul(0.1)
|
.mul(0.1)
|
||||||
.clamped(0.0, 1.0);
|
.clamped(0.0, 1.0);
|
||||||
@ -1011,15 +1038,9 @@ fn write_column<R: Rng>(
|
|||||||
let wposf = wpos.map(|e| e as f64);
|
let wposf = wpos.map(|e| e as f64);
|
||||||
let warp_freq = 1.0 / 16.0;
|
let warp_freq = 1.0 / 16.0;
|
||||||
let warp_amp = Vec3::new(8.0, 8.0, 8.0);
|
let warp_amp = Vec3::new(8.0, 8.0, 8.0);
|
||||||
let xy = wposf.xy();
|
let warp_offset = warp(wposf, warp_freq, warp_amp, seed)?;
|
||||||
let xz = Vec2::new(wposf.x, wposf.z);
|
|
||||||
let yz = Vec2::new(wposf.y, wposf.z);
|
|
||||||
let wposf_warped = wposf.map(|e| e as f32)
|
let wposf_warped = wposf.map(|e| e as f32)
|
||||||
+ Vec3::new(
|
+ warp_offset
|
||||||
FastNoise2d::new(seed).get(yz * warp_freq),
|
|
||||||
FastNoise2d::new(seed).get(xz * warp_freq),
|
|
||||||
FastNoise2d::new(seed).get(xy * warp_freq),
|
|
||||||
) * warp_amp
|
|
||||||
* (wposf.z as f32 - flower.pos.z as f32)
|
* (wposf.z as f32 - flower.pos.z as f32)
|
||||||
.mul(1.0 / flower.stalk)
|
.mul(1.0 / flower.stalk)
|
||||||
.sub(1.0)
|
.sub(1.0)
|
||||||
@ -1044,8 +1065,7 @@ fn write_column<R: Rng>(
|
|||||||
let dist_sq = rpos.xy().magnitude_squared();
|
let dist_sq = rpos.xy().magnitude_squared();
|
||||||
let petal_radius_sq = flower.petal_radius.powi(2);
|
let petal_radius_sq = flower.petal_radius.powi(2);
|
||||||
if dist_sq < petal_radius_sq {
|
if dist_sq < petal_radius_sq {
|
||||||
let petal_height_at =
|
let petal_height_at = (dist_sq / petal_radius_sq) * flower.petal_height;
|
||||||
(dist_sq / petal_radius_sq).powf(1.0) * flower.petal_height;
|
|
||||||
if rpos.z > petal_height_at - 1.0
|
if rpos.z > petal_height_at - 1.0
|
||||||
&& rpos.z <= petal_height_at + petal_thickness
|
&& rpos.z <= petal_height_at + petal_thickness
|
||||||
{
|
{
|
||||||
@ -1108,16 +1128,9 @@ fn write_column<R: Rng>(
|
|||||||
} => {
|
} => {
|
||||||
let wposf = wpos.map(|e| e as f64);
|
let wposf = wpos.map(|e| e as f64);
|
||||||
let warp_freq = 1.0 / 32.0;
|
let warp_freq = 1.0 / 32.0;
|
||||||
let warp_amp = Vec3::new(20.0, 20.0, 20.0);
|
let warp_amp = Vec3::new(12.0, 12.0, 12.0);
|
||||||
let xy = wposf.xy();
|
let warp_offset = warp(wposf, warp_freq, warp_amp, seed)?;
|
||||||
let xz = Vec2::new(wposf.x, wposf.z);
|
let wposf_warped = wposf.map(|e| e as f32) + warp_offset;
|
||||||
let yz = Vec2::new(wposf.y, wposf.z);
|
|
||||||
let wposf_warped = wposf.map(|e| e as f32)
|
|
||||||
+ Vec3::new(
|
|
||||||
FastNoise2d::new(seed).get(yz * warp_freq),
|
|
||||||
FastNoise2d::new(seed).get(xz * warp_freq),
|
|
||||||
FastNoise2d::new(seed).get(xy * warp_freq),
|
|
||||||
) * warp_amp;
|
|
||||||
let rpos = wposf_warped - pos.map(|e| e as f32);
|
let rpos = wposf_warped - pos.map(|e| e as f32);
|
||||||
let dist_sq = rpos.xy().magnitude_squared();
|
let dist_sq = rpos.xy().magnitude_squared();
|
||||||
if dist_sq < radius.powi(2) {
|
if dist_sq < radius.powi(2) {
|
||||||
@ -1142,7 +1155,7 @@ fn write_column<R: Rng>(
|
|||||||
for z in bedrock..z_range.end {
|
for z in bedrock..z_range.end {
|
||||||
let wpos = wpos2d.with_z(z);
|
let wpos = wpos2d.with_z(z);
|
||||||
let mut try_spawn_entity = false;
|
let mut try_spawn_entity = false;
|
||||||
canvas.map_resource(wpos, |_block| {
|
canvas.set(wpos, {
|
||||||
if z < z_range.start - 4 && !void_below {
|
if z < z_range.start - 4 && !void_below {
|
||||||
Block::new(BlockKind::Lava, Rgb::new(255, 65, 0))
|
Block::new(BlockKind::Lava, Rgb::new(255, 65, 0))
|
||||||
} else if basalt > 0.0
|
} else if basalt > 0.0
|
||||||
|
@ -111,6 +111,58 @@ impl StructureGen2d {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Note: Generates all possible closest samples for elements in the range
|
||||||
|
/// of min to max, *exclusive.*
|
||||||
|
pub fn iter_with_wpos(
|
||||||
|
&self,
|
||||||
|
min: Vec2<i32>,
|
||||||
|
max: Vec2<i32>,
|
||||||
|
) -> impl Iterator<Item = (Vec2<i32>, StructureField)> {
|
||||||
|
let freq = self.freq;
|
||||||
|
let spread = self.spread;
|
||||||
|
let spread_mul = Self::spread_mul(spread);
|
||||||
|
assert!(spread * 2 == spread_mul);
|
||||||
|
assert!(spread_mul <= freq);
|
||||||
|
let spread = spread as i32;
|
||||||
|
let freq = freq as i32;
|
||||||
|
let freq_offset = Self::freq_offset(freq);
|
||||||
|
assert!(freq_offset * 2 == freq);
|
||||||
|
|
||||||
|
let min_index = Self::sample_to_index_internal(freq, min) - 1;
|
||||||
|
let max_index = Self::sample_to_index_internal(freq, max) + 1;
|
||||||
|
assert!(min_index.x < max_index.x);
|
||||||
|
// NOTE: xlen > 0
|
||||||
|
let xlen = (max_index.x - min_index.x) as u32;
|
||||||
|
assert!(min_index.y < max_index.y);
|
||||||
|
// NOTE: ylen > 0
|
||||||
|
let ylen = (max_index.y - min_index.y) as u32;
|
||||||
|
// NOTE: Cannot fail, since every product of u32s fits in a u64.
|
||||||
|
let len = ylen as u64 * xlen as u64;
|
||||||
|
// NOTE: since iteration is *exclusive* for the initial range, it's fine that we
|
||||||
|
// don't go up to the maximum value.
|
||||||
|
// NOTE: we convert to usize first, and then iterate, because we want to make
|
||||||
|
// sure we get a properly indexed parallel iterator that can deal with
|
||||||
|
// the whole range at once.
|
||||||
|
let x_field = self.x_field;
|
||||||
|
let y_field = self.y_field;
|
||||||
|
let seed_field = self.seed_field;
|
||||||
|
(0..len).map(move |xy| {
|
||||||
|
let index = min_index + Vec2::new((xy % xlen as u64) as i32, (xy / xlen as u64) as i32);
|
||||||
|
let index_wpos = min + Vec2::new((xy % xlen as u64) as i32, (xy / len as u64) as i32);
|
||||||
|
let field = Self::index_to_sample_internal(
|
||||||
|
freq,
|
||||||
|
freq_offset,
|
||||||
|
spread,
|
||||||
|
spread_mul,
|
||||||
|
x_field,
|
||||||
|
y_field,
|
||||||
|
seed_field,
|
||||||
|
index,
|
||||||
|
);
|
||||||
|
(index_wpos, field)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sampler<'static> for StructureGen2d {
|
impl Sampler<'static> for StructureGen2d {
|
||||||
|
Loading…
Reference in New Issue
Block a user