mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Experimental giant mother trees
This commit is contained in:
parent
ac5a60d260
commit
24c75f7cc3
@ -275,7 +275,7 @@ void main() {
|
||||
vec3(0, 0, -2)
|
||||
) + vec3(sin(lifetime), sin(lifetime + 0.7), sin(lifetime * 0.5)) * 2.0,
|
||||
vec3(4),
|
||||
vec4(vec3(0.2 + rand7 * 0.2, 0.2 + (0.5 + rand6 * 0.5) * 0.6, 0), 1),
|
||||
vec4(vec3(0.2 + rand7 * 0.2, 0.2 + (0.5 + rand6 * 0.5) * 0.6, 0) * (0.25 + rand1 * 0.5), 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5)
|
||||
);
|
||||
} else if (inst_mode == SNOW) {
|
||||
|
@ -196,8 +196,8 @@ impl Block {
|
||||
#[inline]
|
||||
pub fn get_max_sunlight(&self) -> Option<u8> {
|
||||
match self.kind() {
|
||||
BlockKind::Water => Some(2),
|
||||
BlockKind::Leaves => Some(4),
|
||||
BlockKind::Water => Some(3),
|
||||
BlockKind::Leaves => Some(6),
|
||||
_ if self.is_opaque() => Some(0),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -69,25 +69,23 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
||||
if is_sunlight {
|
||||
for x in 0..outer.size().w {
|
||||
for y in 0..outer.size().h {
|
||||
let z = outer.size().d - 1;
|
||||
let is_air = vol_cached
|
||||
.get(outer.min + Vec3::new(x, y, z))
|
||||
.ok()
|
||||
.map_or(false, |b| b.is_air());
|
||||
|
||||
light_map[lm_idx(x, y, z)] = if is_air {
|
||||
if vol_cached
|
||||
.get(outer.min + Vec3::new(x, y, z - 1))
|
||||
.ok()
|
||||
.map_or(false, |b| b.is_air())
|
||||
let mut light = SUNLIGHT;
|
||||
for z in (0..outer.size().d).rev() {
|
||||
match vol_cached
|
||||
.get(outer.min + Vec3::new(x, y, z))
|
||||
.map_or(None, |b| b.get_max_sunlight())
|
||||
{
|
||||
light_map[lm_idx(x, y, z - 1)] = SUNLIGHT;
|
||||
prop_que.push_back((x as u8, y as u8, z as u16));
|
||||
None => {},
|
||||
Some(0) => {
|
||||
light_map[lm_idx(x, y, z)] = 0;
|
||||
break;
|
||||
},
|
||||
Some(max_sunlight) => light = light.min(max_sunlight),
|
||||
}
|
||||
SUNLIGHT
|
||||
} else {
|
||||
OPAQUE
|
||||
};
|
||||
|
||||
light_map[lm_idx(x, y, z)] = light;
|
||||
prop_que.push_back((x as u8, y as u8, z as u16));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,7 +127,7 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
||||
let light = light_map[lm_idx(pos.x, pos.y, pos.z)];
|
||||
|
||||
// If ray propagate downwards at full strength
|
||||
if is_sunlight && light == SUNLIGHT {
|
||||
if is_sunlight && light == SUNLIGHT && false {
|
||||
// Down is special cased and we know up is a ray
|
||||
// Special cased ray propagation
|
||||
let pos = Vec3::new(pos.x, pos.y, pos.z - 1);
|
||||
@ -401,7 +399,9 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
|
||||
let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z);
|
||||
let draw_delta = Vec3::new(1, 1, z_start);
|
||||
|
||||
let get_light = |_: &mut (), pos: Vec3<i32>| light(pos + range.min);
|
||||
let get_light = |_: &mut (), pos: Vec3<i32>| volume
|
||||
.get(range.min + pos)
|
||||
.map_or(1.0, |b| if b.is_opaque() { 0.0 } else { light(pos + range.min) });
|
||||
let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min);
|
||||
let get_color =
|
||||
|_: &mut (), pos: Vec3<i32>| flat_get(pos).get_color().unwrap_or(Rgb::zero());
|
||||
|
@ -1015,7 +1015,7 @@ impl Renderer {
|
||||
/// NOTE: Apparently Macs aren't the only machines that lie.
|
||||
///
|
||||
/// TODO: Find a way to let graphics cards that don't lie do better.
|
||||
const MAX_TEXTURE_SIZE_MAX: u16 = 8192;
|
||||
const MAX_TEXTURE_SIZE_MAX: u16 = 8192 << 1;
|
||||
// NOTE: Many APIs for textures require coordinates to fit in u16, which is why
|
||||
// we perform this conversion.
|
||||
u16::try_from(factory.get_capabilities().max_texture_size)
|
||||
|
@ -14,6 +14,7 @@ common = { package = "veloren-common", path = "../common" }
|
||||
common-net = { package = "veloren-common-net", path = "../common/net" }
|
||||
bincode = "1.3.1"
|
||||
bitvec = "0.21.0"
|
||||
enum-iterator = "0.6"
|
||||
fxhash = "0.2.1"
|
||||
image = { version = "0.23.12", default-features = false, features = ["png"] }
|
||||
itertools = "0.10"
|
||||
|
@ -1,4 +1,9 @@
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
use std::ops::Range;
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use vek::*;
|
||||
use crate::util::math::close;
|
||||
|
||||
#[derive(Copy, Clone, Debug, IntoEnumIterator)]
|
||||
pub enum ForestKind {
|
||||
Palm,
|
||||
Acacia,
|
||||
@ -9,3 +14,73 @@ pub enum ForestKind {
|
||||
Mangrove,
|
||||
Swamp,
|
||||
}
|
||||
|
||||
pub struct Environment {
|
||||
pub humid: f32,
|
||||
pub temp: f32,
|
||||
pub near_water: f32,
|
||||
}
|
||||
|
||||
impl ForestKind {
|
||||
pub fn humid_range(&self) -> Range<f32> {
|
||||
match self {
|
||||
ForestKind::Palm => 0.25..1.4,
|
||||
ForestKind::Acacia => 0.1..0.6,
|
||||
ForestKind::Baobab => 0.2..0.6,
|
||||
ForestKind::Oak => 0.5..1.5,
|
||||
ForestKind::Pine => 0.2..1.4,
|
||||
ForestKind::Birch => 0.0..0.6,
|
||||
ForestKind::Mangrove => 0.7..1.3,
|
||||
ForestKind::Swamp => 0.5..1.1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn temp_range(&self) -> Range<f32> {
|
||||
match self {
|
||||
ForestKind::Palm => 0.4..1.6,
|
||||
ForestKind::Acacia => 0.4..1.6,
|
||||
ForestKind::Baobab => 0.4..0.9,
|
||||
ForestKind::Oak => -0.35..0.5,
|
||||
ForestKind::Pine => -1.8..-0.2,
|
||||
ForestKind::Birch => -0.7..0.25,
|
||||
ForestKind::Mangrove => 0.4..1.6,
|
||||
ForestKind::Swamp => -0.6..0.8,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn near_water_range(&self) -> Option<Range<f32>> {
|
||||
match self {
|
||||
ForestKind::Palm => Some(0.35..1.8),
|
||||
ForestKind::Swamp => Some(0.5..1.8),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The relative rate at which this tree appears under ideal conditions
|
||||
pub fn ideal_proclivity(&self) -> f32 {
|
||||
match self {
|
||||
ForestKind::Palm => 0.4,
|
||||
ForestKind::Acacia => 0.6,
|
||||
ForestKind::Baobab => 0.2,
|
||||
ForestKind::Oak => 1.0,
|
||||
ForestKind::Pine => 1.0,
|
||||
ForestKind::Birch => 0.65,
|
||||
ForestKind::Mangrove => 1.0,
|
||||
ForestKind::Swamp => 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn proclivity(&self, env: &Environment) -> f32 {
|
||||
self.ideal_proclivity()
|
||||
* close(env.humid, self.humid_range())
|
||||
* close(env.temp, self.temp_range())
|
||||
* self.near_water_range().map_or(1.0, |near_water_range| close(env.near_water, near_water_range))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TreeAttr {
|
||||
pub pos: Vec2<i32>,
|
||||
pub seed: u32,
|
||||
pub scale: f32,
|
||||
pub forest_kind: ForestKind,
|
||||
}
|
||||
|
@ -982,6 +982,13 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
.map(|wd| Lerp::lerp(sub_surface_color, ground, (wd / 3.0).clamped(0.0, 1.0)))
|
||||
.unwrap_or(ground);
|
||||
|
||||
// Ground under thick trees should be receive less sunlight and so often become dirt
|
||||
let ground = Lerp::lerp(
|
||||
ground,
|
||||
sub_surface_color,
|
||||
marble_mid * tree_density,
|
||||
);
|
||||
|
||||
let near_ocean = max_river.and_then(|(_, _, river_data, _)| {
|
||||
if (river_data.is_lake() || river_data.river_kind == Some(RiverKind::Ocean))
|
||||
&& alt <= water_level.max(CONFIG.sea_level + 5.0)
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
all::ForestKind,
|
||||
all::*,
|
||||
block::block_from_structure,
|
||||
column::ColumnGen,
|
||||
util::{RandomPerm, Sampler, UnitChooser},
|
||||
@ -60,9 +60,9 @@ pub fn apply_trees_to(canvas: &mut Canvas) {
|
||||
canvas.foreach_col(|canvas, wpos2d, col| {
|
||||
let trees = info.land().get_near_trees(wpos2d);
|
||||
|
||||
for (tree_wpos, seed) in trees {
|
||||
let tree = if let Some(tree) = tree_cache.entry(tree_wpos).or_insert_with(|| {
|
||||
let col = ColumnGen::new(info.land()).get((tree_wpos, info.index()))?;
|
||||
for TreeAttr { pos, seed, scale, forest_kind } in trees {
|
||||
let tree = if let Some(tree) = tree_cache.entry(pos).or_insert_with(|| {
|
||||
let col = ColumnGen::new(info.land()).get((pos, info.index()))?;
|
||||
|
||||
let is_quirky = QUIRKY_RAND.chance(seed, 1.0 / 500.0);
|
||||
|
||||
@ -82,7 +82,7 @@ pub fn apply_trees_to(canvas: &mut Canvas) {
|
||||
}
|
||||
|
||||
Some(Tree {
|
||||
pos: Vec3::new(tree_wpos.x, tree_wpos.y, col.alt as i32),
|
||||
pos: Vec3::new(pos.x, pos.y, col.alt as i32),
|
||||
model: 'model: {
|
||||
let models: AssetHandle<_> = if is_quirky {
|
||||
if col.temp > CONFIG.desert_temp {
|
||||
@ -91,7 +91,7 @@ pub fn apply_trees_to(canvas: &mut Canvas) {
|
||||
*QUIRKY
|
||||
}
|
||||
} else {
|
||||
match col.forest_kind {
|
||||
match forest_kind {
|
||||
ForestKind::Oak if QUIRKY_RAND.chance(seed + 1, 1.0 / 16.0) => {
|
||||
*OAK_STUMPS
|
||||
},
|
||||
@ -105,7 +105,7 @@ pub fn apply_trees_to(canvas: &mut Canvas) {
|
||||
ForestKind::Oak => {
|
||||
break 'model TreeModel::Procedural(
|
||||
ProceduralTree::generate(
|
||||
TreeConfig::oak(&mut RandomPerm::new(seed)),
|
||||
TreeConfig::oak(&mut RandomPerm::new(seed), scale),
|
||||
&mut RandomPerm::new(seed),
|
||||
),
|
||||
StructureBlock::TemperateLeaves,
|
||||
@ -115,7 +115,7 @@ pub fn apply_trees_to(canvas: &mut Canvas) {
|
||||
ForestKind::Pine => {
|
||||
break 'model TreeModel::Procedural(
|
||||
ProceduralTree::generate(
|
||||
TreeConfig::pine(&mut RandomPerm::new(seed)),
|
||||
TreeConfig::pine(&mut RandomPerm::new(seed), scale),
|
||||
&mut RandomPerm::new(seed),
|
||||
),
|
||||
StructureBlock::PineLeaves,
|
||||
@ -230,7 +230,7 @@ pub struct TreeConfig {
|
||||
/// Maximum number of branch layers (not including trunk).
|
||||
pub max_depth: usize,
|
||||
/// The number of branches that form from each branch.
|
||||
pub splits: usize,
|
||||
pub splits: Range<f32>,
|
||||
/// The range of proportions along a branch at which a split into another
|
||||
/// branch might occur. This value is clamped between 0 and 1, but a
|
||||
/// wider range may bias the results towards branch ends.
|
||||
@ -250,37 +250,39 @@ pub struct TreeConfig {
|
||||
}
|
||||
|
||||
impl TreeConfig {
|
||||
pub fn oak(rng: &mut impl Rng) -> Self {
|
||||
let scale = Lerp::lerp(0.8, 1.5, rng.gen::<f32>().powi(4));
|
||||
pub fn oak(rng: &mut impl Rng, scale: f32) -> Self {
|
||||
let scale = scale * (1.0 + rng.gen::<f32>().powi(4));
|
||||
let log_scale = 1.0 + scale.log2().max(0.0);
|
||||
|
||||
Self {
|
||||
trunk_len: 12.0 * scale,
|
||||
trunk_radius: 3.0 * scale,
|
||||
trunk_len: 9.0 * scale,
|
||||
trunk_radius: 2.0 * scale,
|
||||
branch_child_len: 0.8,
|
||||
branch_child_radius: 0.6,
|
||||
leaf_radius: 3.0 * scale..4.0 * scale,
|
||||
branch_child_radius: 0.75,
|
||||
leaf_radius: 2.5 * log_scale..3.25 * log_scale,
|
||||
straightness: 0.5,
|
||||
max_depth: 4,
|
||||
splits: 3,
|
||||
split_range: 0.5..1.5,
|
||||
max_depth: (4.0 + log_scale) as usize,
|
||||
splits: 2.0..3.0,
|
||||
split_range: 0.75..1.5,
|
||||
branch_len_bias: 0.0,
|
||||
leaf_vertical_scale: 1.0,
|
||||
proportionality: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pine(rng: &mut impl Rng) -> Self {
|
||||
let scale = Lerp::lerp(1.0, 2.0, rng.gen::<f32>().powi(4));
|
||||
pub fn pine(rng: &mut impl Rng, scale: f32) -> Self {
|
||||
let scale = scale * (1.0 + rng.gen::<f32>().powi(4));
|
||||
let log_scale = 1.0 + scale.log2().max(0.0);
|
||||
|
||||
Self {
|
||||
trunk_len: 32.0 * scale,
|
||||
trunk_radius: 1.5 * scale,
|
||||
branch_child_len: 0.3,
|
||||
trunk_radius: 1.25 * scale,
|
||||
branch_child_len: 0.3 / scale,
|
||||
branch_child_radius: 0.0,
|
||||
leaf_radius: 2.0 * scale..2.5 * scale,
|
||||
leaf_radius: 1.5 * log_scale..2.0 * log_scale,
|
||||
straightness: 0.0,
|
||||
max_depth: 1,
|
||||
splits: 56,
|
||||
splits: 50.0..70.0,
|
||||
split_range: 0.2..1.2,
|
||||
branch_len_bias: 0.75,
|
||||
leaf_vertical_scale: 0.3,
|
||||
@ -361,18 +363,19 @@ impl ProceduralTree {
|
||||
let y_axis = dir.cross(x_axis).normalized();
|
||||
let screw_shift = rng.gen_range(0.0..f32::consts::TAU);
|
||||
|
||||
for i in 0..config.splits {
|
||||
let splits = rng.gen_range(config.splits.clone()).round() as usize;
|
||||
for i in 0..splits {
|
||||
let dist = Lerp::lerp(
|
||||
i as f32 / (config.splits - 1) as f32,
|
||||
i as f32 / (splits - 1) as f32,
|
||||
rng.gen_range(0.0..1.0),
|
||||
config.proportionality,
|
||||
);
|
||||
|
||||
const PHI: f32 = 0.618;
|
||||
const RAD_PER_BRANCH: f32 = f32::consts::TAU * PHI;
|
||||
let screw = (screw_shift + dist * config.splits as f32 * RAD_PER_BRANCH).sin()
|
||||
let screw = (screw_shift + dist * splits as f32 * RAD_PER_BRANCH).sin()
|
||||
* x_axis
|
||||
+ (screw_shift + dist * config.splits as f32 * RAD_PER_BRANCH).cos() * y_axis;
|
||||
+ (screw_shift + dist * splits as f32 * RAD_PER_BRANCH).cos() * y_axis;
|
||||
|
||||
// Choose a point close to the branch to act as the target direction for the
|
||||
// branch to grow in let split_factor =
|
||||
|
@ -23,7 +23,7 @@ pub use self::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
all::ForestKind,
|
||||
all::*,
|
||||
block::BlockGen,
|
||||
civ::Place,
|
||||
column::ColumnGen,
|
||||
@ -45,6 +45,7 @@ use common::{
|
||||
vol::RectVolSize,
|
||||
};
|
||||
use common_net::msg::WorldMapMsg;
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use noise::{
|
||||
BasicMulti, Billow, Fbm, HybridMulti, MultiFractal, NoiseFn, RangeFunction, RidgedMulti,
|
||||
Seedable, SuperSimplex, Worley,
|
||||
@ -114,6 +115,7 @@ pub(crate) struct GenCtx {
|
||||
pub cave_1_nz: SuperSimplex,
|
||||
|
||||
pub structure_gen: StructureGen2d,
|
||||
pub big_structure_gen: StructureGen2d,
|
||||
pub region_gen: StructureGen2d,
|
||||
|
||||
pub fast_turb_x_nz: FastNoise,
|
||||
@ -516,7 +518,8 @@ impl WorldSim {
|
||||
cave_0_nz: SuperSimplex::new().set_seed(rng.gen()),
|
||||
cave_1_nz: SuperSimplex::new().set_seed(rng.gen()),
|
||||
|
||||
structure_gen: StructureGen2d::new(rng.gen(), 24, 8),
|
||||
structure_gen: StructureGen2d::new(rng.gen(), 24, 12),
|
||||
big_structure_gen: StructureGen2d::new(rng.gen(), 768, 512),
|
||||
region_gen: StructureGen2d::new(rng.gen(), 400, 96),
|
||||
humid_nz: Billow::new()
|
||||
.set_octaves(9)
|
||||
@ -1995,9 +1998,25 @@ impl WorldSim {
|
||||
/// 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 = (Vec2<i32>, u32)> + '_ {
|
||||
pub fn get_near_trees(&self, wpos: Vec2<i32>) -> impl Iterator<Item = TreeAttr> + '_ {
|
||||
// Deterministic based on wpos
|
||||
std::array::IntoIter::new(self.gen_ctx.structure_gen.get(wpos))
|
||||
let normal_trees = std::array::IntoIter::new(self.gen_ctx.structure_gen.get(wpos))
|
||||
.map(move |(pos, seed)| TreeAttr {
|
||||
pos,
|
||||
seed,
|
||||
scale: 1.0,
|
||||
forest_kind: self.get_wpos(pos).map_or(ForestKind::Oak, |c| c.forest_kind),
|
||||
});
|
||||
|
||||
let giant_trees = std::array::IntoIter::new(self.gen_ctx.big_structure_gen.get(wpos))
|
||||
.map(move |(pos, seed)| TreeAttr {
|
||||
pos,
|
||||
seed,
|
||||
scale: 10.0,
|
||||
forest_kind: ForestKind::Oak,
|
||||
});
|
||||
|
||||
normal_trees.chain(giant_trees)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2232,80 +2251,14 @@ impl SimChunk {
|
||||
},
|
||||
tree_density,
|
||||
forest_kind: {
|
||||
// Whittaker diagram
|
||||
let candidates = [
|
||||
// A smaller prevalence means that the range of values this tree appears in
|
||||
// will shrink compared to neighbouring trees in the
|
||||
// topology of the Whittaker diagram.
|
||||
// Humidity, temperature, near_water, each with prevalence
|
||||
(
|
||||
ForestKind::Palm,
|
||||
(CONFIG.desert_hum, 1.5),
|
||||
((CONFIG.tropical_temp + CONFIG.desert_temp) / 2.0, 1.25),
|
||||
(1.0, 2.0),
|
||||
),
|
||||
(
|
||||
ForestKind::Acacia,
|
||||
(0.0, 1.5),
|
||||
(CONFIG.tropical_temp, 1.5),
|
||||
(0.0, 1.0),
|
||||
),
|
||||
(
|
||||
ForestKind::Baobab,
|
||||
(0.0, 1.5),
|
||||
(CONFIG.tropical_temp, 1.5),
|
||||
(0.0, 1.0),
|
||||
),
|
||||
(
|
||||
ForestKind::Oak,
|
||||
(CONFIG.forest_hum, 1.5),
|
||||
(0.0, 1.5),
|
||||
(0.0, 1.0),
|
||||
),
|
||||
(
|
||||
ForestKind::Mangrove,
|
||||
(CONFIG.jungle_hum, 0.5),
|
||||
(CONFIG.tropical_temp, 0.5),
|
||||
(0.0, 1.0),
|
||||
),
|
||||
(
|
||||
ForestKind::Pine,
|
||||
(CONFIG.forest_hum, 1.25),
|
||||
(CONFIG.snow_temp, 2.5),
|
||||
(0.0, 1.0),
|
||||
),
|
||||
(
|
||||
ForestKind::Birch,
|
||||
(CONFIG.desert_hum, 1.5),
|
||||
(CONFIG.temperate_temp, 1.5),
|
||||
(0.0, 1.0),
|
||||
),
|
||||
(
|
||||
ForestKind::Swamp,
|
||||
((CONFIG.forest_hum + CONFIG.jungle_hum) / 2.0, 2.0),
|
||||
((CONFIG.temperate_temp + CONFIG.snow_temp) / 2.0, 2.0),
|
||||
(1.0, 2.5),
|
||||
),
|
||||
];
|
||||
let env = Environment {
|
||||
humid: humidity,
|
||||
temp,
|
||||
near_water: if river.is_lake() || river.near_river() { 1.0 } else { 0.0 },
|
||||
};
|
||||
|
||||
candidates
|
||||
.iter()
|
||||
.enumerate()
|
||||
.min_by_key(|(i, (_, (h, h_prev), (t, t_prev), (w, w_prev)))| {
|
||||
let rand = RandomPerm::new(*i as u32 * 1000);
|
||||
let noise =
|
||||
Vec3::iota().map(|e| (rand.get(e) & 0xFF) as f32 / 255.0 - 0.5) * 2.0;
|
||||
(Vec3::new(
|
||||
(*h - humidity) / *h_prev,
|
||||
(*t - temp) / *t_prev,
|
||||
(*w - if river.near_water() { 1.0 } else { 0.0 }) / *w_prev,
|
||||
)
|
||||
.add(noise * 0.1)
|
||||
.map(|e| e * e)
|
||||
.sum()
|
||||
* 10000.0) as i32
|
||||
})
|
||||
.map(|(_, c)| c.0)
|
||||
ForestKind::into_enum_iter()
|
||||
.max_by_key(|fk| (fk.proclivity(&env) * 10000.0) as u32)
|
||||
.unwrap() // Can't fail
|
||||
},
|
||||
spawn_rate: 1.0,
|
||||
|
11
world/src/util/math.rs
Normal file
11
world/src/util/math.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use std::ops::Range;
|
||||
use vek::*;
|
||||
|
||||
/// Return a value between 0 and 1 corresponding to how close to the centre of `range` `x` is.
|
||||
/// The exact function used is left unspecified, but it shall have the shape of a bell-like curve.
|
||||
/// This function is required to return `0` (or a value extremely close to `0`) when `x` is outside of `range`.
|
||||
pub fn close(x: f32, range: Range<f32>) -> f32 {
|
||||
let mean = (range.start + range.end) / 2.0;
|
||||
let width = (range.end - range.start) / 2.0;
|
||||
(1.0 - ((x - mean) / width).clamped(-1.0, 1.0).powi(2)).powi(2)
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
pub mod fast_noise;
|
||||
pub mod math;
|
||||
pub mod map_vec;
|
||||
pub mod random;
|
||||
pub mod sampler;
|
||||
|
Loading…
Reference in New Issue
Block a user