33mod schematic;
44mod ty;
55
6+ use crate :: worldgen:: presence:: PresenceGrid ;
7+ use crate :: worldgen:: util:: shuffle_seed_for_column;
68use crate :: worldgen:: { FinishingGenerator , NearbyBiomes , TopBlocks } ;
7- use feather_core:: Chunk ;
9+ use feather_core:: { Biome , Chunk , ChunkPosition } ;
10+ use rand:: distributions:: { Distribution , WeightedIndex } ;
11+ use rand:: { Rng , SeedableRng } ;
12+ use rand_xorshift:: XorShiftRng ;
13+ use std:: cmp;
14+ use std:: convert:: TryFrom ;
815pub use ty:: TreeType ;
916
1017/// Finisher for generating trees, depending on biome.
@@ -19,6 +26,143 @@ impl FinishingGenerator for TreeFinisher {
1926 top_blocks : & TopBlocks ,
2027 seed : u64 ,
2128 ) {
22- unimplemented ! ( )
29+ // Presence grid for trees.
30+ // We set values to `true` for
31+ // any column in which a tree will be generated.
32+ let mut tree_presences = PresenceGrid :: new ( ) ;
33+
34+ // Compute presence of trees for each column within this chunk
35+ // and the 6 columns bordering it on each side. For columns with
36+ // trees generated, do the following:
37+ // * First, check if a tree is already generated within params.spread.
38+ // If so, remove the tree.
39+ // * Generate a tree schematic for the tree and apply it to the chunk.
40+ for x in -6 ..16 + 6 {
41+ for z in -6 ..16 + 6 {
42+ let biome = biomes. biome_at ( x, z) ;
43+ let params = params_for_biome ( biome) ;
44+
45+ if compute_presence_for_column ( chunk. position ( ) , & params, x, z, seed) {
46+ if tree_presences. is_presence_within ( x, z, params. spread ) {
47+ continue ;
48+ }
49+
50+ tree_presences. set_presence_at ( x, z, true ) ;
51+
52+ let column_seed = column_seed ( seed, chunk. position ( ) , x, z) ;
53+ let mut rng = XorShiftRng :: seed_from_u64 ( column_seed) ;
54+
55+ // Choose tree type based on weights.
56+ let possible_trees = params. possible_trees ;
57+ let index = possible_trees. weights . sample ( & mut rng) ;
58+ let tree = possible_trees. trees [ index] ;
59+
60+ // Generate tree schematic and write it to the chunk.
61+ let schematic = schematic:: generate_tree ( tree, seed) ;
62+ let y = top_blocks. top_block_at (
63+ cmp:: min ( 15 , usize:: try_from ( x) . unwrap_or ( 0 ) ) ,
64+ cmp:: min ( 15 , usize:: try_from ( z) . unwrap_or ( 0 ) ) ,
65+ ) ; // TODO
66+ schematic. write_to_chunk (
67+ chunk,
68+ ( chunk. position ( ) . x * 16 ) as isize + x,
69+ y as isize ,
70+ ( chunk. position ( ) . z * 16 ) as isize + z,
71+ ) ;
72+ }
73+ }
74+ }
75+ }
76+ }
77+
78+ fn compute_presence_for_column (
79+ center_chunk : ChunkPosition ,
80+ params : & TreeParams ,
81+ column_x : isize ,
82+ column_z : isize ,
83+ seed : u64 ,
84+ ) -> bool {
85+ let seed = column_seed ( seed, center_chunk, column_x, column_z) ;
86+ let mut rng = XorShiftRng :: seed_from_u64 ( seed) ;
87+
88+ rng. gen_bool ( params. frequency / 256.0 )
89+ }
90+
91+ fn column_seed ( seed : u64 , center_chunk : ChunkPosition , column_x : isize , column_z : isize ) -> u64 {
92+ let chunk = ChunkPosition :: new (
93+ center_chunk. x + column_x as i32 / 16 ,
94+ center_chunk. z + column_z as i32 / 16 ,
95+ ) ;
96+
97+ let mut local_x = column_x % 16 ;
98+ let mut local_z = column_z % 16 ;
99+ if column_x < 0 {
100+ local_x = 16 - local_x;
101+ }
102+ if column_z < 0 {
103+ local_z = 16 - column_z;
104+ }
105+
106+ shuffle_seed_for_column ( seed, chunk, local_x as usize , local_z as usize )
107+ }
108+
109+ /// WeightedIndex for a biome's tree weights, with
110+ /// the array of possible tree types corresponding
111+ /// to samples from the weighted index.
112+ #[ derive( Debug ) ]
113+ struct TreeWeights {
114+ weights : WeightedIndex < f64 > ,
115+ trees : & ' static [ TreeType ] ,
116+ }
117+
118+ impl TreeWeights {
119+ fn new ( weights : & [ f64 ] , trees : & ' static [ TreeType ] ) -> Self {
120+ Self {
121+ weights : WeightedIndex :: new ( weights) . unwrap ( ) ,
122+ trees,
123+ }
124+ }
125+ }
126+
127+ /// Settings of a biome for tree generation.
128+ #[ derive( Debug ) ]
129+ struct TreeParams {
130+ /// Frequency of trees. Higher values mean more trees.
131+ frequency : f64 ,
132+ /// Minimum distance between two trees.
133+ spread : u32 ,
134+ /// Table of tree types possible for this biome
135+ /// and their corresponding weights. The table
136+ /// for each tree is in a `lazy_static` variable,
137+ /// since initializing it requires an expensive
138+ /// allocation.
139+ possible_trees : & ' static TreeWeights ,
140+ }
141+
142+ lazy_static ! {
143+ static ref WEIGHTS_DEFAULT : TreeWeights = TreeWeights :: new( & [ 1.0 ] , & [ TreeType :: Oak ] ) ;
144+ static ref WEIGHTS_FOREST : TreeWeights = TreeWeights :: new( & [ 1.0 ] , & [ TreeType :: Oak ] ) ;
145+ }
146+
147+ impl Default for TreeParams {
148+ /// Tree parameters which generate zero trees.
149+ fn default ( ) -> Self {
150+ Self {
151+ frequency : 0.0 ,
152+ spread : 0 ,
153+ possible_trees : & WEIGHTS_DEFAULT ,
154+ }
155+ }
156+ }
157+
158+ /// Returns the tree parameters for the given biome.
159+ fn params_for_biome ( biome : Biome ) -> TreeParams {
160+ match biome {
161+ Biome :: Forest | Biome :: DarkForest => TreeParams {
162+ frequency : 4.0 ,
163+ spread : 4 ,
164+ possible_trees : & WEIGHTS_FOREST ,
165+ } ,
166+ _ => TreeParams :: default ( ) , // TODO
23167 }
24168}
0 commit comments