mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Adopt cave performance improvements from zoomy-worldgen
This commit is contained in:
parent
5d9202337e
commit
53cbb90b37
@ -1,22 +1,25 @@
|
|||||||
use super::scatter::close;
|
use super::scatter::close;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
util::{sampler::Sampler, FastNoise2d, RandomField, RandomPerm, StructureGen2d, LOCALITY},
|
util::{
|
||||||
|
sampler::Sampler, FastNoise2d, RandomField, RandomPerm, StructureGen2d, LOCALITY, SQUARE_4,
|
||||||
|
},
|
||||||
Canvas, CanvasInfo, ColumnSample, Land,
|
Canvas, CanvasInfo, ColumnSample, Land,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
generation::EntityInfo,
|
generation::EntityInfo,
|
||||||
terrain::{
|
terrain::{
|
||||||
quadratic_nearest_point, river_spline_coeffs, Block, BlockKind, CoordinateConversions,
|
quadratic_nearest_point, river_spline_coeffs, Block, BlockKind, CoordinateConversions,
|
||||||
SpriteKind,
|
SpriteKind, TerrainChunkSize,
|
||||||
},
|
},
|
||||||
|
vol::RectVolSize,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
|
||||||
use inline_tweak::tweak_fn;
|
use inline_tweak::tweak_fn;
|
||||||
use noise::NoiseFn;
|
use noise::NoiseFn;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
|
collections::HashMap,
|
||||||
f64::consts::PI,
|
f64::consts::PI,
|
||||||
ops::{Add, Mul, Range, Sub},
|
ops::{Add, Mul, Range, Sub},
|
||||||
};
|
};
|
||||||
@ -39,6 +42,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;
|
const LAYERS: u32 = 4;
|
||||||
|
const MAX_RADIUS: f32 = 64.0;
|
||||||
|
|
||||||
fn node_at(cell: Vec2<i32>, level: u32, land: &Land) -> Option<Node> {
|
fn node_at(cell: Vec2<i32>, level: u32, land: &Land) -> Option<Node> {
|
||||||
let rand = RandomField::new(37 + level);
|
let rand = RandomField::new(37 + level);
|
||||||
@ -76,6 +80,7 @@ pub fn surface_entrances<'a>(land: &'a Land) -> impl Iterator<Item = Vec2<i32>>
|
|||||||
.filter_map(|cell| Some(tunnel_below_from_cell(cell, 0, land)?.a.wpos))
|
.filter_map(|cell| Some(tunnel_below_from_cell(cell, 0, land)?.a.wpos))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct Tunnel {
|
pub struct Tunnel {
|
||||||
a: Node,
|
a: Node,
|
||||||
b: Node,
|
b: Node,
|
||||||
@ -83,6 +88,8 @@ pub struct Tunnel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Tunnel {
|
impl Tunnel {
|
||||||
|
const RADIUS_RANGE: Range<f64> = 8.0..MAX_RADIUS as f64;
|
||||||
|
|
||||||
fn ctrl_offset(&self) -> Vec2<f32> {
|
fn ctrl_offset(&self) -> Vec2<f32> {
|
||||||
let start = self.a.wpos.map(|e| e as f64 + 0.5);
|
let start = self.a.wpos.map(|e| e as f64 + 0.5);
|
||||||
let end = self.b.wpos.map(|e| e as f64 + 0.5);
|
let end = self.b.wpos.map(|e| e as f64 + 0.5);
|
||||||
@ -91,70 +98,77 @@ impl Tunnel {
|
|||||||
.map(|e| e as f32)
|
.map(|e| e as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tweak_fn]
|
fn possibly_near(&self, wposf: Vec2<f64>, threshold: f64) -> Option<(f64, Vec2<f64>, f64)> {
|
||||||
fn z_range_at(
|
|
||||||
&self,
|
|
||||||
wposf: Vec2<f64>,
|
|
||||||
info: CanvasInfo,
|
|
||||||
) -> Option<(Range<i32>, f64, f64, f64)> {
|
|
||||||
let start = self.a.wpos.map(|e| e as f64 + 0.5);
|
let start = self.a.wpos.map(|e| e as f64 + 0.5);
|
||||||
let end = self.b.wpos.map(|e| e as f64 + 0.5);
|
let end = self.b.wpos.map(|e| e as f64 + 0.5);
|
||||||
|
|
||||||
if let Some((t, closest, _)) = quadratic_nearest_point(
|
if let Some((t, closest, _)) = quadratic_nearest_point(
|
||||||
&river_spline_coeffs(start, self.ctrl_offset(), end),
|
&river_spline_coeffs(start, self.ctrl_offset(), end),
|
||||||
wposf,
|
wposf,
|
||||||
Vec2::new(start, end),
|
Vec2::new(start, end),
|
||||||
) {
|
) {
|
||||||
let dist = closest.distance(wposf);
|
let dist2 = closest.distance_squared(wposf);
|
||||||
let radius = 8.0..64.0;
|
if dist2 < (Self::RADIUS_RANGE.end + threshold).powi(2) {
|
||||||
if dist < radius.end + 1.0 {
|
Some((t, closest, dist2.sqrt()))
|
||||||
let horizontal = Lerp::lerp(
|
} else {
|
||||||
radius.start,
|
None
|
||||||
radius.end,
|
}
|
||||||
(info.index().noise.cave_fbm_nz.get(
|
} else {
|
||||||
(closest.with_z(info.land().get_alt_approx(self.a.wpos) as f64) / 256.0)
|
None
|
||||||
.into_array(),
|
}
|
||||||
) + 0.5)
|
}
|
||||||
.clamped(0.0, 1.0)
|
|
||||||
.powf(3.0),
|
|
||||||
);
|
|
||||||
let vertical = Lerp::lerp(
|
|
||||||
radius.start,
|
|
||||||
radius.end,
|
|
||||||
(info.index().noise.cave_fbm_nz.get(
|
|
||||||
(closest.with_z(info.land().get_alt_approx(self.b.wpos) as f64) / 256.0)
|
|
||||||
.into_array(),
|
|
||||||
) + 0.5)
|
|
||||||
.clamped(0.0, 1.0)
|
|
||||||
.powf(3.0),
|
|
||||||
)
|
|
||||||
.clamped(radius.start + 0.0, radius.end);
|
|
||||||
let height_here = (1.0 - dist / horizontal).max(0.0).powf(0.3) * vertical;
|
|
||||||
|
|
||||||
if height_here > 0.0 {
|
#[tweak_fn]
|
||||||
let z_offs = info
|
fn z_range_at(
|
||||||
.index()
|
&self,
|
||||||
.noise
|
wposf: Vec2<f64>,
|
||||||
.cave_fbm_nz
|
info: CanvasInfo,
|
||||||
.get((wposf / 512.0).into_array())
|
) -> Option<(Range<i32>, f32, f32, f32)> {
|
||||||
* 96.0
|
let _start = self.a.wpos.map(|e| e as f64 + 0.5);
|
||||||
* ((1.0 - (t - 0.5).abs() * 2.0) * 8.0).min(1.0);
|
let _end = self.b.wpos.map(|e| e as f64 + 0.5);
|
||||||
let alt_here = info.land().get_alt_approx(closest.map(|e| e as i32));
|
if let Some((t, closest, dist)) = self.possibly_near(wposf, 1.0) {
|
||||||
let base = (Lerp::lerp(
|
let horizontal = Lerp::lerp(
|
||||||
alt_here as f64 - self.a.depth as f64,
|
Self::RADIUS_RANGE.start,
|
||||||
alt_here as f64 - self.b.depth as f64,
|
Self::RADIUS_RANGE.end,
|
||||||
t,
|
(info.index().noise.cave_fbm_nz.get(
|
||||||
) + z_offs)
|
(closest.with_z(info.land().get_alt_approx(self.a.wpos) as f64) / 256.0)
|
||||||
.min(alt_here as f64);
|
.into_array(),
|
||||||
Some((
|
) + 0.5)
|
||||||
(base - height_here * 0.3) as i32..(base + height_here * 1.35) as i32,
|
.clamped(0.0, 1.0)
|
||||||
horizontal,
|
.powf(3.0),
|
||||||
vertical,
|
);
|
||||||
dist,
|
let vertical = Lerp::lerp(
|
||||||
))
|
Self::RADIUS_RANGE.start,
|
||||||
} else {
|
Self::RADIUS_RANGE.end,
|
||||||
None
|
(info.index().noise.cave_fbm_nz.get(
|
||||||
}
|
(closest.with_z(info.land().get_alt_approx(self.b.wpos) as f64) / 256.0)
|
||||||
|
.into_array(),
|
||||||
|
) + 0.5)
|
||||||
|
.clamped(0.0, 1.0)
|
||||||
|
.powf(3.0),
|
||||||
|
);
|
||||||
|
let height_here = (1.0 - dist / horizontal).max(0.0).powf(0.3) * vertical;
|
||||||
|
|
||||||
|
if height_here > 0.0 {
|
||||||
|
let z_offs = info
|
||||||
|
.index()
|
||||||
|
.noise
|
||||||
|
.cave_fbm_nz
|
||||||
|
.get((wposf / 512.0).into_array())
|
||||||
|
* 96.0
|
||||||
|
* ((1.0 - (t - 0.5).abs() * 2.0) * 8.0).min(1.0);
|
||||||
|
let alt_here = info.land().get_alt_approx(closest.map(|e| e as i32));
|
||||||
|
let base = (Lerp::lerp(
|
||||||
|
alt_here as f64 - self.a.depth as f64,
|
||||||
|
alt_here as f64 - self.b.depth as f64,
|
||||||
|
t,
|
||||||
|
) + z_offs)
|
||||||
|
.min(alt_here as f64);
|
||||||
|
Some((
|
||||||
|
(base - height_here * 0.3) as i32..(base + height_here * 1.35) as i32,
|
||||||
|
horizontal as f32,
|
||||||
|
vertical as f32,
|
||||||
|
dist as f32,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -178,39 +192,34 @@ impl Tunnel {
|
|||||||
// effectively increase biome size
|
// effectively increase biome size
|
||||||
let humidity = Lerp::lerp(
|
let humidity = Lerp::lerp(
|
||||||
col.humidity,
|
col.humidity,
|
||||||
info.index()
|
FastNoise2d::new(41)
|
||||||
.noise
|
.get(wpos.xy().map(|e| e as f64 / 768.0))
|
||||||
.cave_nz
|
.mul(1.2),
|
||||||
.get(wpos.xy().map(|e| e as f64 / 1024.0).into_array()) as f32,
|
|
||||||
below,
|
below,
|
||||||
);
|
);
|
||||||
|
|
||||||
let temp = Lerp::lerp(
|
let temp = Lerp::lerp(
|
||||||
col.temp,
|
col.temp,
|
||||||
info.index()
|
FastNoise2d::new(42)
|
||||||
.noise
|
.get(wpos.xy().map(|e| e as f64 / 1536.0))
|
||||||
.cave_nz
|
.mul(1.15)
|
||||||
.get(wpos.xy().map(|e| e as f64 / 2048.0).into_array())
|
|
||||||
.mul(2.0)
|
.mul(2.0)
|
||||||
.sub(1.0)
|
.sub(1.0)
|
||||||
.add(
|
.add(
|
||||||
((col.alt as f64 - wpos.z as f64)
|
((col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * LAYERS as f32 * 0.5))
|
||||||
/ (AVG_LEVEL_DEPTH as f64 * LAYERS as f64 * 0.5))
|
|
||||||
.clamped(0.0, 2.5),
|
.clamped(0.0, 2.5),
|
||||||
) as f32,
|
),
|
||||||
below,
|
below,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mineral = info
|
let mineral = FastNoise2d::new(43)
|
||||||
.index()
|
.get(wpos.xy().map(|e| e as f64 / 320.0))
|
||||||
.noise
|
.mul(1.15)
|
||||||
.cave_nz
|
|
||||||
.get(wpos.xy().map(|e| e as f64 / 512.0).into_array())
|
|
||||||
.mul(0.5)
|
.mul(0.5)
|
||||||
.add(
|
.add(
|
||||||
((col.alt as f64 - wpos.z as f64) / (AVG_LEVEL_DEPTH as f64 * LAYERS as f64))
|
((col.alt - wpos.z as f32) / (AVG_LEVEL_DEPTH as f32 * LAYERS as f32))
|
||||||
.clamped(0.0, 1.5),
|
.clamped(0.0, 1.5),
|
||||||
) as f32;
|
);
|
||||||
|
|
||||||
let [
|
let [
|
||||||
barren,
|
barren,
|
||||||
@ -228,35 +237,37 @@ 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.70)
|
* close(humidity, 1.0, 0.7)
|
||||||
* close(temp, 1.2, 1.0)
|
* close(temp, 1.5, 0.9)
|
||||||
* close(depth, 1.0, 0.7);
|
* close(depth, 1.0, 0.6);
|
||||||
// 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)
|
* close(humidity, 0.0, 0.6)
|
||||||
* close(temp, 2.5, 1.5)
|
* close(temp, 2.5, 1.4)
|
||||||
* close(depth, 1.0, 0.55);
|
* close(depth, 1.0, 0.5);
|
||||||
// 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.5, 0.7)
|
* close(humidity, 0.8, 0.8)
|
||||||
* close(temp, 0.8, 0.7)
|
* close(temp, 1.0, 0.8)
|
||||||
* close(depth, 0.0, 0.75);
|
* close(depth, 0.0, 0.6);
|
||||||
// Cool temperature, dry and devoid of value
|
// Cool temperature, dry and devoid of value
|
||||||
let dusty =
|
let dusty = close(humidity, 0.0, 0.5) * close(temp, -0.1, 0.6);
|
||||||
close(humidity, 0.0, 0.5) * close(temp, -0.1, 0.6) * close(mineral, -1.0, 2.0);
|
|
||||||
// Deep underground and freezing cold
|
// Deep underground and freezing cold
|
||||||
let icy = underground * close(temp, -1.0, 0.6) * close(depth, 1.0, 0.5);
|
let icy = underground
|
||||||
|
* close(temp, -1.0, 0.6)
|
||||||
|
* close(depth, 1.0, 0.5)
|
||||||
|
* close(humidity, 1.0, 0.7);
|
||||||
// Rocky cold cave that appear near the surface
|
// Rocky cold cave that appear near the surface
|
||||||
let snowy = close(temp, -0.5, 0.3) * close(depth, 0.0, 0.5);
|
let snowy = close(temp, -0.5, 0.3) * close(depth, 0.0, 0.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.65)
|
* close(humidity, 0.0, 0.7)
|
||||||
* close(temp, -0.3, 0.9)
|
* close(temp, -0.5, 0.8)
|
||||||
* close(depth, 1.0, 0.6)
|
* close(depth, 1.0, 0.55)
|
||||||
* close(mineral, 1.5, 1.5);
|
* close(mineral, 1.5, 1.0);
|
||||||
// Hot, dry and shallow
|
// Hot, dry and shallow
|
||||||
let sandy = close(humidity, 0.0, 0.2) * close(temp, 0.7, 0.8) * close(depth, 0.1, 0.5);
|
let sandy = close(humidity, 0.0, 0.3) * close(temp, 0.7, 0.9) * close(depth, 0.0, 0.6);
|
||||||
|
|
||||||
let biomes = [
|
let biomes = [
|
||||||
barren, mushroom, fire, leafy, dusty, icy, snowy, crystal, sandy,
|
barren, mushroom, fire, leafy, dusty, icy, snowy, crystal, sandy,
|
||||||
@ -352,69 +363,109 @@ fn tunnels_down_from<'a>(
|
|||||||
.filter_map(move |rpos| tunnel_below_from_cell(col_cell + rpos, level, land))
|
.filter_map(move |rpos| tunnel_below_from_cell(col_cell + rpos, level, land))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn all_tunnels_at<'a>(
|
||||||
|
wpos2d: Vec2<i32>,
|
||||||
|
_info: &'a CanvasInfo,
|
||||||
|
land: &'a Land,
|
||||||
|
) -> impl Iterator<Item = (u32, Tunnel)> + 'a {
|
||||||
|
(1..LAYERS + 1).flat_map(move |level| {
|
||||||
|
tunnels_at(wpos2d, level, land)
|
||||||
|
.chain(tunnels_down_from(wpos2d, level - 1, land))
|
||||||
|
.map(move |tunnel| (level, tunnel))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tunnel_bounds_at_from<'a>(
|
||||||
|
wpos2d: Vec2<i32>,
|
||||||
|
info: &'a CanvasInfo,
|
||||||
|
_land: &'a Land,
|
||||||
|
tunnels: impl Iterator<Item = (u32, Tunnel)> + 'a,
|
||||||
|
) -> impl Iterator<Item = (u32, Range<i32>, f32, f32, f32, Tunnel)> + 'a {
|
||||||
|
let wposf = wpos2d.map(|e| e as f64 + 0.5);
|
||||||
|
info.col_or_gen(wpos2d)
|
||||||
|
.map(move |col| {
|
||||||
|
let col_alt = col.alt;
|
||||||
|
let col_water_dist = col.water_dist;
|
||||||
|
tunnels.filter_map(move |(level, tunnel)| {
|
||||||
|
let (z_range, horizontal, vertical, dist) = tunnel.z_range_at(wposf, *info)?;
|
||||||
|
// Avoid cave entrances intersecting water
|
||||||
|
let z_range = Lerp::lerp(
|
||||||
|
z_range.end,
|
||||||
|
z_range.start,
|
||||||
|
1.0 - (1.0
|
||||||
|
- ((col_water_dist.unwrap_or(1000.0) - 4.0).max(0.0) / 32.0)
|
||||||
|
.clamped(0.0, 1.0))
|
||||||
|
* (1.0 - ((col_alt - z_range.end as f32 - 4.0) / 8.0).clamped(0.0, 1.0)),
|
||||||
|
)..z_range.end;
|
||||||
|
if z_range.end - z_range.start > 0 {
|
||||||
|
Some((level, z_range, horizontal, vertical, dist, tunnel))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tunnel_bounds_at<'a>(
|
pub fn tunnel_bounds_at<'a>(
|
||||||
wpos2d: Vec2<i32>,
|
wpos2d: Vec2<i32>,
|
||||||
info: &'a CanvasInfo,
|
info: &'a CanvasInfo,
|
||||||
land: &'a Land,
|
land: &'a Land,
|
||||||
) -> impl Iterator<Item = (u32, Range<i32>, f64, f64, f64, Tunnel)> + 'a {
|
) -> impl Iterator<Item = (u32, Range<i32>, f32, f32, f32, Tunnel)> + 'a {
|
||||||
let wposf = wpos2d.map(|e| e as f64 + 0.5);
|
tunnel_bounds_at_from(wpos2d, info, land, all_tunnels_at(wpos2d, info, land))
|
||||||
info.col_or_gen(wpos2d).into_iter().flat_map(move |col| {
|
|
||||||
let col_alt = col.alt;
|
|
||||||
let col_water_dist = col.water_dist;
|
|
||||||
(1..LAYERS + 1).flat_map(move |level| {
|
|
||||||
tunnels_at(wpos2d, level, land)
|
|
||||||
.chain(tunnels_down_from(wpos2d, level - 1, land))
|
|
||||||
.filter_map(move |tunnel| {
|
|
||||||
let (z_range, horizontal, vertical, dist) = tunnel.z_range_at(wposf, *info)?;
|
|
||||||
// Avoid cave entrances intersecting water
|
|
||||||
let z_range = Lerp::lerp(
|
|
||||||
z_range.end,
|
|
||||||
z_range.start,
|
|
||||||
1.0 - (1.0
|
|
||||||
- ((col_water_dist.unwrap_or(1000.0) - 4.0).max(0.0) / 32.0)
|
|
||||||
.clamped(0.0, 1.0))
|
|
||||||
* (1.0
|
|
||||||
- ((col_alt - z_range.end as f32 - 4.0) / 8.0).clamped(0.0, 1.0)),
|
|
||||||
)..z_range.end;
|
|
||||||
if z_range.end - z_range.start > 0 {
|
|
||||||
Some((level, z_range, horizontal, vertical, dist, tunnel))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||||
let info = canvas.info();
|
let info = canvas.info();
|
||||||
let mut structure_cache = HashMap::new();
|
let land = info.land();
|
||||||
canvas.foreach_col(|canvas, wpos2d, col| {
|
|
||||||
let land = info.land();
|
|
||||||
|
|
||||||
let tunnel_bounds = tunnel_bounds_at(wpos2d, &info, &land).collect::<Vec<_>>();
|
let diagonal = (TerrainChunkSize::RECT_SIZE.map(|e| e * e).sum() as f32).sqrt() as f64;
|
||||||
|
let tunnels = all_tunnels_at(
|
||||||
|
info.wpos() + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2,
|
||||||
|
&info,
|
||||||
|
&land,
|
||||||
|
)
|
||||||
|
.filter(|(_, tunnel)| {
|
||||||
|
SQUARE_4
|
||||||
|
.into_iter()
|
||||||
|
.map(|rpos| info.wpos() + rpos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
||||||
|
.any(|wpos| {
|
||||||
|
tunnel
|
||||||
|
.possibly_near(wpos.map(|e| e as f64), diagonal + 1.0)
|
||||||
|
.is_some()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if !tunnels.is_empty() {
|
||||||
|
let mut structure_cache = HashMap::new();
|
||||||
|
canvas.foreach_col(|canvas, wpos2d, col| {
|
||||||
|
let tunnel_bounds =
|
||||||
|
tunnel_bounds_at_from(wpos2d, &info, &land, tunnels.iter().copied())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// First, clear out tunnels
|
// First, clear out tunnels
|
||||||
for (_, z_range, _, _, _, _) in &tunnel_bounds {
|
for (_, z_range, _, _, _, _) in &tunnel_bounds {
|
||||||
for z in z_range.start..z_range.end.min(col.alt as i32 + 1) {
|
for z in z_range.start..z_range.end.min(col.alt as i32 + 1) {
|
||||||
canvas.set(wpos2d.with_z(z), Block::air(SpriteKind::Empty));
|
canvas.set(wpos2d.with_z(z), Block::empty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
||||||
col,
|
col,
|
||||||
level,
|
level,
|
||||||
wpos2d,
|
wpos2d,
|
||||||
z_range.clone(),
|
z_range.clone(),
|
||||||
tunnel,
|
tunnel,
|
||||||
(horizontal, vertical, dist),
|
(horizontal, vertical, dist),
|
||||||
&mut structure_cache,
|
&mut structure_cache,
|
||||||
rng,
|
rng,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -479,7 +530,7 @@ fn write_column<R: Rng>(
|
|||||||
wpos2d: Vec2<i32>,
|
wpos2d: Vec2<i32>,
|
||||||
z_range: Range<i32>,
|
z_range: Range<i32>,
|
||||||
tunnel: Tunnel,
|
tunnel: Tunnel,
|
||||||
dimensions: (f64, f64, f64),
|
dimensions: (f32, f32, f32),
|
||||||
structure_cache: &mut HashMap<(Vec3<i32>, Vec2<i32>), Option<CaveStructure>>,
|
structure_cache: &mut HashMap<(Vec3<i32>, Vec2<i32>), Option<CaveStructure>>,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) {
|
) {
|
||||||
@ -490,23 +541,21 @@ fn write_column<R: Rng>(
|
|||||||
let void_below = !canvas.get(wpos2d.with_z(z_range.start - 1)).is_filled();
|
let void_below = !canvas.get(wpos2d.with_z(z_range.start - 1)).is_filled();
|
||||||
// Exposed to the sky
|
// Exposed to the sky
|
||||||
let sky_above = z_range.end as f32 > col.alt;
|
let sky_above = z_range.end as f32 > col.alt;
|
||||||
let cavern_height = (z_range.end - z_range.start) as f64;
|
let cavern_height = (z_range.end - z_range.start) as f32;
|
||||||
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);
|
||||||
|
|
||||||
let stalactite = {
|
let stalactite = {
|
||||||
info
|
FastNoise2d::new(35)
|
||||||
.index()
|
.get(wpos2d.map(|e| e as f64 / 8.0))
|
||||||
.noise
|
.mul(1.0)
|
||||||
.cave_nz
|
.sub(0.5 + (biome.leafy - 1.0).max(0.0))
|
||||||
.get(wpos2d.map(|e| e as f64 / 16.0).into_array())
|
|
||||||
.sub(0.5 + (biome.leafy - 1.0).max(0.0) as f64)
|
|
||||||
.max(0.0)
|
.max(0.0)
|
||||||
.mul(2.0)
|
.mul(2.0)
|
||||||
.add((biome.leafy - 1.0).max(0.0) as f64)
|
.add((biome.leafy - 1.0).max(0.0))
|
||||||
// No stalactites near entrances
|
// No stalactites near entrances
|
||||||
.mul(((col.alt as f64 - z_range.end as f64) / 8.0).clamped(0.0, 1.0))
|
.mul(((col.alt - z_range.end as f32) / 32.0).clamped(0.0, 1.0))
|
||||||
.mul(8.0 + cavern_height * (0.4 + (biome.sandy as f64 - 0.5).max(0.0)))
|
.mul(4.0 + cavern_height * (0.4 + (biome.sandy - 0.5).max(0.0)))
|
||||||
};
|
};
|
||||||
|
|
||||||
let ceiling_cover = if (biome.leafy - 0.3)
|
let ceiling_cover = if (biome.leafy - 0.3)
|
||||||
@ -516,8 +565,8 @@ fn write_column<R: Rng>(
|
|||||||
.max(biome.fire - 0.5)
|
.max(biome.fire - 0.5)
|
||||||
> 0.0
|
> 0.0
|
||||||
{
|
{
|
||||||
1.0.mul(((col.alt as f64 - z_range.end as f64) / 8.0).clamped(0.0, 1.0))
|
1.0.mul(((col.alt - z_range.end as f32) / 32.0).clamped(0.0, 1.0))
|
||||||
.mul(cavern_height * (dist_cave_center / (cave_width)).powf(3.33))
|
.mul(cavern_height * (dist_cave_center / cave_width).powf(3.33))
|
||||||
.max(1.0)
|
.max(1.0)
|
||||||
.sub(
|
.sub(
|
||||||
if col.marble_mid
|
if col.marble_mid
|
||||||
@ -528,7 +577,7 @@ fn write_column<R: Rng>(
|
|||||||
.max(biome.leafy - 0.4)
|
.max(biome.leafy - 0.4)
|
||||||
.max(biome.mushroom - 0.4)
|
.max(biome.mushroom - 0.4)
|
||||||
{
|
{
|
||||||
cavern_height * col.marble_mid as f64
|
cavern_height * col.marble_mid
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
},
|
},
|
||||||
@ -539,29 +588,26 @@ fn write_column<R: Rng>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let basalt = if biome.fire > 0.0 {
|
let basalt = if biome.fire > 0.0 {
|
||||||
info.index()
|
FastNoise2d::new(36)
|
||||||
.noise
|
.get(wpos2d.map(|e| e as f64 / 32.0))
|
||||||
.cave_nz
|
.mul(1.25)
|
||||||
.get(wpos2d.map(|e| e as f64 / 48.0).into_array())
|
|
||||||
.sub(0.5)
|
.sub(0.5)
|
||||||
.max(0.0)
|
.max(0.0)
|
||||||
.mul(6.0 + cavern_height * 0.5)
|
.mul(6.0 + cavern_height * 0.5)
|
||||||
.mul(biome.fire as f64)
|
.mul(biome.fire)
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
|
||||||
let lava = if biome.fire > 0.0 {
|
let lava = if biome.fire > 0.0 {
|
||||||
info.index()
|
FastNoise2d::new(37)
|
||||||
.noise
|
.get(wpos2d.map(|e| e as f64 / 32.0))
|
||||||
.cave_nz
|
.mul(0.5)
|
||||||
.get(wpos2d.map(|e| e as f64 / 64.0).into_array())
|
|
||||||
.sub(0.5)
|
|
||||||
.abs()
|
.abs()
|
||||||
.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((biome.fire as f64 - 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)
|
||||||
.max(-32.0)
|
.max(-32.0)
|
||||||
} else {
|
} else {
|
||||||
@ -570,11 +616,9 @@ fn write_column<R: Rng>(
|
|||||||
|
|
||||||
let height_factor = (max_height / 32.0).clamped(0.0, 1.0).powf(2.0);
|
let height_factor = (max_height / 32.0).clamped(0.0, 1.0).powf(2.0);
|
||||||
let width_factor = (cave_width / 32.0).clamped(0.0, 1.0).powf(2.0);
|
let width_factor = (cave_width / 32.0).clamped(0.0, 1.0).powf(2.0);
|
||||||
let ridge = info
|
let ridge = FastNoise2d::new(38)
|
||||||
.index()
|
.get(wpos2d.map(|e| e as f64 / 512.0))
|
||||||
.noise
|
.mul(1.0)
|
||||||
.cave_nz
|
|
||||||
.get(wpos2d.map(|e| e as f64 / 512.0).into_array())
|
|
||||||
.sub(0.25)
|
.sub(0.25)
|
||||||
.max(0.0)
|
.max(0.0)
|
||||||
.mul(1.3)
|
.mul(1.3)
|
||||||
@ -582,28 +626,22 @@ fn write_column<R: Rng>(
|
|||||||
.mul(width_factor)
|
.mul(width_factor)
|
||||||
.mul(
|
.mul(
|
||||||
(0.75 * dist_cave_center)
|
(0.75 * dist_cave_center)
|
||||||
+ max_height
|
+ max_height * (close(dist_cave_center, cave_width, cave_width * 0.7)),
|
||||||
* (close(
|
)
|
||||||
dist_cave_center as f32,
|
.mul(((col.alt - z_range.end as f32) / 64.0).clamped(0.0, 1.0));
|
||||||
cave_width as f32,
|
|
||||||
cave_width as f32 * 0.7,
|
|
||||||
)) as f64,
|
|
||||||
);
|
|
||||||
|
|
||||||
let bump = info
|
let bump = FastNoise2d::new(39)
|
||||||
.index()
|
.get(wpos2d.map(|e| e as f64 / 8.0))
|
||||||
.noise
|
.mul(1.0)
|
||||||
.cave_nz
|
|
||||||
.get(wpos2d.map(|e| e as f64 / 8.0).into_array())
|
|
||||||
.add(1.0)
|
.add(1.0)
|
||||||
.mul(0.5)
|
.mul(0.5)
|
||||||
.mul(((col.alt as f64 - z_range.end as f64) / 16.0).clamped(0.0, 1.0))
|
.mul(((col.alt - z_range.end as f32) / 16.0).clamped(0.0, 1.0))
|
||||||
.mul({
|
.mul({
|
||||||
let (val, total) = [
|
let (val, total) = [
|
||||||
(biome.sandy as f64 - 0.3, 0.9),
|
(biome.sandy - 0.3, 0.9),
|
||||||
(biome.dusty as f64 - 0.2, 0.5),
|
(biome.dusty - 0.2, 0.5),
|
||||||
(biome.leafy as f64 - 0.5, 0.6),
|
(biome.leafy - 0.5, 0.6),
|
||||||
(biome.barren as f64 - 0.1, 0.6),
|
(biome.barren - 0.1, 0.6),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.fold((0.0, 0.0), |a, x| (a.0 + x.0.max(0.0) * x.1, a.1 + x.1));
|
.fold((0.0, 0.0), |a, x| (a.0 + x.0.max(0.0) * x.1, a.1 + x.1));
|
||||||
@ -642,7 +680,7 @@ fn write_column<R: Rng>(
|
|||||||
|
|
||||||
if biome.mushroom > 0.7
|
if biome.mushroom > 0.7
|
||||||
&& rng.gen_bool(
|
&& rng.gen_bool(
|
||||||
0.5 * close(vertical as f32, 64.0, 48.0) as f64
|
0.5 * close(vertical, MAX_RADIUS, 48.0) as f64
|
||||||
* close(biome.mushroom, 1.0, 0.7) as f64,
|
* close(biome.mushroom, 1.0, 0.7) as f64,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -660,7 +698,7 @@ fn write_column<R: Rng>(
|
|||||||
),
|
),
|
||||||
}))
|
}))
|
||||||
} else if biome.crystal > 0.5
|
} else if biome.crystal > 0.5
|
||||||
&& rng.gen_bool(0.6 * close(biome.crystal, 1.0, 0.7) as f64)
|
&& rng.gen_bool(0.4 * close(biome.crystal, 1.0, 0.7) as f64)
|
||||||
{
|
{
|
||||||
let colors = [
|
let colors = [
|
||||||
Rgb::new(209, 106, 255),
|
Rgb::new(209, 106, 255),
|
||||||
@ -677,7 +715,7 @@ fn write_column<R: Rng>(
|
|||||||
|
|
||||||
let mut crystals: Vec<Crystal> = Vec::new();
|
let mut crystals: Vec<Crystal> = Vec::new();
|
||||||
|
|
||||||
let max_length = (48.0 * close(vertical as f32, 64.0, 42.0)).max(12.0);
|
let max_length = (48.0 * close(vertical, MAX_RADIUS, 42.0)).max(12.0);
|
||||||
let main_length = rng.gen_range(8.0..max_length);
|
let main_length = rng.gen_range(8.0..max_length);
|
||||||
let main_radius = Lerp::lerp_unclamped(
|
let main_radius = Lerp::lerp_unclamped(
|
||||||
2.0,
|
2.0,
|
||||||
@ -715,7 +753,7 @@ fn write_column<R: Rng>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let mut color: Rgb<u8> = *(colors.choose(&mut rng).unwrap());
|
let mut color: Rgb<u8> = *(colors.choose(&mut rng).unwrap());
|
||||||
if tunnel.biome_at(pos, &info).icy > 0.125 {
|
if tunnel.biome_at(pos, &info).icy > 0.5 {
|
||||||
color.r = color.r.saturating_sub(150u8);
|
color.r = color.r.saturating_sub(150u8);
|
||||||
color.g = color.g.saturating_sub(40u8);
|
color.g = color.g.saturating_sub(40u8);
|
||||||
color.b = color.b.saturating_add(0u8);
|
color.b = color.b.saturating_add(0u8);
|
||||||
@ -727,19 +765,21 @@ fn write_column<R: Rng>(
|
|||||||
}))
|
}))
|
||||||
} else if biome.leafy > 0.8
|
} else if biome.leafy > 0.8
|
||||||
&& rng.gen_bool(
|
&& rng.gen_bool(
|
||||||
0.2 * close(vertical as f32, 64.0, 52.0) as f64
|
0.2 * (close(vertical, MAX_RADIUS, MAX_RADIUS - 16.0)
|
||||||
* close(biome.leafy, 1.0, 0.2) as f64,
|
* close(horizontal, MAX_RADIUS, MAX_RADIUS - 12.0)
|
||||||
|
* close(biome.leafy, 1.0, 0.2))
|
||||||
|
as f64,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Some(CaveStructure::Flower(Flower {
|
Some(CaveStructure::Flower(Flower {
|
||||||
pos,
|
pos,
|
||||||
stalk: 8.0
|
stalk: 4.0
|
||||||
+ rng.gen::<f32>().powf(2.0)
|
+ rng.gen::<f32>().powf(2.0)
|
||||||
* (z_range.end - z_range.start - 8) as f32
|
* (z_range.end - z_range.start - 8) as f32
|
||||||
* 0.8,
|
* 0.75,
|
||||||
petals: rng.gen_range(1..5) * 2 + 1,
|
petals: rng.gen_range(1..5) * 2 + 1,
|
||||||
petal_height: rng.gen_range(4.0..8.0),
|
petal_height: rng.gen_range(4.0..16.0),
|
||||||
petal_radius: rng.gen_range(10.0..18.0),
|
petal_radius: rng.gen_range(8.0..16.0),
|
||||||
rotation: (Mat3::rotation_x(
|
rotation: (Mat3::rotation_x(
|
||||||
-(rng.gen_bool(1.0) as u32 as f32) * std::f32::consts::PI
|
-(rng.gen_bool(1.0) as u32 as f32) * std::f32::consts::PI
|
||||||
/ rng.gen_range(3.0..16.0),
|
/ rng.gen_range(3.0..16.0),
|
||||||
@ -748,15 +788,15 @@ fn write_column<R: Rng>(
|
|||||||
))
|
))
|
||||||
.transposed(),
|
.transposed(),
|
||||||
}))
|
}))
|
||||||
} else if biome.leafy > 0.5
|
} else if biome.leafy > 0.8
|
||||||
&& rng.gen_bool(0.6 * close(biome.leafy, 1.0, 0.5) as f64)
|
&& rng.gen_bool(0.4 * close(biome.leafy, 1.0, 0.4) as f64)
|
||||||
{
|
{
|
||||||
Some(CaveStructure::GiantRoot {
|
Some(CaveStructure::GiantRoot {
|
||||||
pos,
|
pos,
|
||||||
radius: rng.gen_range(
|
radius: rng.gen_range(
|
||||||
3.0..(6.0
|
2.0..(6.0
|
||||||
+ close(vertical as f32, 64.0, 32.0) * 2.0
|
+ close(vertical, MAX_RADIUS, MAX_RADIUS / 2.0) * 2.0
|
||||||
+ close(horizontal as f32, 64.0, 32.0) * 2.0),
|
+ close(horizontal, MAX_RADIUS, MAX_RADIUS / 2.0) * 2.0),
|
||||||
),
|
),
|
||||||
height: (z_range.end - z_range.start) as f32,
|
height: (z_range.end - z_range.start) as f32,
|
||||||
})
|
})
|
||||||
@ -1018,7 +1058,8 @@ fn write_column<R: Rng>(
|
|||||||
let mold_pos = mold_posf.map(|e| e.floor() as i32);
|
let mold_pos = mold_posf.map(|e| e.floor() as i32);
|
||||||
let mut rng = RandomPerm::new(((mold_pos.x << 16) | mold_pos.y) as u32);
|
let mut rng = RandomPerm::new(((mold_pos.x << 16) | mold_pos.y) as u32);
|
||||||
|
|
||||||
if biome.mushroom > 0.7
|
if !void_above
|
||||||
|
&& biome.mushroom > 0.7
|
||||||
&& ceiling_cover > 0.0
|
&& ceiling_cover > 0.0
|
||||||
&& rng.gen_bool(0.025 * close(biome.mushroom, 1.0, 0.3) as f64)
|
&& rng.gen_bool(0.025 * close(biome.mushroom, 1.0, 0.3) as f64)
|
||||||
{
|
{
|
||||||
@ -1092,9 +1133,7 @@ fn write_column<R: Rng>(
|
|||||||
biome.icy,
|
biome.icy,
|
||||||
);
|
);
|
||||||
Block::new(
|
Block::new(
|
||||||
if rand.chance(wpos, biome.mushroom * 0.01) {
|
if rand.chance(wpos, (biome.mushroom * 0.01).max(biome.icy)) {
|
||||||
BlockKind::GlowingWeakRock
|
|
||||||
} else if rand.chance(wpos, biome.icy) {
|
|
||||||
BlockKind::GlowingWeakRock
|
BlockKind::GlowingWeakRock
|
||||||
} else if rand.chance(wpos, biome.sandy) {
|
} else if rand.chance(wpos, biome.sandy) {
|
||||||
BlockKind::Sand
|
BlockKind::Sand
|
||||||
@ -1382,10 +1421,10 @@ fn write_column<R: Rng>(
|
|||||||
.flatten()
|
.flatten()
|
||||||
{
|
{
|
||||||
Block::air(sprite)
|
Block::air(sprite)
|
||||||
} else if let Some(vine) =
|
} else if let Some(mold) =
|
||||||
ceiling_mold(wposf).or_else(|| ceiling_mold(wposf.xy().yx().with_z(wposf.z)))
|
ceiling_mold(wposf).or_else(|| ceiling_mold(wposf.xy().yx().with_z(wposf.z)))
|
||||||
{
|
{
|
||||||
vine
|
mold
|
||||||
} else if let Some(structure_block) = get_structure(wpos, rng) {
|
} else if let Some(structure_block) = get_structure(wpos, rng) {
|
||||||
structure_block
|
structure_block
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user