mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
More tree variety, denser forests
This commit is contained in:
parent
8ed16ccb07
commit
ac5a60d260
@ -31,7 +31,13 @@ impl<T> PartialEq for Id<T> {
|
|||||||
}
|
}
|
||||||
impl<T> fmt::Debug for Id<T> {
|
impl<T> fmt::Debug for Id<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "Id<{}>({}, {})", std::any::type_name::<T>(), self.idx, self.gen)
|
write!(
|
||||||
|
f,
|
||||||
|
"Id<{}>({}, {})",
|
||||||
|
std::any::type_name::<T>(),
|
||||||
|
self.idx,
|
||||||
|
self.gen
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T> hash::Hash for Id<T> {
|
impl<T> hash::Hash for Id<T> {
|
||||||
@ -69,9 +75,7 @@ impl<T> Store<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, id: Id<T>) -> &T {
|
pub fn get(&self, id: Id<T>) -> &T {
|
||||||
let entry = self.entries
|
let entry = self.entries.get(id.idx as usize).unwrap();
|
||||||
.get(id.idx as usize)
|
|
||||||
.unwrap();
|
|
||||||
if entry.gen == id.gen {
|
if entry.gen == id.gen {
|
||||||
entry.item.as_ref().unwrap()
|
entry.item.as_ref().unwrap()
|
||||||
} else {
|
} else {
|
||||||
@ -80,9 +84,7 @@ impl<T> Store<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, id: Id<T>) -> &mut T {
|
pub fn get_mut(&mut self, id: Id<T>) -> &mut T {
|
||||||
let entry = self.entries
|
let entry = self.entries.get_mut(id.idx as usize).unwrap();
|
||||||
.get_mut(id.idx as usize)
|
|
||||||
.unwrap();
|
|
||||||
if entry.gen == id.gen {
|
if entry.gen == id.gen {
|
||||||
entry.item.as_mut().unwrap()
|
entry.item.as_mut().unwrap()
|
||||||
} else {
|
} else {
|
||||||
@ -90,13 +92,9 @@ impl<T> Store<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ids(&self) -> impl Iterator<Item = Id<T>> + '_ {
|
pub fn ids(&self) -> impl Iterator<Item = Id<T>> + '_ { self.iter().map(|(id, _)| id) }
|
||||||
self.iter().map(|(id, _)| id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn values(&self) -> impl Iterator<Item = &T> + '_ {
|
pub fn values(&self) -> impl Iterator<Item = &T> + '_ { self.iter().map(|(_, item)| item) }
|
||||||
self.iter().map(|(_, item)| item)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut T> + '_ {
|
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut T> + '_ {
|
||||||
self.iter_mut().map(|(_, item)| item)
|
self.iter_mut().map(|(_, item)| item)
|
||||||
@ -111,7 +109,8 @@ impl<T> Store<T> {
|
|||||||
idx: idx as u32,
|
idx: idx as u32,
|
||||||
gen: entry.gen,
|
gen: entry.gen,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}).zip(entry.item.as_ref())
|
})
|
||||||
|
.zip(entry.item.as_ref())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,14 +123,16 @@ impl<T> Store<T> {
|
|||||||
idx: idx as u32,
|
idx: idx as u32,
|
||||||
gen: entry.gen,
|
gen: entry.gen,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}).zip(entry.item.as_mut())
|
})
|
||||||
|
.zip(entry.item.as_mut())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, item: T) -> Id<T> {
|
pub fn insert(&mut self, item: T) -> Id<T> {
|
||||||
if self.len < self.entries.len() {
|
if self.len < self.entries.len() {
|
||||||
// TODO: Make this more efficient with a lookahead system
|
// TODO: Make this more efficient with a lookahead system
|
||||||
let (idx, entry) = self.entries
|
let (idx, entry) = self
|
||||||
|
.entries
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(_, e)| e.item.is_none())
|
.find(|(_, e)| e.item.is_none())
|
||||||
@ -161,13 +162,10 @@ impl<T> Store<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, id: Id<T>) -> Option<T> {
|
pub fn remove(&mut self, id: Id<T>) -> Option<T> {
|
||||||
if let Some(item) = self.entries
|
if let Some(item) = self
|
||||||
|
.entries
|
||||||
.get_mut(id.idx as usize)
|
.get_mut(id.idx as usize)
|
||||||
.and_then(|e| if e.gen == id.gen {
|
.and_then(|e| if e.gen == id.gen { e.item.take() } else { None })
|
||||||
e.item.take()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
self.len -= 1;
|
self.len -= 1;
|
||||||
Some(item)
|
Some(item)
|
||||||
|
@ -5,7 +5,10 @@ use veloren_world::site2::test_site;
|
|||||||
fn main() {
|
fn main() {
|
||||||
let site = test_site();
|
let site = test_site();
|
||||||
let size = site.bounds().size();
|
let size = site.bounds().size();
|
||||||
println!("{}", BeginSvg { w: size.w as f32, h: size.h as f32 });
|
println!("{}", BeginSvg {
|
||||||
|
w: size.w as f32,
|
||||||
|
h: size.h as f32
|
||||||
|
});
|
||||||
|
|
||||||
for plot in site.plots() {
|
for plot in site.plots() {
|
||||||
let bounds = plot.find_bounds();
|
let bounds = plot.find_bounds();
|
||||||
@ -15,7 +18,11 @@ fn main() {
|
|||||||
w: bounds.size().w as f32,
|
w: bounds.size().w as f32,
|
||||||
h: bounds.size().h as f32,
|
h: bounds.size().h as f32,
|
||||||
style: Style {
|
style: Style {
|
||||||
fill: Fill::Color(Color { r: 50, g: 50, b: 50 }),
|
fill: Fill::Color(Color {
|
||||||
|
r: 50,
|
||||||
|
g: 50,
|
||||||
|
b: 50
|
||||||
|
}),
|
||||||
stroke: Stroke::Color(Color { r: 0, g: 0, b: 0 }, 1.0),
|
stroke: Stroke::Color(Color { r: 0, g: 0, b: 0 }, 1.0),
|
||||||
opacity: 1.0,
|
opacity: 1.0,
|
||||||
stroke_opacity: 1.0,
|
stroke_opacity: 1.0,
|
||||||
|
@ -7,14 +7,17 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets::AssetHandle,
|
assets::AssetHandle,
|
||||||
terrain::{Block, BlockKind, structure::{Structure, StructureBlock, StructuresGroup}},
|
terrain::{
|
||||||
|
structure::{Structure, StructureBlock, StructuresGroup},
|
||||||
|
Block, BlockKind,
|
||||||
|
},
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use rand::prelude::*;
|
||||||
use std::{f32, ops::Range};
|
use std::{f32, ops::Range};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use rand::prelude::*;
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref OAKS: AssetHandle<StructuresGroup> = Structure::load_group("oaks");
|
static ref OAKS: AssetHandle<StructuresGroup> = Structure::load_group("oaks");
|
||||||
@ -99,15 +102,25 @@ pub fn apply_trees_to(canvas: &mut Canvas) {
|
|||||||
ForestKind::Acacia => *ACACIAS,
|
ForestKind::Acacia => *ACACIAS,
|
||||||
ForestKind::Baobab => *BAOBABS,
|
ForestKind::Baobab => *BAOBABS,
|
||||||
// ForestKind::Oak => *OAKS,
|
// ForestKind::Oak => *OAKS,
|
||||||
ForestKind::Oak => break 'model TreeModel::Procedural(
|
ForestKind::Oak => {
|
||||||
ProceduralTree::generate(TreeConfig::OAK, seed),
|
break 'model TreeModel::Procedural(
|
||||||
StructureBlock::TemperateLeaves,
|
ProceduralTree::generate(
|
||||||
),
|
TreeConfig::oak(&mut RandomPerm::new(seed)),
|
||||||
|
&mut RandomPerm::new(seed),
|
||||||
|
),
|
||||||
|
StructureBlock::TemperateLeaves,
|
||||||
|
);
|
||||||
|
},
|
||||||
//ForestKind::Pine => *PINES,
|
//ForestKind::Pine => *PINES,
|
||||||
ForestKind::Pine => break 'model TreeModel::Procedural(
|
ForestKind::Pine => {
|
||||||
ProceduralTree::generate(TreeConfig::PINE, seed),
|
break 'model TreeModel::Procedural(
|
||||||
StructureBlock::PineLeaves,
|
ProceduralTree::generate(
|
||||||
),
|
TreeConfig::pine(&mut RandomPerm::new(seed)),
|
||||||
|
&mut RandomPerm::new(seed),
|
||||||
|
),
|
||||||
|
StructureBlock::PineLeaves,
|
||||||
|
);
|
||||||
|
},
|
||||||
ForestKind::Birch => *BIRCHES,
|
ForestKind::Birch => *BIRCHES,
|
||||||
ForestKind::Mangrove => *MANGROVE_TREES,
|
ForestKind::Mangrove => *MANGROVE_TREES,
|
||||||
ForestKind::Swamp => *SWAMP_TREES,
|
ForestKind::Swamp => *SWAMP_TREES,
|
||||||
@ -115,8 +128,11 @@ pub fn apply_trees_to(canvas: &mut Canvas) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let models = models.read();
|
let models = models.read();
|
||||||
TreeModel::Structure(models[(MODEL_RAND.get(seed.wrapping_mul(17)) / 13) as usize % models.len()]
|
TreeModel::Structure(
|
||||||
.clone())
|
models[(MODEL_RAND.get(seed.wrapping_mul(17)) / 13) as usize
|
||||||
|
% models.len()]
|
||||||
|
.clone(),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
seed,
|
seed,
|
||||||
units: UNIT_CHOOSER.get(seed),
|
units: UNIT_CHOOSER.get(seed),
|
||||||
@ -133,9 +149,7 @@ pub fn apply_trees_to(canvas: &mut Canvas) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let rpos2d = (wpos2d - tree.pos.xy())
|
let rpos2d = (wpos2d - tree.pos.xy())
|
||||||
.map2(Vec2::new(tree.units.0, tree.units.1), |p, unit| {
|
.map2(Vec2::new(tree.units.0, tree.units.1), |p, unit| unit * p)
|
||||||
unit * p
|
|
||||||
})
|
|
||||||
.sum();
|
.sum();
|
||||||
if !Aabr::from(bounds).contains_point(rpos2d) {
|
if !Aabr::from(bounds).contains_point(rpos2d) {
|
||||||
// Skip this column
|
// Skip this column
|
||||||
@ -158,15 +172,17 @@ pub fn apply_trees_to(canvas: &mut Canvas) {
|
|||||||
info.index(),
|
info.index(),
|
||||||
if let Some(block) = match &tree.model {
|
if let Some(block) = match &tree.model {
|
||||||
TreeModel::Structure(s) => s.get(model_pos).ok().copied(),
|
TreeModel::Structure(s) => s.get(model_pos).ok().copied(),
|
||||||
TreeModel::Procedural(t, leaf_block) => Some(match t.is_branch_or_leaves_at(model_pos.map(|e| e as f32 + 0.5)) {
|
TreeModel::Procedural(t, leaf_block) => Some(
|
||||||
(true, _) => StructureBlock::Normal(Rgb::new(60, 30, 0)),
|
match t.is_branch_or_leaves_at(model_pos.map(|e| e as f32 + 0.5)) {
|
||||||
(_, true) => *leaf_block,
|
(true, _) => StructureBlock::Normal(Rgb::new(60, 30, 0)),
|
||||||
(_, _) => StructureBlock::None,
|
(_, true) => *leaf_block,
|
||||||
}),
|
(_, _) => StructureBlock::None,
|
||||||
|
},
|
||||||
|
),
|
||||||
} {
|
} {
|
||||||
block
|
block
|
||||||
} else {
|
} else {
|
||||||
break
|
break;
|
||||||
},
|
},
|
||||||
wpos,
|
wpos,
|
||||||
tree.pos.xy(),
|
tree.pos.xy(),
|
||||||
@ -215,50 +231,62 @@ pub struct TreeConfig {
|
|||||||
pub max_depth: usize,
|
pub max_depth: usize,
|
||||||
/// The number of branches that form from each branch.
|
/// The number of branches that form from each branch.
|
||||||
pub splits: usize,
|
pub splits: usize,
|
||||||
/// The range of proportions along a branch at which a split into another branch might occur.
|
/// The range of proportions along a branch at which a split into another
|
||||||
/// This value is clamped between 0 and 1, but a wider range may bias the results towards branch ends.
|
/// branch might occur. This value is clamped between 0 and 1, but a
|
||||||
|
/// wider range may bias the results towards branch ends.
|
||||||
pub split_range: Range<f32>,
|
pub split_range: Range<f32>,
|
||||||
/// The bias applied to the length of branches based on the proportion along their parent that they eminate from.
|
/// The bias applied to the length of branches based on the proportion along
|
||||||
/// -1.0 = negative bias (branches at ends are longer, branches at the start are shorter)
|
/// their parent that they eminate from. -1.0 = negative bias (branches
|
||||||
/// 0.0 = no bias (branches do not change their length with regard to parent branch proportion)
|
/// at ends are longer, branches at the start are shorter) 0.0 = no bias
|
||||||
/// 1.0 = positive bias (branches at ends are shorter, branches at the start are longer)
|
/// (branches do not change their length with regard to parent branch
|
||||||
|
/// proportion) 1.0 = positive bias (branches at ends are shorter,
|
||||||
|
/// branches at the start are longer)
|
||||||
pub branch_len_bias: f32,
|
pub branch_len_bias: f32,
|
||||||
/// The scale of leaves in the vertical plane. Less than 1.0 implies a flattening of the leaves.
|
/// The scale of leaves in the vertical plane. Less than 1.0 implies a
|
||||||
|
/// flattening of the leaves.
|
||||||
pub leaf_vertical_scale: f32,
|
pub leaf_vertical_scale: f32,
|
||||||
/// How evenly spaced (vs random) sub-branches are along their parent.
|
/// How evenly spaced (vs random) sub-branches are along their parent.
|
||||||
pub proportionality: f32,
|
pub proportionality: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TreeConfig {
|
impl TreeConfig {
|
||||||
pub const OAK: Self = Self {
|
pub fn oak(rng: &mut impl Rng) -> Self {
|
||||||
trunk_len: 12.0,
|
let scale = Lerp::lerp(0.8, 1.5, rng.gen::<f32>().powi(4));
|
||||||
trunk_radius: 3.0,
|
|
||||||
branch_child_len: 0.8,
|
|
||||||
branch_child_radius: 0.6,
|
|
||||||
leaf_radius: 3.0..5.0,
|
|
||||||
straightness: 0.5,
|
|
||||||
max_depth: 4,
|
|
||||||
splits: 3,
|
|
||||||
split_range: 0.5..1.5,
|
|
||||||
branch_len_bias: 0.0,
|
|
||||||
leaf_vertical_scale: 1.0,
|
|
||||||
proportionality: 0.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PINE: Self = Self {
|
Self {
|
||||||
trunk_len: 32.0,
|
trunk_len: 12.0 * scale,
|
||||||
trunk_radius: 1.5,
|
trunk_radius: 3.0 * scale,
|
||||||
branch_child_len: 0.3,
|
branch_child_len: 0.8,
|
||||||
branch_child_radius: 0.0,
|
branch_child_radius: 0.6,
|
||||||
leaf_radius: 2.0..2.5,
|
leaf_radius: 3.0 * scale..4.0 * scale,
|
||||||
straightness: 0.0,
|
straightness: 0.5,
|
||||||
max_depth: 1,
|
max_depth: 4,
|
||||||
splits: 56,
|
splits: 3,
|
||||||
split_range: 0.2..1.2,
|
split_range: 0.5..1.5,
|
||||||
branch_len_bias: 0.75,
|
branch_len_bias: 0.0,
|
||||||
leaf_vertical_scale: 0.3,
|
leaf_vertical_scale: 1.0,
|
||||||
proportionality: 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));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
trunk_len: 32.0 * scale,
|
||||||
|
trunk_radius: 1.5 * scale,
|
||||||
|
branch_child_len: 0.3,
|
||||||
|
branch_child_radius: 0.0,
|
||||||
|
leaf_radius: 2.0 * scale..2.5 * scale,
|
||||||
|
straightness: 0.0,
|
||||||
|
max_depth: 1,
|
||||||
|
splits: 56,
|
||||||
|
split_range: 0.2..1.2,
|
||||||
|
branch_len_bias: 0.75,
|
||||||
|
leaf_vertical_scale: 0.3,
|
||||||
|
proportionality: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Rename this to `Tree` when the name conflict is gone
|
// TODO: Rename this to `Tree` when the name conflict is gone
|
||||||
@ -269,9 +297,7 @@ pub struct ProceduralTree {
|
|||||||
|
|
||||||
impl ProceduralTree {
|
impl ProceduralTree {
|
||||||
/// Generate a new tree using the given configuration and seed.
|
/// Generate a new tree using the given configuration and seed.
|
||||||
pub fn generate(config: TreeConfig, seed: u32) -> Self {
|
pub fn generate(config: TreeConfig, rng: &mut impl Rng) -> Self {
|
||||||
let mut rng = RandomPerm::new(seed);
|
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
branches: Vec::new(),
|
branches: Vec::new(),
|
||||||
trunk_idx: 0, // Gets replaced later
|
trunk_idx: 0, // Gets replaced later
|
||||||
@ -283,20 +309,21 @@ impl ProceduralTree {
|
|||||||
// Our trunk starts at the origin...
|
// Our trunk starts at the origin...
|
||||||
Vec3::zero(),
|
Vec3::zero(),
|
||||||
// ...and has a roughly upward direction
|
// ...and has a roughly upward direction
|
||||||
Vec3::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0), 5.0).normalized(),
|
Vec3::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0), 10.0).normalized(),
|
||||||
config.trunk_len,
|
config.trunk_len,
|
||||||
config.trunk_radius,
|
config.trunk_radius,
|
||||||
0,
|
0,
|
||||||
None,
|
None,
|
||||||
&mut rng,
|
rng,
|
||||||
);
|
);
|
||||||
this.trunk_idx = trunk_idx;
|
this.trunk_idx = trunk_idx;
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively add a branch (with sub-branches) to the tree's branch graph, returning the index and AABB of the
|
// Recursively add a branch (with sub-branches) to the tree's branch graph,
|
||||||
// branch. This AABB gets propagated down to the parent and is used later during sampling to cull the branches to
|
// returning the index and AABB of the branch. This AABB gets propagated
|
||||||
|
// down to the parent and is used later during sampling to cull the branches to
|
||||||
// be sampled.
|
// be sampled.
|
||||||
fn add_branch(
|
fn add_branch(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -318,7 +345,8 @@ impl ProceduralTree {
|
|||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
|
||||||
// The AABB that covers this branch, along with wood and leaves that eminate from it
|
// The AABB that covers this branch, along with wood and leaves that eminate
|
||||||
|
// from it
|
||||||
let mut aabb = Aabb {
|
let mut aabb = Aabb {
|
||||||
min: Vec3::partial_min(start, end) - wood_radius.max(leaf_radius),
|
min: Vec3::partial_min(start, end) - wood_radius.max(leaf_radius),
|
||||||
max: Vec3::partial_max(start, end) + wood_radius.max(leaf_radius),
|
max: Vec3::partial_max(start, end) + wood_radius.max(leaf_radius),
|
||||||
@ -327,31 +355,43 @@ impl ProceduralTree {
|
|||||||
let mut child_idx = None;
|
let mut child_idx = None;
|
||||||
// Don't add child branches if we're already enough layers into the tree
|
// Don't add child branches if we're already enough layers into the tree
|
||||||
if depth < config.max_depth {
|
if depth < config.max_depth {
|
||||||
let x_axis = dir.cross(Vec3::<f32>::zero().map(|_| rng.gen_range(-1.0..1.0))).normalized();
|
let x_axis = dir
|
||||||
|
.cross(Vec3::<f32>::zero().map(|_| rng.gen_range(-1.0..1.0)))
|
||||||
|
.normalized();
|
||||||
let y_axis = dir.cross(x_axis).normalized();
|
let y_axis = dir.cross(x_axis).normalized();
|
||||||
let screw_shift = rng.gen_range(0.0..f32::consts::TAU);
|
let screw_shift = rng.gen_range(0.0..f32::consts::TAU);
|
||||||
|
|
||||||
for i in 0..config.splits {
|
for i in 0..config.splits {
|
||||||
let dist = Lerp::lerp(i as f32 / (config.splits - 1) as f32, rng.gen_range(0.0..1.0), config.proportionality);
|
let dist = Lerp::lerp(
|
||||||
|
i as f32 / (config.splits - 1) as f32,
|
||||||
|
rng.gen_range(0.0..1.0),
|
||||||
|
config.proportionality,
|
||||||
|
);
|
||||||
|
|
||||||
const PHI: f32 = 0.618;
|
const PHI: f32 = 0.618;
|
||||||
const RAD_PER_BRANCH: f32 = f32::consts::TAU * PHI;
|
const RAD_PER_BRANCH: f32 = f32::consts::TAU * PHI;
|
||||||
let screw =
|
let screw = (screw_shift + dist * config.splits as f32 * RAD_PER_BRANCH).sin()
|
||||||
(screw_shift + dist * config.splits as f32 * RAD_PER_BRANCH).sin() * x_axis +
|
* x_axis
|
||||||
(screw_shift + dist * config.splits as f32 * RAD_PER_BRANCH).cos() * y_axis;
|
+ (screw_shift + dist * config.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
|
// Choose a point close to the branch to act as the target direction for the
|
||||||
// let split_factor = rng.gen_range(config.split_range.start, config.split_range.end).clamped(0.0, 1.0);
|
// branch to grow in let split_factor =
|
||||||
let split_factor = Lerp::lerp(config.split_range.start, config.split_range.end, dist);
|
// rng.gen_range(config.split_range.start, config.split_range.end).clamped(0.0,
|
||||||
let tgt = Lerp::lerp_unclamped(
|
// 1.0);
|
||||||
start,
|
let split_factor =
|
||||||
end,
|
Lerp::lerp(config.split_range.start, config.split_range.end, dist);
|
||||||
split_factor,
|
let tgt = Lerp::lerp_unclamped(start, end, split_factor)
|
||||||
) + Lerp::lerp(Vec3::<f32>::zero().map(|_| rng.gen_range(-1.0..1.0)), screw, config.proportionality);
|
+ Lerp::lerp(
|
||||||
|
Vec3::<f32>::zero().map(|_| rng.gen_range(-1.0..1.0)),
|
||||||
|
screw,
|
||||||
|
config.proportionality,
|
||||||
|
);
|
||||||
// Start the branch at the closest point to the target
|
// Start the branch at the closest point to the target
|
||||||
let branch_start = line.projected_point(tgt);
|
let branch_start = line.projected_point(tgt);
|
||||||
// Now, interpolate between the target direction and the parent branch's direction to find a direction
|
// Now, interpolate between the target direction and the parent branch's
|
||||||
let branch_dir = Lerp::lerp(tgt - branch_start, dir, config.straightness).normalized();
|
// direction to find a direction
|
||||||
|
let branch_dir =
|
||||||
|
Lerp::lerp(tgt - branch_start, dir, config.straightness).normalized();
|
||||||
|
|
||||||
let (branch_idx, branch_aabb) = self.add_branch(
|
let (branch_idx, branch_aabb) = self.add_branch(
|
||||||
config,
|
config,
|
||||||
@ -359,14 +399,18 @@ impl ProceduralTree {
|
|||||||
branch_dir,
|
branch_dir,
|
||||||
branch_len
|
branch_len
|
||||||
* config.branch_child_len
|
* config.branch_child_len
|
||||||
* (1.0 - (split_factor - 0.5) * 2.0 * config.branch_len_bias.clamped(-1.0, 1.0)),
|
* (1.0
|
||||||
|
- (split_factor - 0.5)
|
||||||
|
* 2.0
|
||||||
|
* config.branch_len_bias.clamped(-1.0, 1.0)),
|
||||||
branch_radius * config.branch_child_radius,
|
branch_radius * config.branch_child_radius,
|
||||||
depth + 1,
|
depth + 1,
|
||||||
child_idx,
|
child_idx,
|
||||||
rng,
|
rng,
|
||||||
);
|
);
|
||||||
child_idx = Some(branch_idx);
|
child_idx = Some(branch_idx);
|
||||||
// Parent branches AABBs include the AABBs of child branches to allow for culling during sampling
|
// Parent branches AABBs include the AABBs of child branches to allow for
|
||||||
|
// culling during sampling
|
||||||
aabb.expand_to_contain(branch_aabb);
|
aabb.expand_to_contain(branch_aabb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -386,19 +430,20 @@ impl ProceduralTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the bounding box that covers the tree (all branches and leaves)
|
/// Get the bounding box that covers the tree (all branches and leaves)
|
||||||
pub fn get_bounds(&self) -> Aabb<f32> {
|
pub fn get_bounds(&self) -> Aabb<f32> { self.branches[self.trunk_idx].aabb }
|
||||||
self.branches[self.trunk_idx].aabb
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively search for branches or leaves by walking the tree's branch graph.
|
// Recursively search for branches or leaves by walking the tree's branch graph.
|
||||||
fn is_branch_or_leaves_at_inner(&self, pos: Vec3<f32>, branch_idx: usize) -> (bool, bool) {
|
fn is_branch_or_leaves_at_inner(&self, pos: Vec3<f32>, branch_idx: usize) -> (bool, bool) {
|
||||||
let branch = &self.branches[branch_idx];
|
let branch = &self.branches[branch_idx];
|
||||||
// Always probe the sibling branch, since our AABB doesn't include its bounds (it's not one of our children)
|
// Always probe the sibling branch, since our AABB doesn't include its bounds
|
||||||
let branch_or_leaves = branch.sibling_idx
|
// (it's not one of our children)
|
||||||
|
let branch_or_leaves = branch
|
||||||
|
.sibling_idx
|
||||||
.map(|idx| Vec2::from(self.is_branch_or_leaves_at_inner(pos, idx)))
|
.map(|idx| Vec2::from(self.is_branch_or_leaves_at_inner(pos, idx)))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Only continue probing this sub-graph of the tree if the sample position falls within its AABB
|
// Only continue probing this sub-graph of the tree if the sample position falls
|
||||||
|
// within its AABB
|
||||||
if branch.aabb.contains_point(pos) {
|
if branch.aabb.contains_point(pos) {
|
||||||
(branch_or_leaves
|
(branch_or_leaves
|
||||||
// Probe this branch
|
// Probe this branch
|
||||||
@ -407,23 +452,25 @@ impl ProceduralTree {
|
|||||||
| branch.child_idx
|
| branch.child_idx
|
||||||
.map(|idx| Vec2::from(self.is_branch_or_leaves_at_inner(pos, idx)))
|
.map(|idx| Vec2::from(self.is_branch_or_leaves_at_inner(pos, idx)))
|
||||||
.unwrap_or_default())
|
.unwrap_or_default())
|
||||||
.into_tuple()
|
.into_tuple()
|
||||||
} else {
|
} else {
|
||||||
branch_or_leaves.into_tuple()
|
branch_or_leaves.into_tuple()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine whether there are either branches or leaves at the given position in the tree.
|
/// Determine whether there are either branches or leaves at the given
|
||||||
|
/// position in the tree.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool) {
|
pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool) {
|
||||||
self.is_branch_or_leaves_at_inner(pos, self.trunk_idx)
|
self.is_branch_or_leaves_at_inner(pos, self.trunk_idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Branches are arranged in a graph shape. Each branch points to both its first child (if any) and also to the next
|
// Branches are arranged in a graph shape. Each branch points to both its first
|
||||||
// branch in the list of child branches associated with the parent. This means that the entire tree is laid out in a
|
// child (if any) and also to the next branch in the list of child branches
|
||||||
// walkable graph where each branch refers only to two other branches. As a result, walking the tree is simply a case
|
// associated with the parent. This means that the entire tree is laid out in a
|
||||||
// of performing double recursion.
|
// walkable graph where each branch refers only to two other branches. As a
|
||||||
|
// result, walking the tree is simply a case of performing double recursion.
|
||||||
struct Branch {
|
struct Branch {
|
||||||
line: LineSegment3<f32>,
|
line: LineSegment3<f32>,
|
||||||
wood_radius: f32,
|
wood_radius: f32,
|
||||||
@ -436,7 +483,8 @@ struct Branch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Branch {
|
impl Branch {
|
||||||
/// Determine whether there are either branches or leaves at the given position in the branch.
|
/// Determine whether there are either branches or leaves at the given
|
||||||
|
/// position in the branch.
|
||||||
pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool) {
|
pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool) {
|
||||||
// fn finvsqrt(x: f32) -> f32 {
|
// fn finvsqrt(x: f32) -> f32 {
|
||||||
// let y = f32::from_bits(0x5f375a86 - (x.to_bits() >> 1));
|
// let y = f32::from_bits(0x5f375a86 - (x.to_bits() >> 1));
|
||||||
|
@ -516,7 +516,7 @@ impl WorldSim {
|
|||||||
cave_0_nz: SuperSimplex::new().set_seed(rng.gen()),
|
cave_0_nz: SuperSimplex::new().set_seed(rng.gen()),
|
||||||
cave_1_nz: SuperSimplex::new().set_seed(rng.gen()),
|
cave_1_nz: SuperSimplex::new().set_seed(rng.gen()),
|
||||||
|
|
||||||
structure_gen: StructureGen2d::new(rng.gen(), 32, 16),
|
structure_gen: StructureGen2d::new(rng.gen(), 24, 8),
|
||||||
region_gen: StructureGen2d::new(rng.gen(), 400, 96),
|
region_gen: StructureGen2d::new(rng.gen(), 400, 96),
|
||||||
humid_nz: Billow::new()
|
humid_nz: Billow::new()
|
||||||
.set_octaves(9)
|
.set_octaves(9)
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
mod tile;
|
|
||||||
mod plot;
|
mod plot;
|
||||||
|
mod tile;
|
||||||
|
|
||||||
use vek::*;
|
|
||||||
use common::store::{Store, Id};
|
|
||||||
use crate::util::Grid;
|
|
||||||
use self::{
|
use self::{
|
||||||
tile::TileGrid,
|
|
||||||
plot::{Plot, PlotKind},
|
plot::{Plot, PlotKind},
|
||||||
|
tile::TileGrid,
|
||||||
};
|
};
|
||||||
|
use crate::util::Grid;
|
||||||
|
use common::store::{Id, Store};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Site {
|
pub struct Site {
|
||||||
@ -25,19 +25,17 @@ impl Site {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plots(&self) -> impl Iterator<Item=&Plot> + '_ {
|
pub fn plots(&self) -> impl Iterator<Item = &Plot> + '_ { self.plots.values() }
|
||||||
self.plots.values()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_plot(&mut self, plot: Plot) -> Id<Plot> {
|
pub fn create_plot(&mut self, plot: Plot) -> Id<Plot> { self.plots.insert(plot) }
|
||||||
self.plots.insert(plot)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate(rng: &mut impl Rng) -> Self {
|
pub fn generate(rng: &mut impl Rng) -> Self {
|
||||||
let mut site = Site::default();
|
let mut site = Site::default();
|
||||||
|
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
let dir = Vec2::<f32>::zero().map(|_| rng.gen_range(-1.0..1.0)).normalized();
|
let dir = Vec2::<f32>::zero()
|
||||||
|
.map(|_| rng.gen_range(-1.0..1.0))
|
||||||
|
.normalized();
|
||||||
let search_pos = (dir * 32.0).map(|e| e as i32);
|
let search_pos = (dir * 32.0).map(|e| e as i32);
|
||||||
|
|
||||||
site.tiles
|
site.tiles
|
||||||
@ -51,6 +49,4 @@ impl Site {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_site() -> Site {
|
pub fn test_site() -> Site { Site::generate(&mut thread_rng()) }
|
||||||
Site::generate(&mut thread_rng())
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use vek::*;
|
|
||||||
use crate::util::DHashSet;
|
use crate::util::DHashSet;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
pub struct Plot {
|
pub struct Plot {
|
||||||
kind: PlotKind,
|
kind: PlotKind,
|
||||||
@ -11,7 +11,9 @@ impl Plot {
|
|||||||
pub fn find_bounds(&self) -> Aabr<i32> {
|
pub fn find_bounds(&self) -> Aabr<i32> {
|
||||||
self.tiles
|
self.tiles
|
||||||
.iter()
|
.iter()
|
||||||
.fold(Aabr::new_empty(self.root_tile), |b, t| b.expanded_to_contain_point(*t))
|
.fold(Aabr::new_empty(self.root_tile), |b, t| {
|
||||||
|
b.expanded_to_contain_point(*t)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,18 +23,22 @@ impl TileGrid {
|
|||||||
let tpos = tpos + TILE_RADIUS as i32;
|
let tpos = tpos + TILE_RADIUS as i32;
|
||||||
self.zones
|
self.zones
|
||||||
.get(tpos)
|
.get(tpos)
|
||||||
.and_then(|zone| zone.as_ref()?.get(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32))))
|
.and_then(|zone| {
|
||||||
|
zone.as_ref()?
|
||||||
|
.get(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32)))
|
||||||
|
})
|
||||||
.and_then(|tile| tile.as_ref())
|
.and_then(|tile| tile.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, tpos: Vec2<i32>) -> Option<&mut Tile> {
|
pub fn get_mut(&mut self, tpos: Vec2<i32>) -> Option<&mut Tile> {
|
||||||
let tpos = tpos + TILE_RADIUS as i32;
|
let tpos = tpos + TILE_RADIUS as i32;
|
||||||
self.zones
|
self.zones.get_mut(tpos).and_then(|zone| {
|
||||||
.get_mut(tpos)
|
zone.get_or_insert_with(|| {
|
||||||
.and_then(|zone| zone
|
Grid::populate_from(Vec2::broadcast(ZONE_RADIUS as i32 * 2 + 1), |_| None)
|
||||||
.get_or_insert_with(|| Grid::populate_from(Vec2::broadcast(ZONE_RADIUS as i32 * 2 + 1), |_| None))
|
})
|
||||||
.get_mut(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32)))
|
.get_mut(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32)))
|
||||||
.map(|tile| tile.get_or_insert_with(|| Tile::empty())))
|
.map(|tile| tile.get_or_insert_with(|| Tile::empty()))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_near(&self, tpos: Vec2<i32>, f: impl Fn(&Tile) -> bool) -> Option<Vec2<i32>> {
|
pub fn find_near(&self, tpos: Vec2<i32>, f: impl Fn(&Tile) -> bool) -> Option<Vec2<i32>> {
|
||||||
@ -62,7 +66,5 @@ impl Tile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool { self.kind == TileKind::Empty }
|
||||||
self.kind == TileKind::Empty
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user