Merge branch 'zesterer/shrubs' into 'master'

Shrubs (and water fixes)

See merge request veloren/veloren!2796
This commit is contained in:
Joshua Barretto 2021-10-04 23:24:56 +00:00
commit 38ecfe7c8b
27 changed files with 1188 additions and 882 deletions

View File

@ -13,12 +13,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added a crafting station icon to the crafting menu sidebar for items that could be crafted at a crafting station
- Added a setting to disable the hotkey hints
- Added a credits screen in the main menu which shows attributions for assets
- Shrubs, a system for spawning smaller tree-like plants into the world.
- Waterfalls
- Sailing boat (currently requires spawning in)
### Changed
- Made dungeon tiers 3, 4, and 5 more common
- Put date at the begining of the log file instead of the end to allow MIME type recognition
- Tweaked CR and exp calculation formula
- Sprite spawn rates
### Removed
@ -27,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The menu map now properly handles dragging the map, zooming, and setting the waypoint when hovering icons
- Falling through an airship in flight should no longer be possible (although many issues with airship physics remain)
- Avoided black hexagons when bloom is enabled by suppressing NaN/Inf pixels during the first bloom blur pass
- Many know water generation problems
## [0.11.0] - 2021-09-11

View File

@ -47,4 +47,26 @@
central: ("air_balloon.rudder"),
),
),
SailBoat: (
bone0: (
offset: (-6.5, -15.5, 0.0),
phys_offset: (0.0, 0.0, 0.0),
central: ("sail_boat.structure"),
),
bone1: (
offset: (0.0, 0.0, 0.0),
phys_offset: (0.0, 0.0, 0.0),
central: ("empty"),
),
bone2: (
offset: (0.0, 0.0, 0.0),
phys_offset: (0.0, 0.0, 0.0),
central: ("empty"),
),
bone3: (
offset: (-1.5, -6.0, -5.0),
phys_offset: (0.0, 0.0, 0.0),
central: ("empty"),
),
),
})

BIN
assets/server/voxel/sail_boat/structure.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -204,7 +204,7 @@ void main() {
const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2);
const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2);
// float faces_fluid = faces_fluid && f_pos.z <= floor(f_alt);
float fluid_alt = max(f_pos.z + 1, floor(f_alt));
float fluid_alt = max(f_pos.z + 1, floor(f_alt + 1));
float R_s = /*(f_pos.z < f_alt)*/faces_fluid /*&& f_pos.z <= fluid_alt*/ ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x);
// vec3 surf_color = /*srgb_to_linear*/(f_col);

View File

@ -0,0 +1,32 @@
#![enable(unwrap_newtypes)]
[
(
specifier: "world.shrub.1",
center: (6, 6, 2),
),
(
specifier: "world.shrub.jungle.bush-0",
center: (5, 5, 3),
),
(
specifier: "world.shrub.jungle.bush-1",
center: (5, 5, 2),
),
(
specifier: "world.shrub.jungle.bush-2",
center: (5, 5, 3),
),
(
specifier: "world.shrub.jungle.bush-3",
center: (5, 5, 3),
),
(
specifier: "world.shrub.jungle.bush-4",
center: (5, 5, 4),
),
(
specifier: "world.shrub.jungle.bush-5",
center: (5, 5, 5),
),
]

BIN
assets/world/shrub/1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/shrub/jungle/bush-0.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/shrub/jungle/bush-1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/shrub/jungle/bush-2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/shrub/jungle/bush-3.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/shrub/jungle/bush-4.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/shrub/jungle/bush-5.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -854,6 +854,7 @@ impl Body {
Body::Ship(ship) => match ship {
ship::Body::DefaultAirship => [0.0, 0.0, 10.0],
ship::Body::AirBalloon => [0.0, 0.0, 5.0],
ship::Body::SailBoat => [-2.0, -5.0, 4.0],
},
_ => [0.0, 0.0, 0.0],
}

View File

