2020-11-07 21:50:52 +00:00
use crate ::{
all ::ForestKind ,
block ::block_from_structure ,
column ::ColumnGen ,
util ::{ RandomPerm , Sampler , UnitChooser } ,
Canvas , CONFIG ,
} ;
use common ::{
2020-12-12 22:14:24 +00:00
assets ::AssetHandle ,
2021-01-03 20:43:07 +00:00
terrain ::{ Block , BlockKind , structure ::{ Structure , StructureBlock , StructuresGroup } } ,
2020-11-07 21:50:52 +00:00
vol ::ReadVol ,
} ;
2020-12-12 01:45:46 +00:00
use hashbrown ::HashMap ;
2020-11-07 21:50:52 +00:00
use lazy_static ::lazy_static ;
2020-12-12 22:14:24 +00:00
use std ::f32 ;
2020-11-07 21:50:52 +00:00
use vek ::* ;
2021-01-03 20:43:07 +00:00
use rand ::prelude ::* ;
2020-11-07 21:50:52 +00:00
lazy_static! {
2020-12-12 22:14:24 +00:00
static ref OAKS : AssetHandle < StructuresGroup > = Structure ::load_group ( " oaks " ) ;
static ref OAK_STUMPS : AssetHandle < StructuresGroup > = Structure ::load_group ( " oak_stumps " ) ;
static ref PINES : AssetHandle < StructuresGroup > = Structure ::load_group ( " pines " ) ;
static ref PALMS : AssetHandle < StructuresGroup > = Structure ::load_group ( " palms " ) ;
static ref ACACIAS : AssetHandle < StructuresGroup > = Structure ::load_group ( " acacias " ) ;
static ref BAOBABS : AssetHandle < StructuresGroup > = Structure ::load_group ( " baobabs " ) ;
static ref FRUIT_TREES : AssetHandle < StructuresGroup > = Structure ::load_group ( " fruit_trees " ) ;
static ref BIRCHES : AssetHandle < StructuresGroup > = Structure ::load_group ( " birch " ) ;
2020-12-13 01:09:57 +00:00
static ref MANGROVE_TREES : AssetHandle < StructuresGroup > =
Structure ::load_group ( " mangrove_trees " ) ;
2020-12-12 22:14:24 +00:00
static ref QUIRKY : AssetHandle < StructuresGroup > = Structure ::load_group ( " quirky " ) ;
static ref QUIRKY_DRY : AssetHandle < StructuresGroup > = Structure ::load_group ( " quirky_dry " ) ;
static ref SWAMP_TREES : AssetHandle < StructuresGroup > = Structure ::load_group ( " swamp_trees " ) ;
2020-11-07 21:50:52 +00:00
}
static MODEL_RAND : RandomPerm = RandomPerm ::new ( 0xDB21C052 ) ;
static UNIT_CHOOSER : UnitChooser = UnitChooser ::new ( 0x700F4EC7 ) ;
static QUIRKY_RAND : RandomPerm = RandomPerm ::new ( 0xA634460F ) ;
2020-11-23 15:39:03 +00:00
#[ allow(clippy::if_same_then_else) ]
2020-11-08 23:19:07 +00:00
pub fn apply_trees_to ( canvas : & mut Canvas ) {
2021-01-03 20:43:07 +00:00
// TODO: Get rid of this
enum TreeModel {
Structure ( Structure ) ,
Procedural ( ProceduralTree ) ,
}
2020-11-07 21:50:52 +00:00
struct Tree {
pos : Vec3 < i32 > ,
2021-01-03 20:43:07 +00:00
model : TreeModel ,
2020-11-07 21:50:52 +00:00
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 ( ) ) ) ? ;
2020-11-11 11:42:22 +00:00
let is_quirky = QUIRKY_RAND . chance ( seed , 1.0 / 500.0 ) ;
2020-11-23 15:39:03 +00:00
// Ensure that it's valid to place a *thing* here
2020-11-11 11:42:22 +00:00
if col . alt < col . water_level
2020-11-22 01:37:20 +00:00
| | col . spawn_rate < 0.9
2020-11-07 21:50:52 +00:00
| | col . water_dist . map ( | d | d < 8.0 ) . unwrap_or ( false )
| | col . path . map ( | ( d , _ , _ , _ ) | d < 12.0 ) . unwrap_or ( false )
{
return None ;
2020-11-23 15:39:03 +00:00
}
// Ensure that it's valid to place a tree here
if ! is_quirky & & ( ( seed . wrapping_mul ( 13 ) ) & 0xFF ) as f32 / 256.0 > col . tree_density
2020-11-15 01:40:23 +00:00
{
2020-11-11 11:42:22 +00:00
return None ;
2020-11-07 21:50:52 +00:00
}
Some ( Tree {
pos : Vec3 ::new ( tree_wpos . x , tree_wpos . y , col . alt as i32 ) ,
2021-01-03 20:43:07 +00:00
model : ' model : {
2020-12-12 22:14:24 +00:00
let models : AssetHandle < _ > = if is_quirky {
2020-11-07 21:50:52 +00:00
if col . temp > CONFIG . desert_temp {
2020-12-12 22:14:24 +00:00
* QUIRKY_DRY
2020-11-07 21:50:52 +00:00
} else {
2020-12-12 22:14:24 +00:00
* QUIRKY
2020-11-07 21:50:52 +00:00
}
} else {
match col . forest_kind {
2020-11-15 01:40:23 +00:00
ForestKind ::Oak if QUIRKY_RAND . chance ( seed + 1 , 1.0 / 16.0 ) = > {
2020-12-12 22:14:24 +00:00
* OAK_STUMPS
2020-11-15 01:40:23 +00:00
} ,
ForestKind ::Oak if QUIRKY_RAND . chance ( seed + 2 , 1.0 / 20.0 ) = > {
2020-12-12 22:14:24 +00:00
* FRUIT_TREES
2020-11-15 01:40:23 +00:00
} ,
2020-12-12 22:14:24 +00:00
ForestKind ::Palm = > * PALMS ,
ForestKind ::Acacia = > * ACACIAS ,
ForestKind ::Baobab = > * BAOBABS ,
2021-01-03 20:43:07 +00:00
// ForestKind::Oak => *OAKS,
ForestKind ::Oak = > {
let mut rng = RandomPerm ::new ( seed ) ;
break 'model TreeModel ::Procedural ( ProceduralTree ::generate ( & mut rng ) ) ;
} ,
2020-12-12 22:14:24 +00:00
ForestKind ::Pine = > * PINES ,
ForestKind ::Birch = > * BIRCHES ,
ForestKind ::Mangrove = > * MANGROVE_TREES ,
ForestKind ::Swamp = > * SWAMP_TREES ,
2020-11-07 21:50:52 +00:00
}
} ;
2020-12-12 22:14:24 +00:00
let models = models . read ( ) ;
2021-01-03 20:43:07 +00:00
TreeModel ::Structure ( models [ ( MODEL_RAND . get ( seed . wrapping_mul ( 17 ) ) / 13 ) as usize % models . len ( ) ]
. clone ( ) )
2020-11-07 21:50:52 +00:00
} ,
seed ,
units : UNIT_CHOOSER . get ( seed ) ,
} )
} ) {
tree
} else {
continue ;
} ;
2021-01-03 20:43:07 +00:00
let bounds = match & tree . model {
TreeModel ::Structure ( s ) = > s . get_bounds ( ) ,
TreeModel ::Procedural ( t ) = > t . get_bounds ( ) . map ( | e | e as i32 ) ,
} ;
2021-01-04 00:28:04 +00:00
let rpos2d = ( wpos2d - tree . pos . xy ( ) )
. map2 ( Vec2 ::new ( tree . units . 0 , tree . units . 1 ) , | p , unit | {
unit * p
} )
. sum ( ) ;
if ! Aabr ::from ( bounds ) . contains_point ( rpos2d ) {
2021-01-03 20:43:07 +00:00
// Skip this column
2021-01-04 00:28:04 +00:00
continue ;
2021-01-03 20:43:07 +00:00
}
2020-11-09 15:06:37 +00:00
let mut is_top = true ;
2020-11-09 17:09:33 +00:00
let mut is_leaf_top = true ;
2020-11-09 15:06:37 +00:00
for z in ( bounds . min . z .. bounds . max . z ) . rev ( ) {
2020-11-07 21:50:52 +00:00
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 ( ) ,
2021-01-03 20:43:07 +00:00
if let Some ( block ) = match & tree . model {
TreeModel ::Structure ( s ) = > s . get ( model_pos ) . ok ( ) . copied ( ) ,
2021-01-04 00:28:04 +00:00
TreeModel ::Procedural ( t ) = > Some ( match t . is_branch_or_leaves_at ( model_pos . map ( | e | e as f32 + 0.5 ) ) {
( true , _ ) = > StructureBlock ::Normal ( Rgb ::new ( 60 , 30 , 0 ) ) ,
( _ , true ) = > StructureBlock ::TemperateLeaves ,
( _ , _ ) = > StructureBlock ::None ,
} ) ,
2021-01-03 20:43:07 +00:00
} {
2020-11-07 22:51:12 +00:00
block
} else {
2021-01-04 00:28:04 +00:00
break
2020-11-07 22:51:12 +00:00
} ,
2020-11-07 21:50:52 +00:00
wpos ,
tree . pos . xy ( ) ,
tree . seed ,
col ,
Block ::air ,
)
2020-11-09 15:06:37 +00:00
. map ( | block | {
2020-11-09 17:09:33 +00:00
// Add a snow covering to the block above under certain circumstances
if col . snow_cover
& & ( ( block . kind ( ) = = BlockKind ::Leaves & & is_leaf_top )
| | ( is_top & & block . is_filled ( ) ) )
{
2020-11-09 15:06:37 +00:00
canvas . set (
wpos + Vec3 ::unit_z ( ) ,
Block ::new ( BlockKind ::Snow , Rgb ::new ( 210 , 210 , 255 ) ) ,
) ;
}
canvas . set ( wpos , block ) ;
2020-11-09 17:09:33 +00:00
is_leaf_top = false ;
2020-11-09 15:06:37 +00:00
is_top = false ;
} )
. unwrap_or_else ( | | {
2020-11-09 17:09:33 +00:00
is_leaf_top = true ;
2020-11-09 15:06:37 +00:00
} ) ;
2020-11-07 21:50:52 +00:00
}
}
} ) ;
}
2021-01-03 20:43:07 +00:00
// TODO: Rename this to `Tree` when the name conflict is gone
struct ProceduralTree {
branches : Vec < Branch > ,
}
impl ProceduralTree {
pub fn generate ( rng : & mut impl Rng ) -> Self {
let mut branches = Vec ::new ( ) ;
2021-01-05 17:24:14 +00:00
const ITERATIONS : usize = 4 ;
2021-01-05 15:51:49 +00:00
fn add_branches ( branches : & mut Vec < Branch > , start : Vec3 < f32 > , dir : Vec3 < f32 > , depth : usize , rng : & mut impl Rng ) {
let mut branch_dir = ( dir + Vec3 ::< f32 > ::new ( rng . gen_range ( - 1.0 , 1.0 ) , rng . gen_range ( - 1.0 , 1.0 ) , rng . gen_range ( 0.25 , 1.0 ) ) . cross ( dir ) . normalized ( ) * 0.45 * ( depth as f32 + 0.5 ) ) . normalized ( ) ; // I wish `vek` had a `Vec3::from_fn`
if branch_dir . z < 0. {
2021-01-05 17:34:22 +00:00
branch_dir . z = ( branch_dir . z ) / 16. + 0.2
2021-01-05 15:51:49 +00:00
}
2021-01-04 00:28:04 +00:00
let branch_len = 12.0 / ( depth as f32 * 0.25 + 1.0 ) ; // Zipf, I guess
2021-01-03 20:43:07 +00:00
let end = start + branch_dir * branch_len ;
2021-01-05 17:24:14 +00:00
branches . push ( Branch ::new ( LineSegment3 { start , end } , 0.3 + 2.5 / ( depth + 1 ) as f32 , if depth = = ITERATIONS {
2021-01-04 01:27:24 +00:00
rng . gen_range ( 3.0 , 5.0 )
2021-01-03 20:43:07 +00:00
} else {
0.0
2021-01-05 16:21:15 +00:00
} ) ) ;
2021-01-03 20:43:07 +00:00
2021-01-05 17:24:14 +00:00
if depth < ITERATIONS {
2021-01-04 01:49:26 +00:00
let sub_branches = if depth = = 0 { 3 } else { rng . gen_range ( 2 , 4 ) } ;
for _ in 0 .. sub_branches {
2021-01-05 15:51:49 +00:00
add_branches ( branches , end , branch_dir , depth + 1 , rng ) ;
2021-01-03 20:43:07 +00:00
}
}
}
2021-01-04 21:19:43 +00:00
let height = rng . gen_range ( 13 , 30 ) as f32 ;
let dx = rng . gen_range ( - 5 , 5 ) as f32 ;
let dy = rng . gen_range ( - 5 , 5 ) as f32 ;
// Generate the trunk
2021-01-05 16:21:15 +00:00
branches . push ( Branch ::new ( LineSegment3 { start : Vec3 ::zero ( ) , end : Vec3 ::new ( dx , dy , height ) } , 3.0 , 0.0 ) ) ;
2021-01-04 21:19:43 +00:00
// Generate branches
2021-01-05 15:51:49 +00:00
const TEN_DEGREES : f32 = f32 ::consts ::TAU / 36. ;
2021-01-04 21:19:43 +00:00
2021-01-05 15:51:49 +00:00
let mut current_angle = 0. ;
while current_angle < f32 ::consts ::TAU {
for i in 1 .. 3 {
let current_angle = current_angle + rng . gen_range ( - TEN_DEGREES / 2. , TEN_DEGREES / 2. ) ;
2021-01-04 21:19:43 +00:00
add_branches (
& mut branches ,
2021-01-05 15:51:49 +00:00
Vec3 ::new ( dx , dy , height - rng . gen_range ( 0.0 , height / 3.0 ) ) ,
Vec3 ::new ( current_angle . cos ( ) , current_angle . sin ( ) , rng . gen_range ( 0.2 * i as f32 , 0.7 * i as f32 ) ) . normalized ( ) ,
1 ,
rng
2021-01-04 21:19:43 +00:00
) ;
if rng . gen_range ( 0 , 4 ) ! = 2 {
break ;
}
}
2021-01-05 15:51:49 +00:00
current_angle + = rng . gen_range ( TEN_DEGREES , TEN_DEGREES * 5. ) ;
2021-01-04 21:19:43 +00:00
}
2021-01-03 20:43:07 +00:00
2021-01-05 17:24:14 +00:00
add_branches (
& mut branches ,
Vec3 ::new ( dx , dy , height - rng . gen_range ( 0.0 , height / 3.0 ) ) ,
Vec3 ::new ( rng . gen_range ( - 0.2 , 0.2 ) , rng . gen_range ( - 0.2 , 0.2 ) , 1. ) . normalized ( ) ,
2 ,
rng
) ;
2021-01-03 20:43:07 +00:00
Self {
branches ,
}
}
pub fn get_bounds ( & self ) -> Aabb < f32 > {
2021-01-04 00:28:04 +00:00
let bounds = self . branches
. iter ( )
. fold ( Aabb ::default ( ) , | Aabb { min , max } , branch | Aabb {
min : Vec3 ::partial_min ( min , Vec3 ::partial_min ( branch . line . start , branch . line . end ) - branch . radius - 8.0 ) ,
max : Vec3 ::partial_max ( max , Vec3 ::partial_max ( branch . line . start , branch . line . end ) + branch . radius + 8.0 ) ,
} ) ;
2021-01-03 20:43:07 +00:00
self . branches
. iter ( )
2021-01-04 00:28:04 +00:00
. for_each ( | branch | {
assert! ( bounds . contains_point ( branch . line . start ) ) ;
assert! ( bounds . contains_point ( branch . line . end ) ) ;
} ) ;
2021-01-03 20:43:07 +00:00
2021-01-04 00:28:04 +00:00
bounds
2021-01-03 20:43:07 +00:00
}
2021-01-04 00:28:04 +00:00
pub fn is_branch_or_leaves_at ( & self , pos : Vec3 < f32 > ) -> ( bool , bool ) {
2021-01-05 16:21:15 +00:00
let mut is_leave = false ;
2021-01-04 00:28:04 +00:00
for branch in & self . branches {
let p_d2 = branch . line . projected_point ( pos ) . distance_squared ( pos ) ;
2021-01-05 16:21:15 +00:00
if ! is_leave {
fn finvsqrt ( x : f32 ) -> f32 {
2021-01-05 16:50:09 +00:00
let y = f32 ::from_bits ( 0x5f375a86 - ( x . to_bits ( ) > > 1 ) ) ;
y * ( 1.5 - ( x * 0.5 * y * y ) )
2021-01-05 16:21:15 +00:00
}
if branch . health * finvsqrt ( p_d2 ) > 1.0 {
is_leave = true ;
}
}
if p_d2 < branch . squared_radius {
return ( true , false ) ;
2021-01-04 01:27:24 +00:00
}
2021-01-04 00:28:04 +00:00
}
2021-01-05 16:21:15 +00:00
( false , is_leave )
2021-01-03 20:43:07 +00:00
}
}
struct Branch {
line : LineSegment3 < f32 > ,
radius : f32 ,
2021-01-05 16:21:15 +00:00
squared_radius : f32 ,
2021-01-03 20:43:07 +00:00
health : f32 ,
}
2021-01-05 16:21:15 +00:00
impl Branch {
fn new ( line : LineSegment3 < f32 > , radius : f32 , health : f32 ) -> Self {
Self {
line ,
squared_radius : radius . powi ( 2 ) ,
radius ,
health ,
}
}
}