mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Turned tree generation into a post-processing layer, ripped out old tree generator for performance wins
This commit is contained in:
parent
647dcb2fec
commit
9b233708e2
@ -132,7 +132,7 @@ vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, const float time_of
|
|||||||
float ndist = step_to_dist(trunc(dist_to_step(cdist - 0.25)));
|
float ndist = step_to_dist(trunc(dist_to_step(cdist - 0.25)));
|
||||||
vec3 sample = cloud_at(origin + (dir + dir_diff / ndist) * ndist * splay, ndist);
|
vec3 sample = cloud_at(origin + (dir + dir_diff / ndist) * ndist * splay, ndist);
|
||||||
|
|
||||||
vec2 density_integrals = sample.yz * (cdist - ndist);
|
vec2 density_integrals = max(sample.yz, vec2(0)) * (cdist - ndist);
|
||||||
|
|
||||||
float sun_access = sample.x;
|
float sun_access = sample.x;
|
||||||
float scatter_factor = 1.0 - 1.0 / (1.0 + density_integrals.x);
|
float scatter_factor = 1.0 - 1.0 / (1.0 + density_integrals.x);
|
||||||
|
@ -281,7 +281,7 @@ impl Server {
|
|||||||
.expect(&format!("no z_cache found for chunk: {}", spawn_chunk));
|
.expect(&format!("no z_cache found for chunk: {}", spawn_chunk));
|
||||||
|
|
||||||
// get the minimum and maximum z values at which there could be solid blocks
|
// get the minimum and maximum z values at which there could be solid blocks
|
||||||
let (min_z, _, max_z) = z_cache.get_z_limits(&mut block_sampler, index);
|
let (min_z, max_z) = z_cache.get_z_limits();
|
||||||
// round range outwards, so no potential air block is missed
|
// round range outwards, so no potential air block is missed
|
||||||
let min_z = min_z.floor() as i32;
|
let min_z = min_z.floor() as i32;
|
||||||
let max_z = max_z.ceil() as i32;
|
let max_z = max_z.ceil() as i32;
|
||||||
@ -296,8 +296,6 @@ impl Server {
|
|||||||
.get_with_z_cache(
|
.get_with_z_cache(
|
||||||
Vec3::new(spawn_location.x, spawn_location.y, *z),
|
Vec3::new(spawn_location.x, spawn_location.y, *z),
|
||||||
Some(&z_cache),
|
Some(&z_cache),
|
||||||
false,
|
|
||||||
index,
|
|
||||||
)
|
)
|
||||||
.map(|b| b.is_air())
|
.map(|b| b.is_air())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
mod natural;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
column::{ColumnGen, ColumnSample},
|
column::{ColumnGen, ColumnSample},
|
||||||
util::{RandomField, Sampler, SmallCache},
|
util::{RandomField, Sampler, SmallCache},
|
||||||
IndexRef,
|
IndexRef,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::terrain::{
|
||||||
terrain::{
|
|
||||||
structure::{self, StructureBlock},
|
structure::{self, StructureBlock},
|
||||||
Block, BlockKind, SpriteKind, Structure,
|
Block, BlockKind, SpriteKind,
|
||||||
},
|
|
||||||
vol::ReadVol,
|
|
||||||
};
|
};
|
||||||
use core::ops::{Div, Mul, Range};
|
use core::ops::{Div, Mul, Range};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@ -18,7 +13,6 @@ use vek::*;
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Colors {
|
pub struct Colors {
|
||||||
pub pyramid: (u8, u8, u8),
|
|
||||||
// TODO(@Sharp): After the merge, construct enough infrastructure to make it convenient to
|
// TODO(@Sharp): After the merge, construct enough infrastructure to make it convenient to
|
||||||
// define mapping functions over the input; i.e. we should be able to interpret some fields as
|
// define mapping functions over the input; i.e. we should be able to interpret some fields as
|
||||||
// defining App<Abs<Fun, Type>, Arg>, where Fun : (Context, Arg) → (S, Type).
|
// defining App<Abs<Fun, Type>, Arg>, where Fun : (Context, Arg) → (S, Type).
|
||||||
@ -26,17 +20,11 @@ pub struct Colors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlockGen<'a> {
|
pub struct BlockGen<'a> {
|
||||||
pub column_cache: SmallCache<Option<ColumnSample<'a>>>,
|
|
||||||
pub column_gen: ColumnGen<'a>,
|
pub column_gen: ColumnGen<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BlockGen<'a> {
|
impl<'a> BlockGen<'a> {
|
||||||
pub fn new(column_gen: ColumnGen<'a>) -> Self {
|
pub fn new(column_gen: ColumnGen<'a>) -> Self { Self { column_gen } }
|
||||||
Self {
|
|
||||||
column_cache: SmallCache::default(),
|
|
||||||
column_gen,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sample_column<'b>(
|
pub fn sample_column<'b>(
|
||||||
column_gen: &ColumnGen<'a>,
|
column_gen: &ColumnGen<'a>,
|
||||||
@ -49,119 +37,17 @@ impl<'a> BlockGen<'a> {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cliff_height(
|
|
||||||
column_gen: &ColumnGen<'a>,
|
|
||||||
cache: &mut SmallCache<Option<ColumnSample<'a>>>,
|
|
||||||
wpos: Vec2<f32>,
|
|
||||||
close_cliffs: &[(Vec2<i32>, u32); 9],
|
|
||||||
cliff_hill: f32,
|
|
||||||
tolerance: f32,
|
|
||||||
index: IndexRef<'a>,
|
|
||||||
) -> f32 {
|
|
||||||
close_cliffs.iter().fold(
|
|
||||||
0.0f32,
|
|
||||||
|max_height, (cliff_pos, seed)| match Self::sample_column(
|
|
||||||
column_gen, cache, *cliff_pos, index,
|
|
||||||
) {
|
|
||||||
Some(cliff_sample) if cliff_sample.is_cliffs && cliff_sample.spawn_rate > 0.5 => {
|
|
||||||
let cliff_pos3d = Vec3::from(*cliff_pos);
|
|
||||||
|
|
||||||
// Conservative range of height: [15.70, 49.33]
|
|
||||||
let height = (RandomField::new(seed + 1).get(cliff_pos3d) % 64) as f32
|
|
||||||
// [0, 63] / (1 + 3 * [0.12, 1.32]) + 3 =
|
|
||||||
// [0, 63] / (1 + [0.36, 3.96]) + 3 =
|
|
||||||
// [0, 63] / [1.36, 4.96] + 3 =
|
|
||||||
// [0, 63] / [1.36, 4.96] + 3 =
|
|
||||||
// (height min) [0, 0] + 3 = [3, 3]
|
|
||||||
// (height max) [12.70, 46.33] + 3 = [15.70, 49.33]
|
|
||||||
/ (1.0 + 3.0 * cliff_sample.chaos)
|
|
||||||
+ 3.0;
|
|
||||||
// Conservative range of radius: [8, 47]
|
|
||||||
let radius = RandomField::new(seed + 2).get(cliff_pos3d) % 48 + 8;
|
|
||||||
|
|
||||||
if cliff_sample
|
|
||||||
.water_dist
|
|
||||||
.map(|d| d > radius as f32)
|
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
|
||||||
max_height.max(
|
|
||||||
if cliff_pos.map(|e| e as f32).distance_squared(wpos)
|
|
||||||
< (radius as f32 + tolerance).powf(2.0)
|
|
||||||
{
|
|
||||||
cliff_sample.alt + height * (1.0 - cliff_sample.chaos) + cliff_hill
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
max_height
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => max_height,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_z_cache(&mut self, wpos: Vec2<i32>, index: IndexRef<'a>) -> Option<ZCache<'a>> {
|
pub fn get_z_cache(&mut self, wpos: Vec2<i32>, index: IndexRef<'a>) -> Option<ZCache<'a>> {
|
||||||
let BlockGen {
|
let BlockGen { column_gen } = self;
|
||||||
column_cache,
|
|
||||||
column_gen,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
// Main sample
|
// Main sample
|
||||||
let sample = column_gen.get((wpos, index))?;
|
let sample = column_gen.get((wpos, index))?;
|
||||||
|
|
||||||
// Tree samples
|
Some(ZCache { sample })
|
||||||
let mut structures = [None, None, None, None, None, None, None, None, None];
|
|
||||||
sample
|
|
||||||
.close_structures
|
|
||||||
.iter()
|
|
||||||
.zip(structures.iter_mut())
|
|
||||||
.for_each(|(close_structure, structure)| {
|
|
||||||
if let Some(st) = *close_structure {
|
|
||||||
let st_sample = Self::sample_column(column_gen, column_cache, st.pos, index);
|
|
||||||
if let Some(st_sample) = st_sample {
|
|
||||||
let st_sample = st_sample.clone();
|
|
||||||
let st_info = match st.meta {
|
|
||||||
None => natural::structure_gen(
|
|
||||||
column_gen,
|
|
||||||
column_cache,
|
|
||||||
st.pos,
|
|
||||||
st.seed,
|
|
||||||
&st_sample,
|
|
||||||
index,
|
|
||||||
),
|
|
||||||
Some(meta) => Some(StructureInfo {
|
|
||||||
pos: Vec3::from(st.pos) + Vec3::unit_z() * st_sample.alt as i32,
|
|
||||||
seed: st.seed,
|
|
||||||
meta,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
if let Some(st_info) = st_info {
|
|
||||||
*structure = Some((st_info, st_sample));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Some(ZCache {
|
|
||||||
wpos,
|
|
||||||
sample,
|
|
||||||
structures,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_with_z_cache(
|
pub fn get_with_z_cache(&mut self, wpos: Vec3<i32>, z_cache: Option<&ZCache>) -> Option<Block> {
|
||||||
&mut self,
|
let BlockGen { column_gen } = self;
|
||||||
wpos: Vec3<i32>,
|
|
||||||
z_cache: Option<&ZCache>,
|
|
||||||
only_structures: bool,
|
|
||||||
index: IndexRef<'a>,
|
|
||||||
) -> Option<Block> {
|
|
||||||
let BlockGen {
|
|
||||||
column_cache,
|
|
||||||
column_gen,
|
|
||||||
} = self;
|
|
||||||
let world = column_gen.sim;
|
let world = column_gen.sim;
|
||||||
|
|
||||||
let z_cache = z_cache?;
|
let z_cache = z_cache?;
|
||||||
@ -180,24 +66,18 @@ impl<'a> BlockGen<'a> {
|
|||||||
// marble,
|
// marble,
|
||||||
// marble_small,
|
// marble_small,
|
||||||
rock,
|
rock,
|
||||||
//cliffs,
|
|
||||||
cliff_hill,
|
|
||||||
close_cliffs,
|
|
||||||
// temp,
|
// temp,
|
||||||
// humidity,
|
// humidity,
|
||||||
stone_col,
|
stone_col,
|
||||||
..
|
..
|
||||||
} = sample;
|
} = sample;
|
||||||
|
|
||||||
let structures = &z_cache.structures;
|
|
||||||
|
|
||||||
let wposf = wpos.map(|e| e as f64);
|
let wposf = wpos.map(|e| e as f64);
|
||||||
|
|
||||||
let (block, _height) = if !only_structures {
|
let (_definitely_underground, height, basement_height, water_height) =
|
||||||
let (_definitely_underground, height, _on_cliff, basement_height, water_height) =
|
|
||||||
if (wposf.z as f32) < alt - 64.0 * chaos {
|
if (wposf.z as f32) < alt - 64.0 * chaos {
|
||||||
// Shortcut warping
|
// Shortcut warping
|
||||||
(true, alt, false, basement, water_level)
|
(true, alt, basement, water_level)
|
||||||
} else {
|
} else {
|
||||||
// Apply warping
|
// Apply warping
|
||||||
let warp = world
|
let warp = world
|
||||||
@ -208,38 +88,11 @@ impl<'a> BlockGen<'a> {
|
|||||||
.mul(16.0);
|
.mul(16.0);
|
||||||
let warp = Lerp::lerp(0.0, warp, warp_factor);
|
let warp = Lerp::lerp(0.0, warp, warp_factor);
|
||||||
|
|
||||||
let surface_height = alt + warp;
|
let height = alt + warp;
|
||||||
|
|
||||||
let (height, on_cliff) = if (wposf.z as f32) < alt + warp - 10.0 {
|
|
||||||
// Shortcut cliffs
|
|
||||||
(surface_height, false)
|
|
||||||
} else {
|
|
||||||
let turb = Vec2::new(
|
|
||||||
world.gen_ctx.fast_turb_x_nz.get(wposf.div(25.0)) as f32,
|
|
||||||
world.gen_ctx.fast_turb_y_nz.get(wposf.div(25.0)) as f32,
|
|
||||||
) * 8.0;
|
|
||||||
|
|
||||||
let wpos_turb = Vec2::from(wpos).map(|e: i32| e as f32) + turb;
|
|
||||||
let cliff_height = Self::get_cliff_height(
|
|
||||||
column_gen,
|
|
||||||
column_cache,
|
|
||||||
wpos_turb,
|
|
||||||
&close_cliffs,
|
|
||||||
cliff_hill,
|
|
||||||
0.0,
|
|
||||||
index,
|
|
||||||
);
|
|
||||||
|
|
||||||
(
|
|
||||||
surface_height.max(cliff_height),
|
|
||||||
cliff_height > surface_height + 16.0,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
(
|
(
|
||||||
false,
|
false,
|
||||||
height,
|
height,
|
||||||
on_cliff,
|
|
||||||
basement + height - alt,
|
basement + height - alt,
|
||||||
(if water_level <= alt {
|
(if water_level <= alt {
|
||||||
water_level + warp
|
water_level + warp
|
||||||
@ -318,163 +171,29 @@ impl<'a> BlockGen<'a> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
(block, height)
|
|
||||||
} else {
|
|
||||||
(None, sample.alt)
|
|
||||||
};
|
|
||||||
|
|
||||||
let block = structures
|
|
||||||
.iter()
|
|
||||||
.find_map(|st| {
|
|
||||||
let (st, st_sample) = st.as_ref()?;
|
|
||||||
st.get(index, wpos, st_sample)
|
|
||||||
})
|
|
||||||
.or(block);
|
|
||||||
|
|
||||||
block
|
block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ZCache<'a> {
|
pub struct ZCache<'a> {
|
||||||
wpos: Vec2<i32>,
|
|
||||||
pub sample: ColumnSample<'a>,
|
pub sample: ColumnSample<'a>,
|
||||||
structures: [Option<(StructureInfo, ColumnSample<'a>)>; 9],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ZCache<'a> {
|
impl<'a> ZCache<'a> {
|
||||||
pub fn get_z_limits<'b>(
|
pub fn get_z_limits(&self) -> (f32, f32) {
|
||||||
&self,
|
|
||||||
block_gen: &mut BlockGen<'b>,
|
|
||||||
index: IndexRef<'b>,
|
|
||||||
) -> (f32, f32, f32) {
|
|
||||||
let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0);
|
let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0);
|
||||||
let min = min - 4.0;
|
let min = min - 4.0;
|
||||||
|
|
||||||
let cliff = BlockGen::get_cliff_height(
|
|
||||||
&block_gen.column_gen,
|
|
||||||
&mut block_gen.column_cache,
|
|
||||||
self.wpos.map(|e| e as f32),
|
|
||||||
&self.sample.close_cliffs,
|
|
||||||
self.sample.cliff_hill,
|
|
||||||
32.0,
|
|
||||||
index,
|
|
||||||
);
|
|
||||||
|
|
||||||
let rocks = if self.sample.rock > 0.0 { 12.0 } else { 0.0 };
|
let rocks = if self.sample.rock > 0.0 { 12.0 } else { 0.0 };
|
||||||
|
|
||||||
let warp = self.sample.chaos * 32.0;
|
let warp = self.sample.chaos * 32.0;
|
||||||
|
|
||||||
let (structure_min, structure_max) = self
|
let ground_max = self.sample.alt + warp + rocks + 2.0;
|
||||||
.structures
|
|
||||||
.iter()
|
let max = ground_max.max(self.sample.water_level + 2.0);
|
||||||
.filter_map(|st| st.as_ref())
|
|
||||||
.fold((0.0f32, 0.0f32), |(min, max), (st_info, _st_sample)| {
|
|
||||||
let bounds = st_info.get_bounds();
|
|
||||||
let st_area = Aabr {
|
|
||||||
min: Vec2::from(bounds.min),
|
|
||||||
max: Vec2::from(bounds.max),
|
|
||||||
};
|
|
||||||
|
|
||||||
if st_area.contains_point(self.wpos - st_info.pos) {
|
|
||||||
(min.min(bounds.min.z as f32), max.max(bounds.max.z as f32))
|
|
||||||
} else {
|
|
||||||
(min, max)
|
(min, max)
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
let ground_max = (self.sample.alt + warp + rocks).max(cliff) + 2.0;
|
|
||||||
|
|
||||||
let min = min + structure_min;
|
|
||||||
let max = (ground_max + structure_max).max(self.sample.water_level + 2.0);
|
|
||||||
|
|
||||||
let structures_only_min_z = ground_max.max(self.sample.water_level + 2.0);
|
|
||||||
|
|
||||||
(min, structures_only_min_z, max)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum StructureMeta {
|
|
||||||
Pyramid {
|
|
||||||
height: i32,
|
|
||||||
},
|
|
||||||
Volume {
|
|
||||||
units: (Vec2<i32>, Vec2<i32>),
|
|
||||||
volume: &'static Structure,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct StructureInfo {
|
|
||||||
pos: Vec3<i32>,
|
|
||||||
seed: u32,
|
|
||||||
meta: StructureMeta,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StructureInfo {
|
|
||||||
fn get_bounds(&self) -> Aabb<i32> {
|
|
||||||
match self.meta {
|
|
||||||
StructureMeta::Pyramid { height } => {
|
|
||||||
let base = 40;
|
|
||||||
Aabb {
|
|
||||||
min: Vec3::new(-base - height, -base - height, -base),
|
|
||||||
max: Vec3::new(base + height, base + height, height),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
StructureMeta::Volume { units, volume } => {
|
|
||||||
let bounds = volume.get_bounds();
|
|
||||||
|
|
||||||
(Aabb {
|
|
||||||
min: Vec3::from(units.0 * bounds.min.x + units.1 * bounds.min.y)
|
|
||||||
+ Vec3::unit_z() * bounds.min.z,
|
|
||||||
max: Vec3::from(units.0 * bounds.max.x + units.1 * bounds.max.y)
|
|
||||||
+ Vec3::unit_z() * bounds.max.z,
|
|
||||||
})
|
|
||||||
.made_valid()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&self, index: IndexRef, wpos: Vec3<i32>, sample: &ColumnSample) -> Option<Block> {
|
|
||||||
match self.meta {
|
|
||||||
StructureMeta::Pyramid { height } => {
|
|
||||||
if wpos.z - self.pos.z
|
|
||||||
< height
|
|
||||||
- Vec2::from(wpos - self.pos)
|
|
||||||
.map(|e: i32| (e.abs() / 2) * 2)
|
|
||||||
.reduce_max()
|
|
||||||
{
|
|
||||||
Some(Block::new(
|
|
||||||
BlockKind::Rock,
|
|
||||||
index.colors.block.pyramid.into(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
StructureMeta::Volume { units, volume } => {
|
|
||||||
let rpos = wpos - self.pos;
|
|
||||||
let block_pos = Vec3::unit_z() * rpos.z
|
|
||||||
+ Vec3::from(units.0) * rpos.x
|
|
||||||
+ Vec3::from(units.1) * rpos.y;
|
|
||||||
|
|
||||||
volume
|
|
||||||
.get((block_pos * 128) / 128) // Scaling
|
|
||||||
.ok()
|
|
||||||
.and_then(|b| {
|
|
||||||
block_from_structure(
|
|
||||||
index,
|
|
||||||
*b,
|
|
||||||
block_pos,
|
|
||||||
self.pos.into(),
|
|
||||||
self.seed,
|
|
||||||
sample,
|
|
||||||
// TODO: Take environment into account.
|
|
||||||
Block::air,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn block_from_structure(
|
pub fn block_from_structure(
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
use super::{BlockGen, StructureInfo, StructureMeta};
|
|
||||||
use crate::{
|
|
||||||
all::ForestKind,
|
|
||||||
column::{ColumnGen, ColumnSample},
|
|
||||||
util::{RandomPerm, Sampler, SmallCache, UnitChooser},
|
|
||||||
IndexRef, CONFIG,
|
|
||||||
};
|
|
||||||
use common::terrain::Structure;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use std::{sync::Arc, u32};
|
|
||||||
use vek::*;
|
|
||||||
|
|
||||||
static VOLUME_RAND: RandomPerm = RandomPerm::new(0xDB21C052);
|
|
||||||
static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x700F4EC7);
|
|
||||||
static QUIRKY_RAND: RandomPerm = RandomPerm::new(0xA634460F);
|
|
||||||
|
|
||||||
pub fn structure_gen<'a>(
|
|
||||||
column_gen: &ColumnGen<'a>,
|
|
||||||
column_cache: &mut SmallCache<Option<ColumnSample<'a>>>,
|
|
||||||
st_pos: Vec2<i32>,
|
|
||||||
st_seed: u32,
|
|
||||||
st_sample: &ColumnSample,
|
|
||||||
index: IndexRef<'a>,
|
|
||||||
) -> Option<StructureInfo> {
|
|
||||||
// Assuming it's a tree... figure out when it SHOULDN'T spawn
|
|
||||||
let random_seed = (st_seed as f64) / (u32::MAX as f64);
|
|
||||||
if (st_sample.tree_density as f64) < random_seed
|
|
||||||
|| st_sample.alt < st_sample.water_level
|
|
||||||
|| st_sample.spawn_rate < 0.5
|
|
||||||
|| st_sample.water_dist.map(|d| d < 8.0).unwrap_or(false)
|
|
||||||
|| st_sample.path.map(|(d, _, _, _)| d < 12.0).unwrap_or(false)
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cliff_height = BlockGen::get_cliff_height(
|
|
||||||
column_gen,
|
|
||||||
column_cache,
|
|
||||||
st_pos.map(|e| e as f32),
|
|
||||||
&st_sample.close_cliffs,
|
|
||||||
st_sample.cliff_hill,
|
|
||||||
0.0,
|
|
||||||
index,
|
|
||||||
);
|
|
||||||
|
|
||||||
let wheight = st_sample.alt.max(cliff_height);
|
|
||||||
let st_pos3d = Vec3::new(st_pos.x, st_pos.y, wheight as i32);
|
|
||||||
|
|
||||||
let volumes: &'static [_] = if QUIRKY_RAND.get(st_seed) % 512 == 17 {
|
|
||||||
if st_sample.temp > CONFIG.desert_temp {
|
|
||||||
&QUIRKY_DRY
|
|
||||||
} else {
|
|
||||||
&QUIRKY
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match st_sample.forest_kind {
|
|
||||||
ForestKind::Palm => &PALMS,
|
|
||||||
ForestKind::Savannah => &ACACIAS,
|
|
||||||
ForestKind::Oak if QUIRKY_RAND.get(st_seed) % 16 == 7 => &OAK_STUMPS,
|
|
||||||
ForestKind::Oak if QUIRKY_RAND.get(st_seed) % 19 == 7 => &FRUIT_TREES,
|
|
||||||
ForestKind::Oak if QUIRKY_RAND.get(st_seed) % 14 == 7 => &BIRCHES,
|
|
||||||
ForestKind::Oak => &OAKS,
|
|
||||||
ForestKind::Pine => &PINES,
|
|
||||||
ForestKind::SnowPine => &SNOW_PINES,
|
|
||||||
ForestKind::Mangrove => &MANGROVE_TREES,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(StructureInfo {
|
|
||||||
pos: st_pos3d,
|
|
||||||
seed: st_seed,
|
|
||||||
meta: StructureMeta::Volume {
|
|
||||||
units: UNIT_CHOOSER.get(st_seed),
|
|
||||||
volume: &volumes[(VOLUME_RAND.get(st_seed) / 13) as usize % volumes.len()],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref OAKS: Vec<Arc<Structure>> = Structure::load_group("oaks");
|
|
||||||
pub static ref OAK_STUMPS: Vec<Arc<Structure>> = Structure::load_group("oak_stumps");
|
|
||||||
pub static ref PINES: Vec<Arc<Structure>> = Structure::load_group("pines");
|
|
||||||
pub static ref PALMS: Vec<Arc<Structure>> = Structure::load_group("palms");
|
|
||||||
pub static ref SNOW_PINES: Vec<Arc<Structure>> = Structure::load_group("snow_pines");
|
|
||||||
pub static ref ACACIAS: Vec<Arc<Structure>> = Structure::load_group("acacias");
|
|
||||||
pub static ref FRUIT_TREES: Vec<Arc<Structure>> = Structure::load_group("fruit_trees");
|
|
||||||
pub static ref BIRCHES: Vec<Arc<Structure>> = Structure::load_group("birch");
|
|
||||||
pub static ref MANGROVE_TREES: Vec<Arc<Structure>> = Structure::load_group("mangrove_trees");
|
|
||||||
pub static ref QUIRKY: Vec<Arc<Structure>> = Structure::load_group("quirky");
|
|
||||||
pub static ref QUIRKY_DRY: Vec<Arc<Structure>> = Structure::load_group("quirky_dry");
|
|
||||||
}
|
|
@ -1,32 +1,39 @@
|
|||||||
use vek::*;
|
|
||||||
use common::{
|
|
||||||
terrain::{TerrainChunk, Block, TerrainChunkSize},
|
|
||||||
vol::{ReadVol, WriteVol, RectVolSize},
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::ZCache,
|
block::ZCache,
|
||||||
util::Grid,
|
|
||||||
column::ColumnSample,
|
column::ColumnSample,
|
||||||
index::IndexRef,
|
index::IndexRef,
|
||||||
|
sim::{SimChunk, WorldSim as Land},
|
||||||
|
util::Grid,
|
||||||
};
|
};
|
||||||
|
use common::{
|
||||||
|
terrain::{Block, TerrainChunk, TerrainChunkSize},
|
||||||
|
vol::{ReadVol, RectVolSize, WriteVol},
|
||||||
|
};
|
||||||
|
use std::ops::Deref;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct CanvasInfo<'a> {
|
pub struct CanvasInfo<'a> {
|
||||||
pub(crate) wpos: Vec2<i32>,
|
pub(crate) wpos: Vec2<i32>,
|
||||||
pub(crate) column_grid: &'a Grid<Option<ZCache<'a>>>,
|
pub(crate) column_grid: &'a Grid<Option<ZCache<'a>>>,
|
||||||
pub(crate) column_grid_border: i32,
|
pub(crate) column_grid_border: i32,
|
||||||
|
pub(crate) land: &'a Land,
|
||||||
pub(crate) index: IndexRef<'a>,
|
pub(crate) index: IndexRef<'a>,
|
||||||
|
pub(crate) chunk: &'a SimChunk,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CanvasInfo<'a> {
|
impl<'a> CanvasInfo<'a> {
|
||||||
pub fn wpos(&self) -> Vec2<i32> {
|
pub fn wpos(&self) -> Vec2<i32> { self.wpos }
|
||||||
self.wpos
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn area(&self) -> Aabr<i32> {
|
pub fn area(&self) -> Aabr<i32> {
|
||||||
Rect::from((self.wpos(), Extent2::from(TerrainChunkSize::RECT_SIZE.map(|e| e as i32)))).into()
|
Rect::from((
|
||||||
|
self.wpos(),
|
||||||
|
Extent2::from(TerrainChunkSize::RECT_SIZE.map(|e| e as i32)),
|
||||||
|
))
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn col(&self, pos: Vec2<i32>) -> Option<&ColumnSample> {
|
pub fn col(&self, pos: Vec2<i32>) -> Option<&'a ColumnSample> {
|
||||||
self.column_grid
|
self.column_grid
|
||||||
.get(self.column_grid_border + pos - self.wpos())
|
.get(self.column_grid_border + pos - self.wpos())
|
||||||
.map(Option::as_ref)
|
.map(Option::as_ref)
|
||||||
@ -34,24 +41,24 @@ impl<'a> CanvasInfo<'a> {
|
|||||||
.map(|zc| &zc.sample)
|
.map(|zc| &zc.sample)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index(&self) -> IndexRef {
|
pub fn index(&self) -> IndexRef<'a> { self.index }
|
||||||
self.index
|
|
||||||
}
|
pub fn chunk(&self) -> &'a SimChunk { self.chunk }
|
||||||
|
|
||||||
|
pub fn land(&self) -> &'a Land { self.land }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Canvas<'a> {
|
pub struct Canvas<'a> {
|
||||||
pub(crate) wpos: Vec2<i32>,
|
pub(crate) info: CanvasInfo<'a>,
|
||||||
pub(crate) chunk: &'a mut TerrainChunk,
|
pub(crate) chunk: &'a mut TerrainChunk,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Canvas<'a> {
|
impl<'a> Canvas<'a> {
|
||||||
pub fn wpos(&self) -> Vec2<i32> {
|
/// The borrow checker complains at immutable features of canvas (column
|
||||||
self.wpos
|
/// sampling, etc.) being used at the same time as mutable features
|
||||||
}
|
/// (writing blocks). To avoid this, this method extracts the
|
||||||
|
/// inner `CanvasInfo` such that it may be used independently.
|
||||||
pub fn area(&self) -> Aabr<i32> {
|
pub fn info(&mut self) -> CanvasInfo<'a> { self.info }
|
||||||
Rect::from((self.wpos(), Extent2::from(TerrainChunkSize::RECT_SIZE.map(|e| e as i32)))).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&mut self, pos: Vec3<i32>) -> Option<Block> {
|
pub fn get(&mut self, pos: Vec3<i32>) -> Option<Block> {
|
||||||
self.chunk.get(pos - self.wpos()).ok().copied()
|
self.chunk.get(pos - self.wpos()).ok().copied()
|
||||||
@ -60,4 +67,30 @@ impl<'a> Canvas<'a> {
|
|||||||
pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
|
pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
|
||||||
let _ = self.chunk.set(pos - self.wpos(), block);
|
let _ = self.chunk.set(pos - self.wpos(), block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn map(&mut self, pos: Vec3<i32>, f: impl FnOnce(Block) -> Block) {
|
||||||
|
let _ = self.chunk.map(pos - self.wpos(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute an operation upon each column in this canvas.
|
||||||
|
pub fn foreach_col(&mut self, mut f: impl FnMut(&mut Self, Vec2<i32>, &ColumnSample)) {
|
||||||
|
for y in 0..self.area().size().h as i32 {
|
||||||
|
for x in 0..self.area().size().w as i32 {
|
||||||
|
let wpos2d = self.wpos() + Vec2::new(x, y);
|
||||||
|
let info = self.info;
|
||||||
|
let col = if let Some(col) = info.col(wpos2d) {
|
||||||
|
col
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
f(self, wpos2d, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for Canvas<'a> {
|
||||||
|
type Target = CanvasInfo<'a>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.info }
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
all::ForestKind,
|
all::ForestKind,
|
||||||
block::StructureMeta,
|
|
||||||
sim::{local_cells, Cave, Path, RiverKind, SimChunk, WorldSim},
|
sim::{local_cells, Cave, Path, RiverKind, SimChunk, WorldSim},
|
||||||
util::Sampler,
|
util::Sampler,
|
||||||
IndexRef, CONFIG,
|
IndexRef, CONFIG,
|
||||||
@ -54,56 +53,6 @@ pub struct Colors {
|
|||||||
|
|
||||||
impl<'a> ColumnGen<'a> {
|
impl<'a> ColumnGen<'a> {
|
||||||
pub fn new(sim: &'a WorldSim) -> Self { Self { sim } }
|
pub fn new(sim: &'a WorldSim) -> Self { Self { sim } }
|
||||||
|
|
||||||
#[allow(clippy::if_same_then_else)] // TODO: Pending review in #587
|
|
||||||
fn get_local_structure(&self, wpos: Vec2<i32>) -> Option<StructureData> {
|
|
||||||
let (pos, seed) = self
|
|
||||||
.sim
|
|
||||||
.gen_ctx
|
|
||||||
.region_gen
|
|
||||||
.get(wpos)
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.min_by_key(|(pos, _)| pos.distance_squared(wpos))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let chunk_pos = pos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
|
|
||||||
let chunk = self.sim.get(chunk_pos)?;
|
|
||||||
|
|
||||||
if seed % 5 == 2
|
|
||||||
&& chunk.temp > CONFIG.desert_temp
|
|
||||||
&& chunk.alt > chunk.water_alt + 5.0
|
|
||||||
&& chunk.chaos <= 0.35
|
|
||||||
{
|
|
||||||
/*Some(StructureData {
|
|
||||||
pos,
|
|
||||||
seed,
|
|
||||||
meta: Some(StructureMeta::Pyramid { height: 140 }),
|
|
||||||
})*/
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_close_structures(&self, wpos: Vec2<i32>) -> [Option<StructureData>; 9] {
|
|
||||||
let mut metas = [None; 9];
|
|
||||||
self.sim
|
|
||||||
.gen_ctx
|
|
||||||
.structure_gen
|
|
||||||
.get(wpos)
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.enumerate()
|
|
||||||
.for_each(|(i, (pos, seed))| {
|
|
||||||
metas[i] = self.get_local_structure(pos).or(Some(StructureData {
|
|
||||||
pos,
|
|
||||||
seed,
|
|
||||||
meta: None,
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
metas
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Sampler<'a> for ColumnGen<'a> {
|
impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||||
@ -1053,7 +1002,6 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
0.0
|
0.0
|
||||||
},
|
},
|
||||||
forest_kind: sim_chunk.forest_kind,
|
forest_kind: sim_chunk.forest_kind,
|
||||||
close_structures: self.gen_close_structures(wpos),
|
|
||||||
marble,
|
marble,
|
||||||
marble_small,
|
marble_small,
|
||||||
rock,
|
rock,
|
||||||
@ -1086,7 +1034,6 @@ pub struct ColumnSample<'a> {
|
|||||||
pub sub_surface_color: Rgb<f32>,
|
pub sub_surface_color: Rgb<f32>,
|
||||||
pub tree_density: f32,
|
pub tree_density: f32,
|
||||||
pub forest_kind: ForestKind,
|
pub forest_kind: ForestKind,
|
||||||
pub close_structures: [Option<StructureData>; 9],
|
|
||||||
pub marble: f32,
|
pub marble: f32,
|
||||||
pub marble_small: f32,
|
pub marble_small: f32,
|
||||||
pub rock: f32,
|
pub rock: f32,
|
||||||
@ -1104,10 +1051,3 @@ pub struct ColumnSample<'a> {
|
|||||||
|
|
||||||
pub chunk: &'a SimChunk,
|
pub chunk: &'a SimChunk,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct StructureData {
|
|
||||||
pub pos: Vec2<i32>,
|
|
||||||
pub seed: u32,
|
|
||||||
pub meta: Option<StructureMeta>,
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
pub mod scatter;
|
pub mod scatter;
|
||||||
|
pub mod tree;
|
||||||
|
|
||||||
pub use self::scatter::apply_scatter_to;
|
pub use self::{scatter::apply_scatter_to, tree::apply_trees_to};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
column::ColumnSample,
|
column::ColumnSample,
|
||||||
util::{RandomField, Sampler},
|
util::{RandomField, Sampler},
|
||||||
IndexRef,
|
Canvas, IndexRef,
|
||||||
Canvas,
|
|
||||||
CanvasInfo,
|
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets::Asset,
|
assets::Asset,
|
||||||
@ -34,42 +33,28 @@ pub struct Colors {
|
|||||||
|
|
||||||
const EMPTY_AIR: Block = Block::air(SpriteKind::Empty);
|
const EMPTY_AIR: Block = Block::air(SpriteKind::Empty);
|
||||||
|
|
||||||
pub fn apply_paths_to<'a>(
|
pub fn apply_paths_to<'a>(canvas: &mut Canvas) {
|
||||||
canvas: &mut Canvas,
|
let info = canvas.info();
|
||||||
info: &CanvasInfo,
|
canvas.foreach_col(|canvas, wpos2d, col| {
|
||||||
) {
|
let surface_z = col.riverless_alt.floor() as i32;
|
||||||
for y in 0..canvas.area().size().h as i32 {
|
|
||||||
for x in 0..canvas.area().size().w as i32 {
|
|
||||||
let offs = Vec2::new(x, y);
|
|
||||||
|
|
||||||
let wpos2d = canvas.wpos() + offs;
|
let noisy_color = |color: Rgb<u8>, factor: u32| {
|
||||||
|
|
||||||
// Sample terrain
|
|
||||||
let col_sample = if let Some(col_sample) = info.col(wpos2d) {
|
|
||||||
col_sample
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let surface_z = col_sample.riverless_alt.floor() as i32;
|
|
||||||
|
|
||||||
let noisy_color = |col: Rgb<u8>, factor: u32| {
|
|
||||||
let nz = RandomField::new(0).get(Vec3::new(wpos2d.x, wpos2d.y, surface_z));
|
let nz = RandomField::new(0).get(Vec3::new(wpos2d.x, wpos2d.y, surface_z));
|
||||||
col.map(|e| {
|
color.map(|e| {
|
||||||
(e as u32 + nz % (factor * 2))
|
(e as u32 + nz % (factor * 2))
|
||||||
.saturating_sub(factor)
|
.saturating_sub(factor)
|
||||||
.min(255) as u8
|
.min(255) as u8
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((path_dist, path_nearest, path, _)) = col_sample
|
if let Some((path_dist, path_nearest, path, _)) =
|
||||||
.path
|
col.path.filter(|(dist, _, path, _)| *dist < path.width)
|
||||||
.filter(|(dist, _, path, _)| *dist < path.width)
|
|
||||||
{
|
{
|
||||||
let inset = 0;
|
let inset = 0;
|
||||||
|
|
||||||
// Try to use the column at the centre of the path for sampling to make them
|
// Try to use the column at the centre of the path for sampling to make them
|
||||||
// flatter
|
// flatter
|
||||||
let col_pos = (offs - wpos2d).map(|e| e as f32) + path_nearest;
|
let col_pos = -info.wpos().map(|e| e as f32) + path_nearest;
|
||||||
let col00 = info.col(info.wpos() + col_pos.map(|e| e.floor() as i32) + Vec2::new(0, 0));
|
let col00 = info.col(info.wpos() + col_pos.map(|e| e.floor() as i32) + Vec2::new(0, 0));
|
||||||
let col10 = info.col(info.wpos() + col_pos.map(|e| e.floor() as i32) + Vec2::new(1, 0));
|
let col10 = info.col(info.wpos() + col_pos.map(|e| e.floor() as i32) + Vec2::new(1, 0));
|
||||||
let col01 = info.col(info.wpos() + col_pos.map(|e| e.floor() as i32) + Vec2::new(0, 1));
|
let col01 = info.col(info.wpos() + col_pos.map(|e| e.floor() as i32) + Vec2::new(0, 1));
|
||||||
@ -83,7 +68,7 @@ pub fn apply_paths_to<'a>(
|
|||||||
Lerp::lerp(col_attr(col01), col_attr(col11), path_nearest.x.fract()),
|
Lerp::lerp(col_attr(col01), col_attr(col11), path_nearest.x.fract()),
|
||||||
path_nearest.y.fract(),
|
path_nearest.y.fract(),
|
||||||
),
|
),
|
||||||
_ => col_attr(col_sample),
|
_ => col_attr(col),
|
||||||
}
|
}
|
||||||
.into_array();
|
.into_array();
|
||||||
let (bridge_offset, depth) = (
|
let (bridge_offset, depth) = (
|
||||||
@ -104,9 +89,8 @@ pub fn apply_paths_to<'a>(
|
|||||||
noisy_color(info.index().colors.layer.bridge.into(), 8),
|
noisy_color(info.index().colors.layer.bridge.into(), 8),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let path_color = path.surface_color(
|
let path_color =
|
||||||
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
|
path.surface_color(col.sub_surface_color.map(|e| (e * 255.0) as u8));
|
||||||
);
|
|
||||||
Block::new(BlockKind::Earth, noisy_color(path_color, 8))
|
Block::new(BlockKind::Earth, noisy_color(path_color, 8))
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -119,33 +103,16 @@ pub fn apply_paths_to<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_caves_to<'a>(
|
pub fn apply_caves_to<'a>(canvas: &mut Canvas) {
|
||||||
wpos2d: Vec2<i32>,
|
let info = canvas.info();
|
||||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
canvas.foreach_col(|canvas, wpos2d, col| {
|
||||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
let surface_z = col.riverless_alt.floor() as i32;
|
||||||
index: IndexRef,
|
|
||||||
) {
|
|
||||||
for y in 0..vol.size_xy().y as i32 {
|
|
||||||
for x in 0..vol.size_xy().x as i32 {
|
|
||||||
let offs = Vec2::new(x, y);
|
|
||||||
|
|
||||||
let wpos2d = wpos2d + offs;
|
if let Some((cave_dist, _, cave, _)) =
|
||||||
|
col.cave.filter(|(dist, _, cave, _)| *dist < cave.width)
|
||||||
// Sample terrain
|
|
||||||
let col_sample = if let Some(col_sample) = get_column(offs) {
|
|
||||||
col_sample
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let surface_z = col_sample.riverless_alt.floor() as i32;
|
|
||||||
|
|
||||||
if let Some((cave_dist, _, cave, _)) = col_sample
|
|
||||||
.cave
|
|
||||||
.filter(|(dist, _, cave, _)| *dist < cave.width)
|
|
||||||
{
|
{
|
||||||
let cave_x = (cave_dist / cave.width).min(1.0);
|
let cave_x = (cave_dist / cave.width).min(1.0);
|
||||||
|
|
||||||
@ -159,53 +126,69 @@ pub fn apply_caves_to<'a>(
|
|||||||
|
|
||||||
for z in cave_base..cave_roof {
|
for z in cave_base..cave_roof {
|
||||||
if cave_x < 0.95
|
if cave_x < 0.95
|
||||||
|| index.noise.cave_nz.get(
|
|| info.index().noise.cave_nz.get(
|
||||||
Vec3::new(wpos2d.x, wpos2d.y, z)
|
Vec3::new(wpos2d.x, wpos2d.y, z)
|
||||||
.map(|e| e as f64 * 0.15)
|
.map(|e| e as f64 * 0.15)
|
||||||
.into_array(),
|
.into_array(),
|
||||||
) < 0.0
|
) < 0.0
|
||||||
{
|
{
|
||||||
let _ = vol.set(Vec3::new(offs.x, offs.y, z), EMPTY_AIR);
|
// If the block a little above is liquid, we should stop carving out the cave in
|
||||||
|
// order to leave a ceiling, and not floating water
|
||||||
|
if canvas
|
||||||
|
.get(Vec3::new(wpos2d.x, wpos2d.y, z + 2))
|
||||||
|
.map(|b| b.is_liquid())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.map(Vec3::new(wpos2d.x, wpos2d.y, z), |b| {
|
||||||
|
if b.is_liquid() { b } else { EMPTY_AIR }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stalagtites
|
// Stalagtites
|
||||||
let stalagtites = index
|
let stalagtites = info
|
||||||
|
.index()
|
||||||
.noise
|
.noise
|
||||||
.cave_nz
|
.cave_nz
|
||||||
.get(wpos2d.map(|e| e as f64 * 0.125).into_array())
|
.get(wpos2d.map(|e| e as f64 * 0.125).into_array())
|
||||||
.sub(0.5)
|
.sub(0.5)
|
||||||
.max(0.0)
|
.max(0.0)
|
||||||
.mul(
|
.mul(
|
||||||
(col_sample.alt - cave_roof as f32 - 5.0)
|
(col.alt - cave_roof as f32 - 5.0)
|
||||||
.mul(0.15)
|
.mul(0.15)
|
||||||
.clamped(0.0, 1.0) as f64,
|
.clamped(0.0, 1.0) as f64,
|
||||||
)
|
)
|
||||||
.mul(45.0) as i32;
|
.mul(45.0) as i32;
|
||||||
|
|
||||||
for z in cave_roof - stalagtites..cave_roof {
|
for z in cave_roof - stalagtites..cave_roof {
|
||||||
let _ = vol.set(
|
canvas.set(
|
||||||
Vec3::new(offs.x, offs.y, z),
|
Vec3::new(wpos2d.x, wpos2d.y, z),
|
||||||
Block::new(BlockKind::WeakRock, index.colors.layer.stalagtite.into()),
|
Block::new(
|
||||||
|
BlockKind::WeakRock,
|
||||||
|
info.index().colors.layer.stalagtite.into(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cave_depth = (col_sample.alt - cave.alt).max(0.0);
|
let cave_depth = (col.alt - cave.alt).max(0.0);
|
||||||
let difficulty = cave_depth / 100.0;
|
let difficulty = cave_depth / 100.0;
|
||||||
|
|
||||||
// Scatter things in caves
|
// Scatter things in caves
|
||||||
if RandomField::new(index.seed).chance(wpos2d.into(), 0.001 * difficulty.powf(1.5))
|
if RandomField::new(info.index().seed)
|
||||||
|
.chance(wpos2d.into(), 0.001 * difficulty.powf(1.5))
|
||||||
&& cave_base < surface_z as i32 - 25
|
&& cave_base < surface_z as i32 - 25
|
||||||
{
|
{
|
||||||
let kind = *Lottery::<SpriteKind>::load_expect("common.cave_scatter")
|
let kind = *Lottery::<SpriteKind>::load_expect("common.cave_scatter")
|
||||||
.choose_seeded(RandomField::new(index.seed + 1).get(wpos2d.into()));
|
.choose_seeded(RandomField::new(info.index().seed + 1).get(wpos2d.into()));
|
||||||
let _ = vol.map(Vec3::new(offs.x, offs.y, cave_base), |block| {
|
canvas.map(Vec3::new(wpos2d.x, wpos2d.y, cave_base), |block| {
|
||||||
block.with_sprite(kind)
|
block.with_sprite(kind)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#[allow(clippy::eval_order_dependence)]
|
#[allow(clippy::eval_order_dependence)]
|
||||||
pub fn apply_caves_supplement<'a>(
|
pub fn apply_caves_supplement<'a>(
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
use crate::{column::ColumnSample, sim::SimChunk, util::RandomField, IndexRef, CONFIG};
|
use crate::{column::ColumnSample, sim::SimChunk, util::RandomField, Canvas, CONFIG};
|
||||||
use common::{
|
use common::terrain::SpriteKind;
|
||||||
terrain::{Block, SpriteKind},
|
|
||||||
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
|
||||||
};
|
|
||||||
use noise::NoiseFn;
|
use noise::NoiseFn;
|
||||||
use std::f32;
|
use std::f32;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -11,13 +8,7 @@ fn close(x: f32, tgt: f32, falloff: f32) -> f32 {
|
|||||||
(1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.125)
|
(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 everything around the mushroom spawning rate
|
||||||
pub fn apply_scatter_to<'a>(
|
pub fn apply_scatter_to<'a>(canvas: &mut Canvas) {
|
||||||
wpos2d: Vec2<i32>,
|
|
||||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
|
||||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
|
||||||
index: IndexRef,
|
|
||||||
chunk: &SimChunk,
|
|
||||||
) {
|
|
||||||
use SpriteKind::*;
|
use SpriteKind::*;
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
// TODO: Add back all sprites we had before
|
// TODO: Add back all sprites we had before
|
||||||
@ -286,29 +277,18 @@ pub fn apply_scatter_to<'a>(
|
|||||||
(Chest, true, |_, _| (MUSH_FACT * 0.1, None)),
|
(Chest, true, |_, _| (MUSH_FACT * 0.1, None)),
|
||||||
];
|
];
|
||||||
|
|
||||||
for y in 0..vol.size_xy().y as i32 {
|
canvas.foreach_col(|canvas, wpos2d, col| {
|
||||||
for x in 0..vol.size_xy().x as i32 {
|
let underwater = col.water_level > col.alt;
|
||||||
let offs = Vec2::new(x, y);
|
|
||||||
|
|
||||||
let wpos2d = wpos2d + offs;
|
|
||||||
|
|
||||||
// Sample terrain
|
|
||||||
let col_sample = if let Some(col_sample) = get_column(offs) {
|
|
||||||
col_sample
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let underwater = col_sample.water_level > col_sample.alt;
|
|
||||||
|
|
||||||
let kind = scatter
|
let kind = scatter
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find_map(|(i, (kind, is_underwater, f))| {
|
.find_map(|(i, (kind, is_underwater, f))| {
|
||||||
let (density, patch) = f(chunk, col_sample);
|
let (density, patch) = f(canvas.chunk(), col);
|
||||||
let is_patch = patch
|
let is_patch = patch
|
||||||
.map(|(wavelen, threshold)| {
|
.map(|(wavelen, threshold)| {
|
||||||
index
|
canvas
|
||||||
|
.index()
|
||||||
.noise
|
.noise
|
||||||
.scatter_nz
|
.scatter_nz
|
||||||
.get(
|
.get(
|
||||||
@ -322,8 +302,7 @@ pub fn apply_scatter_to<'a>(
|
|||||||
.unwrap_or(true);
|
.unwrap_or(true);
|
||||||
if density > 0.0
|
if density > 0.0
|
||||||
&& is_patch
|
&& is_patch
|
||||||
&& RandomField::new(i as u32)
|
&& RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
|
||||||
.chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
|
|
||||||
&& underwater == *is_underwater
|
&& underwater == *is_underwater
|
||||||
{
|
{
|
||||||
Some(*kind)
|
Some(*kind)
|
||||||
@ -333,29 +312,30 @@ pub fn apply_scatter_to<'a>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Some(kind) = kind {
|
if let Some(kind) = kind {
|
||||||
let alt = col_sample.alt as i32;
|
let alt = col.alt as i32;
|
||||||
|
|
||||||
// Find the intersection between ground and air, if there is one near the
|
// Find the intersection between ground and air, if there is one near the
|
||||||
// surface
|
// surface
|
||||||
if let Some(solid_end) = (-4..8)
|
if let Some(solid_end) = (-4..8)
|
||||||
.find(|z| {
|
.find(|z| {
|
||||||
vol.get(Vec3::new(offs.x, offs.y, alt + z))
|
canvas
|
||||||
|
.get(Vec3::new(wpos2d.x, wpos2d.y, alt + z))
|
||||||
.map(|b| b.is_solid())
|
.map(|b| b.is_solid())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
})
|
})
|
||||||
.and_then(|solid_start| {
|
.and_then(|solid_start| {
|
||||||
(1..8).map(|z| solid_start + z).find(|z| {
|
(1..8).map(|z| solid_start + z).find(|z| {
|
||||||
vol.get(Vec3::new(offs.x, offs.y, alt + z))
|
canvas
|
||||||
|
.get(Vec3::new(wpos2d.x, wpos2d.y, alt + z))
|
||||||
.map(|b| !b.is_solid())
|
.map(|b| !b.is_solid())
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
let _ = vol.map(Vec3::new(offs.x, offs.y, alt + solid_end), |block| {
|
canvas.map(Vec3::new(wpos2d.x, wpos2d.y, alt + solid_end), |block| {
|
||||||
block.with_sprite(kind)
|
block.with_sprite(kind)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
127
world/src/layer/tree.rs
Normal file
127
world/src/layer/tree.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
use crate::{
|
||||||
|
all::ForestKind,
|
||||||
|
block::block_from_structure,
|
||||||
|
column::ColumnGen,
|
||||||
|
util::{RandomPerm, Sampler, UnitChooser},
|
||||||
|
Canvas, CONFIG,
|
||||||
|
};
|
||||||
|
use common::{
|
||||||
|
terrain::{
|
||||||
|
structure::{Structure, StructureBlock},
|
||||||
|
Block,
|
||||||
|
},
|
||||||
|
vol::ReadVol,
|
||||||
|
};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use std::{collections::HashMap, f32, sync::Arc};
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref OAKS: Vec<Arc<Structure>> = Structure::load_group("oaks");
|
||||||
|
pub static ref OAK_STUMPS: Vec<Arc<Structure>> = Structure::load_group("oak_stumps");
|
||||||
|
pub static ref PINES: Vec<Arc<Structure>> = Structure::load_group("pines");
|
||||||
|
pub static ref PALMS: Vec<Arc<Structure>> = Structure::load_group("palms");
|
||||||
|
pub static ref SNOW_PINES: Vec<Arc<Structure>> = Structure::load_group("snow_pines");
|
||||||
|
pub static ref ACACIAS: Vec<Arc<Structure>> = Structure::load_group("acacias");
|
||||||
|
pub static ref FRUIT_TREES: Vec<Arc<Structure>> = Structure::load_group("fruit_trees");
|
||||||
|
pub static ref BIRCHES: Vec<Arc<Structure>> = Structure::load_group("birch");
|
||||||
|
pub static ref MANGROVE_TREES: Vec<Arc<Structure>> = Structure::load_group("mangrove_trees");
|
||||||
|
pub static ref QUIRKY: Vec<Arc<Structure>> = Structure::load_group("quirky");
|
||||||
|
pub static ref QUIRKY_DRY: Vec<Arc<Structure>> = Structure::load_group("quirky_dry");
|
||||||
|
}
|
||||||
|
|
||||||
|
static MODEL_RAND: RandomPerm = RandomPerm::new(0xDB21C052);
|
||||||
|
static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x700F4EC7);
|
||||||
|
static QUIRKY_RAND: RandomPerm = RandomPerm::new(0xA634460F);
|
||||||
|
|
||||||
|
pub fn apply_trees_to<'a>(canvas: &mut Canvas) {
|
||||||
|
struct Tree {
|
||||||
|
pos: Vec3<i32>,
|
||||||
|
model: Arc<Structure>,
|
||||||
|
seed: u32,
|
||||||
|
units: (Vec2<i32>, Vec2<i32>),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tree_cache = HashMap::new();
|
||||||
|
|
||||||
|
let info = canvas.info();
|
||||||
|
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()))?;
|
||||||
|
|
||||||
|
// Ensure that it's valid to place a tree here
|
||||||
|
if ((seed.wrapping_mul(13)) & 0xFF) as f32 / 256.0 > col.tree_density
|
||||||
|
|| col.alt < col.water_level
|
||||||
|
|| col.spawn_rate < 0.5
|
||||||
|
|| col.water_dist.map(|d| d < 8.0).unwrap_or(false)
|
||||||
|
|| col.path.map(|(d, _, _, _)| d < 12.0).unwrap_or(false)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Tree {
|
||||||
|
pos: Vec3::new(tree_wpos.x, tree_wpos.y, col.alt as i32),
|
||||||
|
model: {
|
||||||
|
let models: &'static [_] = if QUIRKY_RAND.get(seed) % 512 == 17 {
|
||||||
|
if col.temp > CONFIG.desert_temp {
|
||||||
|
&QUIRKY_DRY
|
||||||
|
} else {
|
||||||
|
&QUIRKY
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match col.forest_kind {
|
||||||
|
ForestKind::Palm => &PALMS,
|
||||||
|
ForestKind::Savannah => &ACACIAS,
|
||||||
|
ForestKind::Oak if QUIRKY_RAND.get(seed) % 16 == 7 => &OAK_STUMPS,
|
||||||
|
ForestKind::Oak if QUIRKY_RAND.get(seed) % 19 == 7 => &FRUIT_TREES,
|
||||||
|
ForestKind::Oak if QUIRKY_RAND.get(seed) % 14 == 7 => &BIRCHES,
|
||||||
|
ForestKind::Oak => &OAKS,
|
||||||
|
ForestKind::Pine => &PINES,
|
||||||
|
ForestKind::SnowPine => &SNOW_PINES,
|
||||||
|
ForestKind::Mangrove => &MANGROVE_TREES,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
models[(MODEL_RAND.get(seed.wrapping_mul(17)) / 13) as usize % models.len()]
|
||||||
|
.clone()
|
||||||
|
},
|
||||||
|
seed,
|
||||||
|
units: UNIT_CHOOSER.get(seed),
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
tree
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let bounds = tree.model.get_bounds();
|
||||||
|
for z in bounds.min.z..bounds.max.z {
|
||||||
|
let wpos = Vec3::new(wpos2d.x, wpos2d.y, tree.pos.z + z);
|
||||||
|
let model_pos = Vec3::from(
|
||||||
|
(wpos - tree.pos)
|
||||||
|
.xy()
|
||||||
|
.map2(Vec2::new(tree.units.0, tree.units.1), |rpos, unit| {
|
||||||
|
unit * rpos
|
||||||
|
})
|
||||||
|
.sum(),
|
||||||
|
) + Vec3::unit_z() * (wpos.z - tree.pos.z);
|
||||||
|
block_from_structure(
|
||||||
|
info.index(),
|
||||||
|
tree.model
|
||||||
|
.get(model_pos)
|
||||||
|
.ok()
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(StructureBlock::None),
|
||||||
|
wpos,
|
||||||
|
tree.pos.xy(),
|
||||||
|
tree.seed,
|
||||||
|
col,
|
||||||
|
Block::air,
|
||||||
|
)
|
||||||
|
.map(|block| canvas.set(wpos, block));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@ -8,7 +8,8 @@
|
|||||||
const_generics,
|
const_generics,
|
||||||
const_panic,
|
const_panic,
|
||||||
label_break_value,
|
label_break_value,
|
||||||
or_patterns
|
or_patterns,
|
||||||
|
array_value_iter
|
||||||
)]
|
)]
|
||||||
|
|
||||||
mod all;
|
mod all;
|
||||||
@ -27,8 +28,8 @@ pub mod util;
|
|||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
config::CONFIG,
|
|
||||||
canvas::{Canvas, CanvasInfo},
|
canvas::{Canvas, CanvasInfo},
|
||||||
|
config::CONFIG,
|
||||||
};
|
};
|
||||||
pub use block::BlockGen;
|
pub use block::BlockGen;
|
||||||
pub use column::ColumnSample;
|
pub use column::ColumnSample;
|
||||||
@ -171,8 +172,7 @@ impl World {
|
|||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (min_z, only_structures_min_z, max_z) =
|
let (min_z, max_z) = z_cache.get_z_limits();
|
||||||
z_cache.get_z_limits(&mut sampler, index);
|
|
||||||
|
|
||||||
(base_z..min_z as i32).for_each(|z| {
|
(base_z..min_z as i32).for_each(|z| {
|
||||||
let _ = chunk.set(Vec3::new(x, y, z), stone);
|
let _ = chunk.set(Vec3::new(x, y, z), stone);
|
||||||
@ -181,11 +181,8 @@ impl World {
|
|||||||
(min_z as i32..max_z as i32).for_each(|z| {
|
(min_z as i32..max_z as i32).for_each(|z| {
|
||||||
let lpos = Vec3::new(x, y, z);
|
let lpos = Vec3::new(x, y, z);
|
||||||
let wpos = Vec3::from(chunk_wpos2d) + lpos;
|
let wpos = Vec3::from(chunk_wpos2d) + lpos;
|
||||||
let only_structures = lpos.z >= only_structures_min_z as i32;
|
|
||||||
|
|
||||||
if let Some(block) =
|
if let Some(block) = sampler.get_with_z_cache(wpos, Some(&z_cache)) {
|
||||||
sampler.get_with_z_cache(wpos, Some(&z_cache), only_structures, index)
|
|
||||||
{
|
|
||||||
let _ = chunk.set(lpos, block);
|
let _ = chunk.set(lpos, block);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -204,21 +201,22 @@ impl World {
|
|||||||
let mut dynamic_rng = rand::thread_rng();
|
let mut dynamic_rng = rand::thread_rng();
|
||||||
|
|
||||||
// Apply layers (paths, caves, etc.)
|
// Apply layers (paths, caves, etc.)
|
||||||
layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk, index);
|
let mut canvas = Canvas {
|
||||||
layer::apply_scatter_to(chunk_wpos2d, sample_get, &mut chunk, index, sim_chunk);
|
info: CanvasInfo {
|
||||||
|
|
||||||
let canvas_info = CanvasInfo {
|
|
||||||
wpos: chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
wpos: chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||||
column_grid: &zcache_grid,
|
column_grid: &zcache_grid,
|
||||||
column_grid_border: grid_border,
|
column_grid_border: grid_border,
|
||||||
|
land: &self.sim,
|
||||||
index,
|
index,
|
||||||
};
|
chunk: sim_chunk,
|
||||||
let mut canvas = Canvas {
|
},
|
||||||
wpos: chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
|
||||||
chunk: &mut chunk,
|
chunk: &mut chunk,
|
||||||
};
|
};
|
||||||
|
|
||||||
layer::apply_paths_to(&mut canvas, &canvas_info);
|
layer::apply_trees_to(&mut canvas);
|
||||||
|
layer::apply_scatter_to(&mut canvas);
|
||||||
|
layer::apply_caves_to(&mut canvas);
|
||||||
|
layer::apply_paths_to(&mut canvas);
|
||||||
|
|
||||||
// Apply site generation
|
// Apply site generation
|
||||||
sim_chunk.sites.iter().for_each(|site| {
|
sim_chunk.sites.iter().for_each(|site| {
|
||||||
|
@ -1431,25 +1431,11 @@ impl WorldSim {
|
|||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map_init(
|
.map_init(
|
||||||
|| Box::new(BlockGen::new(ColumnGen::new(self))),
|
|| Box::new(BlockGen::new(ColumnGen::new(self))),
|
||||||
|block_gen, posi| {
|
|_block_gen, posi| {
|
||||||
let wpos = uniform_idx_as_vec2(self.map_size_lg(), posi);
|
let sample = column_sample.get(
|
||||||
let mut sample = column_sample.get(
|
|
||||||
(uniform_idx_as_vec2(self.map_size_lg(), posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
(uniform_idx_as_vec2(self.map_size_lg(), posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||||
index)
|
index)
|
||||||
)?;
|
)?;
|
||||||
let alt = sample.alt;
|
|
||||||
/* let z_cache = block_gen.get_z_cache(wpos);
|
|
||||||
sample.alt = alt.max(z_cache.get_z_limits(&mut block_gen).2); */
|
|
||||||
sample.alt = alt.max(BlockGen::get_cliff_height(
|
|
||||||
&block_gen.column_gen,
|
|
||||||
&mut block_gen.column_cache,
|
|
||||||
wpos.map(|e| e as f32),
|
|
||||||
&sample.close_cliffs,
|
|
||||||
sample.cliff_hill,
|
|
||||||
32.0,
|
|
||||||
index,
|
|
||||||
));
|
|
||||||
sample.basement += sample.alt - alt;
|
|
||||||
// sample.water_level = CONFIG.sea_level.max(sample.water_level);
|
// sample.water_level = CONFIG.sea_level.max(sample.water_level);
|
||||||
|
|
||||||
Some(sample)
|
Some(sample)
|
||||||
@ -1997,6 +1983,14 @@ impl WorldSim {
|
|||||||
pub fn get_nearest_cave(&self, wpos: Vec2<i32>) -> Option<(f32, Vec2<f32>, Cave, Vec2<f32>)> {
|
pub fn get_nearest_cave(&self, wpos: Vec2<i32>) -> Option<(f32, Vec2<f32>, Cave, Vec2<f32>)> {
|
||||||
self.get_nearest_way(wpos, |chunk| Some(chunk.cave))
|
self.get_nearest_way(wpos, |chunk| Some(chunk.cave))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)> + '_ {
|
||||||
|
// Deterministic based on wpos
|
||||||
|
std::array::IntoIter::new(self.gen_ctx.structure_gen.get(wpos))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
Loading…
Reference in New Issue
Block a user