@ -1,13 +1,13 @@
use crate::{
comp::{Density, Mass},
consts::AIR_DENSITY,
consts::{AIR_DENSITY, WATER_DENSITY},
make_case_elim,
};
use rand::prelude::SliceRandom;
use serde::{Deserialize, Serialize};
use vek::Vec3;
pub const ALL_BODIES: [Body; 2] = [Body::DefaultAirship, Body::AirBalloon];
pub const ALL_BODIES: [Body; 3] = [Body::DefaultAirship, Body::AirBalloon, Body::SailBoat];
make_case_elim!(
body,
@ -16,6 +16,7 @@ make_case_elim!(
pub enum Body {
DefaultAirship = 0,
AirBalloon = 1,
SailBoat = 2,
}
);
@ -35,6 +36,7 @@ impl Body {
match self {
Body::DefaultAirship => "airship_human.structure",
Body::AirBalloon => "air_balloon.structure",
Body::SailBoat => "sail_boat.structure",
}
}
@ -42,15 +44,21 @@ impl Body {
match self {
Body::DefaultAirship => Vec3::new(25.0, 50.0, 40.0),
Body::AirBalloon => Vec3::new(25.0, 50.0, 40.0),
Body::SailBoat => Vec3::new(13.0, 31.0, 3.0),
}
}
fn balloon_vol(&self) -> f32 {
let spheroid_vol = |equat_d: f32, polar_d: f32| -> f32 {
(std::f32::consts::PI / 6.0) * equat_d.powi(2) * polar_d
};
let dim = self.dimensions();
spheroid_vol(dim.z, dim.y)
match self {
Body::DefaultAirship | Body::AirBalloon => {
let spheroid_vol = |equat_d: f32, polar_d: f32| -> f32 {
(std::f32::consts::PI / 6.0) * equat_d.powi(2) * polar_d
};
let dim = self.dimensions();
spheroid_vol(dim.z, dim.y)
},
_ => 0.0,
}
}
fn hull_vol(&self) -> f32 {
@ -66,14 +74,19 @@ impl Body {
Density(ratio * oak_density + (1.0 - ratio) * AIR_DENSITY)
}
pub fn density(&self) -> Density { Density(AIR_DENSITY) }
pub fn density(&self) -> Density {
match self {
Body::DefaultAirship | Body::AirBalloon => Density(AIR_DENSITY),
_ => Density(AIR_DENSITY * 0.8 + WATER_DENSITY * 0.2), // Most boats should be buoyant
}
}
pub fn mass(&self) -> Mass { Mass((self.hull_vol() + self.balloon_vol()) * self.density().0) }
pub fn can_fly(&self) -> bool {
match self {
Body::DefaultAirship | Body::AirBalloon => true,
}
pub fn can_fly(&self) -> bool { matches!(self, Body::DefaultAirship | Body::AirBalloon) }
pub fn has_water_thrust(&self) -> bool {
!self.can_fly() // TODO: Differentiate this more carefully
}
}

View File

@ -173,6 +173,7 @@ impl Body {
quadruped_low::Species::Lavadrake => 1.7,
_ => 2.0,
},
Body::Ship(ship) if ship.has_water_thrust() => 0.1,
Body::Ship(_) => 0.035,
}
}
@ -180,7 +181,7 @@ impl Body {
/// Returns thrust force if the body type can swim, otherwise None
pub fn swim_thrust(&self) -> Option<f32> {
match self {
Body::Object(_) | Body::Ship(_) => None,
Body::Object(_) => None,
Body::BipedLarge(_) | Body::Golem(_) => Some(200.0 * self.mass().0),
Body::BipedSmall(_) => Some(100.0 * self.mass().0),
Body::BirdMedium(_) => Some(50.0 * self.mass().0),
@ -200,6 +201,8 @@ impl Body {
Body::QuadrupedLow(_) => Some(300.0 * self.mass().0),
Body::QuadrupedMedium(_) => Some(300.0 * self.mass().0),
Body::QuadrupedSmall(_) => Some(300.0 * self.mass().0),
Body::Ship(ship) if ship.has_water_thrust() => Some(1500.0 * self.mass().0),
Body::Ship(_) => None,
}
}

View File

@ -551,8 +551,11 @@ impl<'a> MapConfig<'a> {
downhill_wpos,
);
let (_t, _pt, dist) = if let Some((t, pt, dist)) =
quadratic_nearest_point(&coeffs, wposf)
{
quadratic_nearest_point(
&coeffs,
wposf,
Vec2::new(neighbor_wpos, downhill_wpos),
) {
(t, pt, dist)
} else {
let ndist = wposf.distance_squared(neighbor_wpos);

View File

@ -293,7 +293,53 @@ pub fn river_spline_coeffs(
pub fn quadratic_nearest_point(
spline: &Vec3<Vec2<f64>>,
point: Vec2<f64>,
_line: Vec2<Vec2<f64>>, // Used for alternative distance functions below
) -> Option<(f64, Vec2<f64>, f64)> {
//let eval_at = |t: f64| spline.x * t * t + spline.y * t + spline.z;
// Linear
// let line = LineSegment2 {
// start: line.x,
// end: line.y,
// };
// let len_sq = line.start.distance_squared(line.end);
// let t = ((point - line.start).dot(line.end - line.start) /
// len_sq).clamped(0.0, 1.0); let pos = line.start + (line.end - line.start)
// * t; return Some((t, pos, pos.distance_squared(point)));
// Quadratic
// let curve = QuadraticBezier2 {
// start: line.x,
// ctrl: eval_at(0.5),
// end: line.y,
// };
// let (t, pos) = curve.binary_search_point_by_steps(point, 16, 0.001);
// let t = t.clamped(0.0, 1.0);
// let pos = curve.evaluate(t);
// return Some((t, pos, pos.distance_squared(point)));
// Cubic
// let ctrl_at = |t: f64, end: f64| {
// let a = eval_at(end);
// let b = eval_at(Lerp::lerp(end, t, 0.1));
// let dir = (b - a).normalized();
// let exact = eval_at(t);
// a + dir * exact.distance(a)
// };
// let curve = CubicBezier2 {
// start: line.x,
// ctrl0: ctrl_at(0.33, 0.0),
// ctrl1: ctrl_at(0.66, 1.0),
// end: line.y,
// };
// let (t, pos) = curve.binary_search_point_by_steps(point, 12, 0.01);
// let t = t.clamped(0.0, 1.0);
// let pos = curve.evaluate(t);
// return Some((t, pos, pos.distance_squared(point)));
let a = spline.z.x;
let b = spline.y.x;
let c = spline.x.x;
@ -328,18 +374,14 @@ pub fn quadratic_nearest_point(
let min_root = roots
.iter()
.copied()
.filter_map(|root| {
.map(|root| {
let river_point = spline.x * root * root + spline.y * root + spline.z;
let river_zero = spline.z;
let river_one = spline.x + spline.y + spline.z;
if root > 0.0 && root < 1.0 {
Some((root, river_point))
} else if river_point.distance_squared(river_zero) < 0.5 {
Some((root, /*river_point*/ river_zero))
} else if river_point.distance_squared(river_one) < 0.5 {
Some((root, /*river_point*/ river_one))
(root, river_point)
} else {
None
let root = root.clamped(0.0, 1.0);
let river_point = spline.x * root * root + spline.y * root + spline.z;
(root, river_point)
}
})
.map(|(root, river_point)| {

View File

@ -31,7 +31,9 @@ impl Skeleton for ShipSkeleton {
buf: &mut [FigureBoneData; super::MAX_BONE_COUNT],
body: Self::Body,
) -> Offsets {
let bone0_mat = base_mat * Mat4::scaling_3d(1.0 / 11.0) * Mat4::<f32>::from(self.bone0);
let scale_mat = Mat4::scaling_3d(1.0 / 11.0);
let bone0_mat = base_mat * scale_mat * Mat4::<f32>::from(self.bone0);
*(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [
make_bone(bone0_mat),
@ -43,10 +45,12 @@ impl Skeleton for ShipSkeleton {
lantern: None,
// TODO: see quadruped_medium for how to animate this
mount_bone: Transform {
position: common::comp::Body::Ship(body)
.mountee_offset()
.into_tuple()
.into(),
position: (base_mat * scale_mat).mul_point(
common::comp::Body::Ship(body)
.mountee_offset()
.into_tuple()
.into(),
),
..Default::default()
},
}
@ -89,18 +93,22 @@ impl<'a> From<&'a Body> for SkeletonAttr {
bone0: match body {
DefaultAirship => (0.0, 0.0, 0.0),
AirBalloon => (0.0, 0.0, 0.0),
SailBoat => (0.0, 0.0, 0.0),
},
bone1: match body {
DefaultAirship => (-13.0, -25.0, 10.0),
AirBalloon => (0.0, 0.0, 0.0),
SailBoat => (0.0, 0.0, 0.0),
},
bone2: match body {
DefaultAirship => (13.0, -25.0, 10.0),
AirBalloon => (0.0, 0.0, 0.0),
SailBoat => (0.0, 0.0, 0.0),
},
bone3: match body {
DefaultAirship => (0.0, -27.5, 8.5),
AirBalloon => (0.0, -9.0, 8.0),
SailBoat => (0.0, 0.0, 0.0),
},
}
}

View File

@ -35,7 +35,7 @@ impl ForestKind {
ForestKind::Cedar => 0.275..1.45,
ForestKind::Pine => 0.2..1.4,
ForestKind::Birch => 0.0..0.6,
ForestKind::Mangrove => 0.65..1.3,
ForestKind::Mangrove => 0.5..1.3,
ForestKind::Swamp => 0.5..1.1,
_ => 0.0..0.0,
}
@ -51,7 +51,7 @@ impl ForestKind {
ForestKind::Cedar => -0.65..0.15,
ForestKind::Pine => -1.8..-0.2,
ForestKind::Birch => -0.7..0.25,
ForestKind::Mangrove => 0.4..1.6,
ForestKind::Mangrove => 0.35..1.6,
ForestKind::Swamp => -0.6..0.8,
_ => 0.0..0.0,
}

View File

@ -147,23 +147,27 @@ impl<'a> BlockGen<'a> {
} else {
Some(Block::new(BlockKind::Earth, col))
}
} else if (wposf.z as f32) < height {
} else if wposf.z as i32 <= height as i32 {
let grass_factor = (wposf.z as f32 - (height - grass_depth))
.div(grass_depth)
.sqrt();
let col = Lerp::lerp(sub_surface_color, surface_color, grass_factor);
// Surface
Some(Block::new(
if snow_cover {
Some(if water_level > height.ceil() {
Block::new(
BlockKind::Sand,
sub_surface_color.map(|e| (e * 255.0) as u8),
)
} else {
let col = Lerp::lerp(sub_surface_color, surface_color, grass_factor);
if grass_factor < 0.7 {
Block::new(BlockKind::Earth, col.map(|e| (e * 255.0) as u8))
} else if snow_cover {
//if temp < CONFIG.snow_temp + 0.031 {
BlockKind::Snow
} else if grass_factor > 0.7 {
BlockKind::Grass
Block::new(BlockKind::Snow, col.map(|e| (e * 255.0) as u8))
} else {
BlockKind::Earth
},
col.map(|e| (e * 255.0) as u8),
))
Block::new(BlockKind::Grass, col.map(|e| (e * 255.0) as u8))
}
})
} else {
None
}

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,12 @@
pub mod scatter;
pub mod shrub;
pub mod spot;
pub mod tree;
pub mod wildlife;
pub use self::{scatter::apply_scatter_to, spot::apply_spots_to, tree::apply_trees_to};
pub use self::{
scatter::apply_scatter_to, shrub::apply_shrubs_to, spot::apply_spots_to, tree::apply_trees_to,
};
use crate::{
column::ColumnSample,

View File

@ -9,7 +9,8 @@ fn close(x: f32, tgt: f32, falloff: f32) -> f32 {
(1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.125)
}
const MUSH_FACT: f32 = 1.0e-4; // To balance everything around the mushroom spawning rate
const MUSH_FACT: f32 = 1.0e-4; // To balance things around the mushroom spawning rate
const GRASS_FACT: f32 = 1.0e-3; // To balance things around the grass spawning rate
const DEPTH_WATER_NORM: f32 = 15.0; // Water depth at which regular underwater sprites start spawning
pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
use SpriteKind::*;
@ -18,132 +19,132 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
let scatter: &[(
_,
bool,
fn(&SimChunk, &ColumnSample) -> (f32, Option<(f32, f32)>),
fn(&SimChunk, &ColumnSample) -> (f32, Option<(f32, f32, f32)>),
)] = &[
// (density, Option<(wavelen, threshold)>)
// (density, Option<(base_density_proportion, wavelen, threshold)>)
// Flowers
(BlueFlower, false, |c, col| {
(BlueFlower, false, |_, col| {
(
close(c.temp, CONFIG.temperate_temp, 0.7).min(close(
c.humidity,
close(col.temp, CONFIG.temperate_temp, 0.7).min(close(
col.humidity,
CONFIG.jungle_hum,
0.4,
)) * col.tree_density
* MUSH_FACT
* 256.0,
Some((256.0, 0.25)),
Some((0.0, 256.0, 0.25)),
)
}),
(PinkFlower, false, |c, col| {
(PinkFlower, false, |_, col| {
(
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
close(col.temp, 0.0, 0.7).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
* col.tree_density
* MUSH_FACT
* 350.0,
Some((100.0, 0.1)),
Some((0.0, 100.0, 0.1)),
)
}),
(PurpleFlower, false, |c, col| {
(PurpleFlower, false, |_, col| {
(
close(c.temp, CONFIG.temperate_temp, 0.7).min(close(
c.humidity,
close(col.temp, CONFIG.temperate_temp, 0.7).min(close(
col.humidity,
CONFIG.jungle_hum,
0.4,
)) * col.tree_density
* MUSH_FACT
* 350.0,
Some((100.0, 0.1)),
Some((0.0, 100.0, 0.1)),
)
}),
(RedFlower, false, |c, col| {
(RedFlower, false, |_, col| {
(
close(c.temp, CONFIG.tropical_temp, 0.7).min(close(
c.humidity,
close(col.temp, CONFIG.tropical_temp, 0.7).min(close(
col.humidity,
CONFIG.jungle_hum,
0.4,
)) * col.tree_density
* MUSH_FACT
* 350.0,
Some((100.0, 0.1)),
Some((0.0, 100.0, 0.1)),
)
}),
(WhiteFlower, false, |c, col| {
(WhiteFlower, false, |_, col| {
(
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
close(col.temp, 0.0, 0.7).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
* col.tree_density
* MUSH_FACT
* 350.0,
Some((100.0, 0.1)),
Some((0.0, 100.0, 0.1)),
)
}),
(YellowFlower, false, |c, col| {
(YellowFlower, false, |_, col| {
(
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
close(col.temp, 0.0, 0.7).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
* col.tree_density
* MUSH_FACT
* 350.0,
Some((100.0, 0.1)),
Some((0.0, 100.0, 0.1)),
)
}),
(Cotton, false, |c, col| {
(Cotton, false, |_, col| {
(
close(c.temp, CONFIG.temperate_temp, 0.7).min(close(
c.humidity,
close(col.temp, CONFIG.temperate_temp, 0.7).min(close(
col.humidity,
CONFIG.jungle_hum,
0.4,
)) * col.tree_density
* MUSH_FACT
* 75.0,
Some((256.0, 0.25)),
Some((0.0, 256.0, 0.25)),
)
}),
(Sunflower, false, |c, col| {
(Sunflower, false, |_, col| {
(
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
close(col.temp, 0.0, 0.7).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
* col.tree_density
* MUSH_FACT
* 350.0,
Some((100.0, 0.15)),
Some((0.0, 100.0, 0.15)),
)
}),
(WildFlax, false, |c, col| {
(WildFlax, false, |_, col| {
(
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
close(col.temp, 0.0, 0.7).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
* col.tree_density
* MUSH_FACT
* 600.0,
Some((100.0, 0.15)),
Some((0.0, 100.0, 0.15)),
)
}),
// Herbs and Spices
(LingonBerry, false, |c, _| {
(LingonBerry, false, |_, col| {
(
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.jungle_hum, 0.5))
close(col.temp, 0.3, 0.4).min(close(col.humidity, CONFIG.jungle_hum, 0.5))
* MUSH_FACT
* 2.5,
None,
)
}),
(LeafyPlant, false, |c, _| {
(LeafyPlant, false, |_, col| {
(
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.jungle_hum, 0.3))
* MUSH_FACT
close(col.temp, 0.3, 0.4).min(close(col.humidity, CONFIG.jungle_hum, 0.3))
* GRASS_FACT
* 4.0,
None,
)
}),
(Fern, false, |c, _| {
(Fern, false, |_, col| {
(
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.forest_hum, 0.5))
* MUSH_FACT
close(col.temp, 0.3, 0.4).min(close(col.humidity, CONFIG.forest_hum, 0.5))
* GRASS_FACT
* 0.25,
Some((64.0, 0.2)),
Some((0.0, 64.0, 0.2)),
)
}),
(Blueberry, false, |c, _| {
(Blueberry, false, |_, col| {
(
close(c.temp, CONFIG.temperate_temp, 0.5).min(close(
c.humidity,
close(col.temp, CONFIG.temperate_temp, 0.5).min(close(
col.humidity,
CONFIG.forest_hum,
0.5,
)) * MUSH_FACT
@ -151,191 +152,200 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
None,
)
}),
(Pumpkin, false, |c, _| {
(Pumpkin, false, |_, col| {
(
close(c.temp, CONFIG.temperate_temp, 0.5).min(close(
c.humidity,
close(col.temp, CONFIG.temperate_temp, 0.5).min(close(
col.humidity,
CONFIG.forest_hum,
0.5,
)) * MUSH_FACT
* 500.0,
Some((512.0, 0.05)),
Some((0.0, 512.0, 0.05)),
)
}),
// Collectable Objects
// Only spawn twigs in temperate forests
(Twigs, false, |c, _| {
(Twigs, false, |_, col| {
(
(c.tree_density * 1.25 - 0.25).powf(0.5).max(0.0) * 0.75e-3,
(col.tree_density * 1.25 - 0.25).powf(0.5).max(0.0) * 0.75e-3,
None,
)
}),
(Stones, false, |c, _| {
((c.rockiness - 0.5).max(0.025) * 1.0e-3, None)
(Stones, false, |chunk, _| {
((chunk.rockiness - 0.5).max(0.025) * 1.0e-3, None)
}),
(Copper, false, |c, _| {
((c.rockiness - 0.5).max(0.0) * 1.5e-3, None)
(Copper, false, |chunk, _| {
((chunk.rockiness - 0.5).max(0.0) * 1.5e-3, None)
}),
(Tin, false, |c, _| {
((c.rockiness - 0.5).max(0.0) * 1.5e-3, None)
(Tin, false, |chunk, _| {
((chunk.rockiness - 0.5).max(0.0) * 1.5e-3, None)
}),
// Don't spawn Mushrooms in snowy regions
(Mushroom, false, |c, _| {
(Mushroom, false, |_, col| {
(
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.forest_hum, 0.35)) * MUSH_FACT,
close(col.temp, 0.3, 0.4).min(close(col.humidity, CONFIG.forest_hum, 0.35))
* MUSH_FACT,
None,
)
}),
// Grass
(ShortGrass, false, |c, _| {
(ShortGrass, false, |_, col| {
(
close(c.temp, 0.2, 0.65).min(close(c.humidity, CONFIG.jungle_hum, 0.4)) * 0.015,
None,
close(col.temp, 0.2, 0.75).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
* GRASS_FACT
* 150.0,
Some((0.3, 64.0, 0.3)),
)
}),
(MediumGrass, false, |c, _| {
(MediumGrass, false, |_, col| {
(
close(c.temp, 0.2, 0.6).min(close(c.humidity, CONFIG.jungle_hum, 0.4)) * 0.012,
None,
close(col.temp, 0.2, 0.6).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
* GRASS_FACT
* 120.0,
Some((0.3, 64.0, 0.3)),
)
}),
(LongGrass, false, |c, _| {
(LongGrass, false, |_, col| {
(
close(c.temp, 0.3, 0.35).min(close(c.humidity, CONFIG.jungle_hum, 0.3)) * 0.15,
Some((48.0, 0.2)),
close(col.temp, 0.3, 0.35).min(close(col.humidity, CONFIG.jungle_hum, 0.3))
* GRASS_FACT
* 150.0,
Some((0.1, 48.0, 0.3)),
)
}),
// Jungle Sprites
// (LongGrass, false, |c, col| {
// (
// close(c.temp, CONFIG.tropical_temp, 0.4).min(close(
// c.humidity,
// close(col.temp, CONFIG.tropical_temp, 0.4).min(close(
// col.humidity,
// CONFIG.jungle_hum,
// 0.6,
// )) * 0.08,
// Some((60.0, 5.0)),
// Some((0.0, 60.0, 5.0)),
// )
// }),
/*(WheatGreen, false, |c, col| {
(
close(c.temp, 0.4, 0.2).min(close(c.humidity, CONFIG.forest_hum, 0.1))
close(col.temp, 0.4, 0.2).min(close(col.humidity, CONFIG.forest_hum, 0.1))
* MUSH_FACT
* 0.001,
None,
)
}),*/
(GrassSnow, false, |c, _| {
(GrassSnow, false, |_, col| {
(
close(c.temp, CONFIG.snow_temp - 0.2, 0.4).min(close(
c.humidity,
close(col.temp, CONFIG.snow_temp - 0.2, 0.4).min(close(
col.humidity,
CONFIG.forest_hum,
0.5,
)) * 0.01,
Some((48.0, 0.2)),
)) * GRASS_FACT
* 100.0,
Some((0.0, 48.0, 0.2)),
)
}),
(Moonbell, false, |c, _| {
(Moonbell, false, |_, col| {
(
close(c.temp, CONFIG.snow_temp - 0.2, 0.4).min(close(
c.humidity,
close(col.temp, CONFIG.snow_temp - 0.2, 0.4).min(close(
col.humidity,
CONFIG.forest_hum,
0.5,
)) * 0.003,
Some((48.0, 0.2)),
Some((0.0, 48.0, 0.2)),
)
}),
// Savanna Plants
(SavannaGrass, false, |c, _| {
(SavannaGrass, false, |_, col| {
(
{
let savanna = close(c.temp, 1.0, 0.4).min(close(c.humidity, 0.2, 0.25));
let desert = close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1));
(savanna - desert * 5.0).max(0.0)
let savanna = close(col.temp, 1.0, 0.4) * close(col.humidity, 0.2, 0.25);
let desert = close(col.temp, 1.0, 0.25) * close(col.humidity, 0.0, 0.1);
(savanna - desert * 5.0).max(0.0) * GRASS_FACT * 250.0
},
Some((0.5, 0.2)),
Some((0.15, 64.0, 0.2)),
)
}),
(TallSavannaGrass, false, |c, _| {
(TallSavannaGrass, false, |_, col| {
(
{
let savanna = close(c.temp, 1.0, 0.4).min(close(c.humidity, 0.2, 0.25));
let desert = close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1));
(savanna - desert * 5.0).max(0.0)
let savanna = close(col.temp, 1.0, 0.4) * close(col.humidity, 0.2, 0.25);
let desert = close(col.temp, 1.0, 0.25) * close(col.humidity, 0.0, 0.1);
(savanna - desert * 5.0).max(0.0) * GRASS_FACT * 150.0
},
Some((12.5, 0.25)),
Some((0.1, 48.0, 0.2)),
)
}),
(RedSavannaGrass, false, |c, _| {
(RedSavannaGrass, false, |_, col| {
(
{
let savanna = close(c.temp, 1.0, 0.4).min(close(c.humidity, 0.2, 0.25));
let desert = close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1));
(savanna - desert * 5.0).max(0.0)
let savanna = close(col.temp, 1.0, 0.4) * close(col.humidity, 0.2, 0.25);
let desert = close(col.temp, 1.0, 0.25) * close(col.humidity, 0.0, 0.1);
(savanna - desert * 5.0).max(0.0) * GRASS_FACT * 120.0
},
Some((0.1, 0.1)),
Some((0.15, 48.0, 0.25)),
)
}),
(SavannaBush, false, |c, _| {
(SavannaBush, false, |_, col| {
(
{
let savanna = close(c.temp, 1.0, 0.4).min(close(c.humidity, 0.2, 0.25));
let desert = close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1));
(savanna - desert * 5.0).max(0.0)
let savanna = close(col.temp, 1.0, 0.4) * close(col.humidity, 0.2, 0.25);
let desert = close(col.temp, 1.0, 0.25) * close(col.humidity, 0.0, 0.1);
(savanna - desert * 5.0).max(0.0) * GRASS_FACT * 40.0
},
Some((0.08, 0.05)),
Some((0.1, 96.0, 0.15)),
)
}),
// Desert Plants
(DeadBush, false, |c, _| {
(DeadBush, false, |_, col| {
(
close(c.temp, 1.0, 0.95).min(close(c.humidity, 0.0, 0.3)) * MUSH_FACT * 7.5,
close(col.temp, 1.0, 0.95).min(close(col.humidity, 0.0, 0.3)) * MUSH_FACT * 7.5,
None,
)
}),
(Pyrebloom, false, |c, _| {
(Pyrebloom, false, |_, col| {
(
close(c.temp, 1.0, 0.95).min(close(c.humidity, 0.0, 0.3)) * MUSH_FACT * 0.35,
close(col.temp, 1.0, 0.95).min(close(col.humidity, 0.0, 0.3)) * MUSH_FACT * 0.35,
None,
)
}),
(LargeCactus, false, |c, _| {
(LargeCactus, false, |_, col| {
(
close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1)) * MUSH_FACT * 3.5,
close(col.temp, 1.0, 0.25).min(close(col.humidity, 0.0, 0.1)) * MUSH_FACT * 1.5,
None,
)
}),
(RoundCactus, false, |c, _| {
(RoundCactus, false, |_, col| {
(
close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1)) * MUSH_FACT * 5.5,
close(col.temp, 1.0, 0.25).min(close(col.humidity, 0.0, 0.1)) * MUSH_FACT * 2.5,
None,
)
}),
(ShortCactus, false, |c, _| {
(ShortCactus, false, |_, col| {
(
close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1)) * MUSH_FACT * 5.5,
close(col.temp, 1.0, 0.25).min(close(col.humidity, 0.0, 0.1)) * MUSH_FACT * 2.5,
None,
)
}),
(MedFlatCactus, false, |c, _| {
(MedFlatCactus, false, |_, col| {
(
close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1)) * MUSH_FACT * 5.5,
close(col.temp, 1.0, 0.25).min(close(col.humidity, 0.0, 0.1)) * MUSH_FACT * 2.5,
None,
)
}),
(ShortFlatCactus, false, |c, _| {
(ShortFlatCactus, false, |_, col| {
(
close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1)) * MUSH_FACT * 5.5,
close(col.temp, 1.0, 0.25).min(close(col.humidity, 0.0, 0.1)) * MUSH_FACT * 2.5,
None,
)
}),
(Reed, false, |c, col| {
(Reed, false, |_, col| {
(
close(c.humidity, CONFIG.jungle_hum, 0.7)
close(col.humidity, CONFIG.jungle_hum, 0.9)
* col
.water_dist
.map(|wd| Lerp::lerp(0.2, 0.0, (wd / 8.0).clamped(0.0, 1.0)))
.unwrap_or(0.0),
Some((128.0, 0.5)),
.unwrap_or(0.0)
* ((col.alt - CONFIG.sea_level) / 12.0).clamped(0.0, 1.0),
Some((0.2, 128.0, 0.5)),
)
}),
// Underwater chests
@ -374,23 +384,23 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
} else {
0.0
},
Some((100.0, 0.15)),
Some((0.0, 100.0, 0.15)),
)
}),
// seagrass
(Seagrass, true, |c, col| {
(Seagrass, true, |_, col| {
(
close(c.temp, CONFIG.temperate_temp, 0.8)
close(col.temp, CONFIG.temperate_temp, 0.8)
* MUSH_FACT
* 300.0
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM + 18.0
{
1.0
} else {
0.0
},
Some((150.0, 0.3)),
Some((0.0, 150.0, 0.3)),
)
}),
// seagrass, coastal patches
@ -398,44 +408,44 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
(
MUSH_FACT
* 600.0
* if col.water_level < CONFIG.sea_level && (col.water_level - col.alt) < 3.0 {
* if col.water_level <= CONFIG.sea_level && (col.water_level - col.alt) < 3.0 {
1.0
} else {
0.0
},
Some((150.0, 0.4)),
Some((0.0, 150.0, 0.4)),
)
}),
// scattered seaweed (temperate species)
(SeaweedTemperate, true, |c, col| {
(SeaweedTemperate, true, |_, col| {
(
close(c.temp, CONFIG.temperate_temp, 0.8)
close(col.temp, CONFIG.temperate_temp, 0.8)
* MUSH_FACT
* 50.0
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM + 11.0
{
1.0
} else {
0.0
},
Some((500.0, 0.75)),
Some((0.0, 500.0, 0.75)),
)
}),
// scattered seaweed (tropical species)
(SeaweedTropical, true, |c, col| {
(SeaweedTropical, true, |_, col| {
(
close(c.temp, 1.0, 0.95)
close(col.temp, 1.0, 0.95)
* MUSH_FACT
* 50.0
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM + 11.0
{
1.0
} else {
0.0
},
Some((500.0, 0.75)),
Some((0.0, 500.0, 0.75)),
)
}),
// Caulerpa lentillifera algae patch
@ -443,14 +453,14 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
(
MUSH_FACT
* 250.0
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM + 10.0
{
1.0
} else {
0.0
},
Some((100.0, 0.15)),
Some((0.0, 100.0, 0.15)),
)
}),
// Caulerpa prolifera algae patch
@ -458,110 +468,110 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
(
MUSH_FACT
* 250.0
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM + 10.0
{
1.0
} else {
0.0
},
Some((100.0, 0.15)),
Some((0.0, 100.0, 0.15)),
)
}),
// Mermaids' fan algae patch
(MermaidsFan, true, |c, col| {
(MermaidsFan, true, |_, col| {
(
close(c.temp, 1.0, 0.95)
close(col.temp, 1.0, 0.95)
* MUSH_FACT
* 500.0
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM + 10.0
{
1.0
} else {
0.0
},
Some((50.0, 0.10)),
Some((0.0, 50.0, 0.10)),
)
}),
// Sea anemones
(SeaAnemone, true, |c, col| {
(SeaAnemone, true, |_, col| {
(
close(c.temp, CONFIG.temperate_temp, 0.8)
close(col.temp, CONFIG.temperate_temp, 0.8)
* MUSH_FACT
* 125.0
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM - 9.0
{
1.0
} else {
0.0
},
Some((100.0, 0.3)),
Some((0.0, 100.0, 0.3)),
)
}),
// Giant Kelp
(GiantKelp, true, |c, col| {
(GiantKelp, true, |_, col| {
(
close(c.temp, CONFIG.temperate_temp, 0.8)
close(col.temp, CONFIG.temperate_temp, 0.8)
* MUSH_FACT
* 220.0
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM - 9.0
{
1.0
} else {
0.0
},
Some((200.0, 0.4)),
Some((0.0, 200.0, 0.4)),
)
}),
// Bull Kelp
(BullKelp, true, |c, col| {
(BullKelp, true, |_, col| {
(
close(c.temp, CONFIG.temperate_temp, 0.7)
close(col.temp, CONFIG.temperate_temp, 0.7)
* MUSH_FACT
* 300.0
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM + 3.0
{
1.0
} else {
0.0
},
Some((75.0, 0.3)),
Some((0.0, 75.0, 0.3)),
)
}),
// Stony Corals
(StonyCoral, true, |c, col| {
(StonyCoral, true, |_, col| {
(
close(c.temp, 1.0, 0.9)
close(col.temp, 1.0, 0.9)
* MUSH_FACT
* 160.0
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM + 10.0
{
1.0
} else {
0.0
},
Some((120.0, 0.4)),
Some((0.0, 120.0, 0.4)),
)
}),
// Soft Corals
(SoftCoral, true, |c, col| {
(SoftCoral, true, |_, col| {
(
close(c.temp, 1.0, 0.9)
close(col.temp, 1.0, 0.9)
* MUSH_FACT
* 120.0
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM + 10.0
{
1.0
} else {
0.0
},
Some((120.0, 0.4)),
Some((0.0, 120.0, 0.4)),
)
}),
// Seashells
@ -569,7 +579,7 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
(
(c.rockiness - 0.5).max(0.0)
* 1.0e-3
* if col.water_level < CONFIG.sea_level
* if col.water_level <= CONFIG.sea_level
&& col.alt < col.water_level - DEPTH_WATER_NORM + 20.0
{
1.0
@ -594,16 +604,16 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
];
canvas.foreach_col(|canvas, wpos2d, col| {
let underwater = col.water_level > col.alt;
let underwater = col.water_level.floor() > col.alt;
let kind = scatter
.iter()
.enumerate()
.find_map(|(i, (kind, is_underwater, f))| {
let (density, patch) = f(canvas.chunk(), col);
let is_patch = patch
.map(|(wavelen, threshold)| {
canvas
let density = patch
.map(|(base_density_prop, wavelen, threshold)| {
if canvas
.index()
.noise
.scatter_nz
@ -614,10 +624,14 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
)
.abs()
> 1.0 - threshold as f64
{
density
} else {
density * base_density_prop
}
})
.unwrap_or(true);
.unwrap_or(density);
if density > 0.0
&& is_patch
&& rng.gen::<f32>() < density //RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
&& underwater == *is_underwater
{

76
world/src/layer/shrub.rs Normal file
View File

@ -0,0 +1,76 @@
use crate::{
all::ForestKind,
util::{seed_expan, Sampler, StructureGen2d, UnitChooser},
Canvas,
};
use common::{
assets::AssetHandle,
terrain::structure::{Structure, StructuresGroup},
};
use hashbrown::HashMap;
use lazy_static::lazy_static;
use rand::prelude::*;
use rand_chacha::ChaChaRng;
use vek::*;
lazy_static! {
static ref JUNGLE_SHRUBS: AssetHandle<StructuresGroup> = Structure::load_group("shrubs.jungle");
}
struct Shrub {
wpos: Vec3<i32>,
seed: u32,
kind: ForestKind,
}
pub fn apply_shrubs_to(canvas: &mut Canvas, _dynamic_rng: &mut impl Rng) {
let mut shrub_cache = HashMap::new();
let shrub_gen = StructureGen2d::new(canvas.index().seed, 8, 4);
let info = canvas.info();
canvas.foreach_col(|_, wpos2d, _| {
for (wpos, seed) in std::array::IntoIter::new(shrub_gen.get(wpos2d)) {
shrub_cache.entry(wpos).or_insert_with(|| {
let col = info.col_or_gen(wpos)?;
let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
const BASE_SHRUB_DENSITY: f64 = 0.15;
if rng.gen_bool((BASE_SHRUB_DENSITY * col.tree_density as f64).clamped(0.0, 1.0))
&& col.water_dist.map_or(true, |d| d > 8.0)
&& col.alt > col.water_level
&& col.spawn_rate > 0.9
&& col.path.map_or(true, |(d, _, _, _)| d > 6.0)
{
Some(Shrub {
wpos: wpos.with_z(col.alt as i32),
seed,
kind: *info
.chunks()
.make_forest_lottery(wpos)
.choose_seeded(seed)
.as_ref()?,
})
} else {
None
}
});
}
});
for shrub in shrub_cache.values().filter_map(|s| s.as_ref()) {
let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(shrub.seed));
let units = UnitChooser::new(shrub.seed).get(shrub.seed).into();
let shrubs = match shrub.kind {
ForestKind::Mangrove => &JUNGLE_SHRUBS,
_ => continue, // TODO: Add more shrub varieties
}
.read();
let structure = shrubs.choose(&mut rng).unwrap();
canvas.blit_structure(shrub.wpos, structure, shrub.seed, units, true);
}
}

View File

@ -360,7 +360,7 @@ impl TreeConfig {
}
pub fn jungle(rng: &mut impl Rng, scale: f32) -> Self {
let scale = scale * (0.9 + rng.gen::<f32>().powi(4) * 1.0);
let scale = scale * (0.8 + rng.gen::<f32>() * 0.5);
let log_scale = 1.0 + scale.log2().max(0.0);
Self {
@ -369,14 +369,14 @@ impl TreeConfig {
branch_child_len: 0.35,
branch_child_radius: 0.5,
branch_child_radius_lerp: true,
leaf_radius: 4.0 * log_scale..4.5 * log_scale,
leaf_radius_scaled: 0.0,
leaf_radius: 10.0 * log_scale..11.5 * log_scale,
leaf_radius_scaled: -8.0 * log_scale,
straightness: 0.2,
max_depth: 2,
splits: 7.5..8.5,
split_range: 0.3..1.25,
split_range: 0.2..1.25,
branch_len_bias: 0.5,
leaf_vertical_scale: 0.4,
leaf_vertical_scale: 0.35,
proportionality: 0.8,
inhabited: false,
hanging_sprites: &[(0.00007, SpriteKind::Beehive), (0.015, SpriteKind::Liana)],
@ -467,16 +467,16 @@ impl TreeConfig {
trunk_len: 13.0 * scale,
trunk_radius: 1.65 * scale,
branch_child_len: 0.75,
branch_child_radius: 0.75,
branch_child_radius: 0.6,
branch_child_radius_lerp: true,
leaf_radius: 2.0 * log_scale..2.1 * log_scale,
leaf_radius: 1.5 * log_scale..2.0 * log_scale,
leaf_radius_scaled: 0.0,
straightness: 0.3,
max_depth: 5,
splits: 3.5..4.25,
split_range: 0.5..1.25,
branch_len_bias: 0.0,
leaf_vertical_scale: 0.5,
leaf_vertical_scale: 0.65,
proportionality: 0.5,
inhabited: false,
hanging_sprites: &[(0.00007, SpriteKind::Beehive)],

View File

@ -347,6 +347,7 @@ impl World {
};
layer::apply_caves_to(&mut canvas, &mut dynamic_rng);
layer::apply_shrubs_to(&mut canvas, &mut dynamic_rng);
layer::apply_trees_to(&mut canvas, &mut dynamic_rng);
layer::apply_scatter_to(&mut canvas, &mut dynamic_rng);
layer::apply_paths_to(&mut canvas);

View File

@ -1563,8 +1563,10 @@ impl WorldSim {
let dist = rpos.map(|e| e as f32).magnitude();
if let Some(c) = self.get_mut(cliff + rpos) {
let warp = 1.0 / (1.0 + dist);
c.tree_density *= 1.0 - warp;
c.cliff_height = Lerp::lerp(44.0, 0.0, -1.0 + dist / 3.5);
if !c.river.near_water() {
c.tree_density *= 1.0 - warp;
c.cliff_height = Lerp::lerp(44.0, 0.0, -1.0 + dist / 3.5);
}
}
});
}
@ -2054,47 +2056,45 @@ impl WorldSim {
self.get_nearest_way(wpos, |chunk| Some(chunk.cave))
}
/// Create a [`Lottery<Option<ForestKind>>`] that generates [`ForestKind`]s
/// according to the conditions at the given position. If no or fewer
/// trees are appropriate for the conditions, `None` may be generated.
pub fn make_forest_lottery(&self, wpos: Vec2<i32>) -> Lottery<Option<ForestKind>> {
let chunk = if let Some(chunk) = self.get_wpos(wpos) {
chunk
} else {
return Lottery::from(vec![(1.0, None)]);
};
let env = chunk.get_environment();
Lottery::from(
ForestKind::into_enum_iter()
.enumerate()
.map(|(i, fk)| {
const CLUSTER_SIZE: f64 = 48.0;
let nz = (FastNoise2d::new(i as u32 * 37)
.get(wpos.map(|e| e as f64) / CLUSTER_SIZE)
+ 1.0)
/ 2.0;
(fk.proclivity(&env) * nz, Some(fk))
})
.chain(std::iter::once((0.001, None)))
.collect::<Vec<_>>(),
)
}
/// Return an iterator over candidate tree positions (note that only some of
/// these will become trees since environmental parameters may forbid
/// them spawning).
pub fn get_near_trees(&self, wpos: Vec2<i32>) -> impl Iterator<Item = TreeAttr> + '_ {
// Deterministic based on wpos
let normal_trees = std::array::IntoIter::new(self.gen_ctx.structure_gen.get(wpos))
.filter_map(move |(pos, seed)| {
let chunk = self.get_wpos(pos)?;
let env = Environment {
humid: chunk.humidity,
temp: chunk.temp,
near_water: if chunk.river.is_lake()
|| chunk.river.near_river()
|| chunk.alt < CONFIG.sea_level + 6.0
// Close to sea in altitude
{
1.0
} else {
0.0
},
};
.filter_map(move |(wpos, seed)| {
let lottery = self.make_forest_lottery(wpos);
Some(TreeAttr {
pos,
pos: wpos,
seed,
scale: 1.0,
forest_kind: *Lottery::from(
ForestKind::into_enum_iter()
.enumerate()
.map(|(i, fk)| {
const CLUSTER_SIZE: f64 = 48.0;
let nz = (FastNoise2d::new(i as u32 * 37)
.get(pos.map(|e| e as f64) / CLUSTER_SIZE)
+ 1.0)
/ 2.0;
(fk.proclivity(&env) * nz, Some(fk))
})
.chain(std::iter::once((0.001, None)))
.collect::<Vec<_>>(),
)
.choose_seeded(seed)
.as_ref()?,
forest_kind: *lottery.choose_seeded(seed).as_ref()?,
inhabited: false,
})
});
@ -2219,7 +2219,8 @@ impl SimChunk {
} else {
Some(
uniform_idx_as_vec2(map_size_lg, downhill_pre as usize)
* TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
* TerrainChunkSize::RECT_SIZE.map(|e| e as i32)
+ TerrainChunkSize::RECT_SIZE.map(|e| e as i32 / 2),
)
};
@ -2295,10 +2296,10 @@ impl SimChunk {
.min(1.0);
// Add geologically short timescale undulation to the world for various reasons
let alt = alt
let alt =
// Don't add undulation to rivers, mainly because this could accidentally result in rivers flowing uphill
+ if river.near_water() {
0.0
if river.near_water() {
alt
} else {
// Sand dunes (formed over a short period of time, so we don't care about erosion sim)
let warp = Vec2::new(
@ -2323,7 +2324,16 @@ impl SimChunk {
const SOIL_SCALE: f32 = 16.0;
let soil = soil_nz * SOIL_SCALE * tree_density.sqrt() * humidity.sqrt();
dune + soil
let warp_factor = ((alt - CONFIG.sea_level) / 16.0).clamped(0.0, 1.0);
let warp = (dune + soil) * warp_factor;
// Prevent warping pushing the altitude underwater
if alt + warp < water_alt {
alt
} else {
alt + warp
}
};
Self {
@ -2415,4 +2425,20 @@ impl SimChunk {
}
pub fn near_cliffs(&self) -> bool { self.cliff_height > 0.0 }
pub fn get_environment(&self) -> Environment {
Environment {
humid: self.humidity,
temp: self.temp,
near_water: if self.river.is_lake()
|| self.river.near_river()
|| self.alt < CONFIG.sea_level + 6.0
// Close to sea in altitude
{
1.0
} else {
0.0
},
}
}
}