1use bevy::prelude::*;
10use rand::{rngs::SmallRng, Rng, SeedableRng};
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14#[derive(Component, Clone, Reflect)]
19pub struct PixelTree {
20 pub trunk: TrunkData,
21 pub branches: Vec<Branch>,
22 pub leaves: LeafCluster,
23 pub wind_params: WindParams,
24 pub lod_level: u8,
25 pub template: TreeTemplate,
26}
27
28#[derive(Clone, Copy, PartialEq, Eq, Hash, Reflect)]
29pub enum TreeTemplate {
30 Default,
31 Pine,
32 Oak,
33 Willow,
34 Minimal,
35}
36
37#[derive(Clone, Reflect)]
38pub struct Branch {
39 pub start: Vec2,
40 pub end: Vec2,
41 pub thickness: f32,
42 pub generation: u8,
43 pub parent: Option<usize>,
44}
45
46#[derive(Clone, Reflect)]
47pub struct TrunkData {
48 pub base_pos: Vec2,
49 pub height: f32,
50 pub base_width: f32,
51 pub segments: Vec<TrunkSegment>,
52}
53
54#[derive(Clone, Reflect)]
55pub struct TrunkSegment {
56 pub start: Vec2,
57 pub end: Vec2,
58 pub width: f32,
59}
60
61#[derive(Component, Clone, Reflect)]
63pub struct LeafCluster {
64 pub leaves: Vec<Leaf>,
65}
66
67#[derive(Clone, Copy, Reflect)]
68pub struct Leaf {
69 pub pos: Vec2,
70 pub rot: f32,
71 pub scale: f32,
72 pub leaf_type: u8,
73 pub color: [u8; 3], }
75
76#[derive(Component, Clone, Reflect)]
77pub struct WindParams {
78 pub strength: f32,
79 pub frequency: f32,
80 pub turbulence: f32,
81 pub direction: Vec2,
82}
83
84#[derive(Component)]
86pub struct TreeInstanceData {
87 pub transform_matrix: Mat4,
88 pub wind_phase: f32,
89 pub color_variation: Vec3,
90}
91
92#[derive(Clone)]
97pub struct GenerationParams {
98 pub seed: u64,
99 pub height_range: (f32, f32),
100 pub trunk_width_range: (f32, f32),
101 pub branch_angle_variance: f32,
102 pub branch_length_decay: f32,
103 pub max_generations: u8,
104 pub branching_probability: f32,
105 pub leaf_density: f32,
106 pub lod_level: u8,
107 pub wind_params: WindParams,
108 pub template: TreeTemplate,
109}
110
111impl Default for GenerationParams {
112 fn default() -> Self {
113 Self {
114 seed: 12345,
115 height_range: (60.0, 120.0),
116 trunk_width_range: (8.0, 16.0),
117 branch_angle_variance: 45.0,
118 branch_length_decay: 0.7,
119 max_generations: 4,
120 branching_probability: 0.8,
121 leaf_density: 1.0,
122 lod_level: 0,
123 wind_params: WindParams {
124 strength: 2.0,
125 frequency: 1.5,
126 turbulence: 0.3,
127 direction: Vec2::new(1.0, 0.0),
128 },
129 template: TreeTemplate::Default,
130 }
131 }
132}
133
134pub struct TreeGenerator {
139 rng: SmallRng,
140}
141
142impl TreeGenerator {
143 pub fn new(seed: u64) -> Self {
144 Self {
145 rng: SmallRng::seed_from_u64(seed),
146 }
147 }
148
149 pub fn generate(&mut self, params: &GenerationParams) -> PixelTree {
150 let trunk = self.generate_trunk(params);
151
152 if params.lod_level >= 3 {
154 return PixelTree {
155 trunk,
156 branches: Vec::new(),
157 leaves: LeafCluster { leaves: Vec::new() },
158 wind_params: params.wind_params.clone(),
159 lod_level: params.lod_level,
160 template: TreeTemplate::Minimal,
161 };
162 }
163
164 let branches = self.generate_branches(&trunk, params);
165 let leaves = if params.lod_level >= 2 {
166 LeafCluster { leaves: Vec::new() }
167 } else {
168 self.generate_leaves(&branches, params)
169 };
170
171 PixelTree {
172 trunk,
173 branches,
174 leaves,
175 wind_params: params.wind_params.clone(),
176 lod_level: params.lod_level,
177 template: params.template,
178 }
179 }
180
181 fn generate_trunk(&mut self, params: &GenerationParams) -> TrunkData {
182 let height = self.rng.gen_range(params.height_range.0..=params.height_range.1);
183 let base_width = self.rng.gen_range(params.trunk_width_range.0..=params.trunk_width_range.1);
184
185 let segment_count = (height / 20.0).max(3.0) as usize;
186 let mut segments = Vec::with_capacity(segment_count);
187
188 for i in 0..segment_count {
189 let t = i as f32 / (segment_count - 1) as f32;
190 let y_start = t * height;
191 let y_end = if i == segment_count - 1 { height } else { (i + 1) as f32 / (segment_count - 1) as f32 * height };
192
193 let width_start = base_width * (1.0 - t * 0.7);
194
195 segments.push(TrunkSegment {
196 start: Vec2::new(0.0, y_start),
197 end: Vec2::new(0.0, y_end),
198 width: width_start,
199 });
200 }
201
202 TrunkData {
203 base_pos: Vec2::ZERO,
204 height,
205 base_width,
206 segments,
207 }
208 }
209
210 fn generate_branches(&mut self, trunk: &TrunkData, params: &GenerationParams) -> Vec<Branch> {
212 let estimated_branches = (params.max_generations as usize).pow(2) * 4;
214 let mut branches = Vec::with_capacity(estimated_branches.min(64));
215 let mut stack = Vec::with_capacity(32);
216
217 let main_branch_count = self.rng.gen_range(3..=6);
219 for i in 0..main_branch_count {
220 let height_factor = 0.6 + (i as f32 / main_branch_count as f32) * 0.4;
221 let start_pos = Vec2::new(0.0, trunk.height * height_factor);
222
223 let angle = self.rng.gen_range(-params.branch_angle_variance..params.branch_angle_variance).to_radians();
224 let length = trunk.height * 0.4 * self.rng.gen_range(0.7..1.2);
225
226 let direction = Vec2::new(angle.sin(), angle.cos().abs());
227 let end_pos = start_pos + direction * length;
228
229 let branch_idx = branches.len();
230 branches.push(Branch {
231 start: start_pos,
232 end: end_pos,
233 thickness: trunk.base_width * 0.4,
234 generation: 1,
235 parent: None,
236 });
237
238 if params.max_generations > 1 && self.rng.gen_range(0.0..1.0) < params.branching_probability {
240 stack.push((branch_idx, 2));
241 }
242 }
243
244 while let Some((parent_idx, generation)) = stack.pop() {
246 if generation > params.max_generations { continue; }
247
248 let parent_start = branches[parent_idx].start;
250 let parent_end = branches[parent_idx].end;
251 let parent_thickness = branches[parent_idx].thickness;
252
253 let sub_branch_count = self.rng.gen_range(1..=3);
254
255 for _ in 0..sub_branch_count {
256 let t = self.rng.gen_range(0.3..0.9);
257 let start_pos = parent_start.lerp(parent_end, t);
258
259 let parent_dir = (parent_end - parent_start).normalize();
260 let angle_offset = self.rng.gen_range(-60.0..60.0_f32).to_radians();
261 let new_dir = Vec2::new(
262 parent_dir.x * angle_offset.cos() - parent_dir.y * angle_offset.sin(),
263 parent_dir.x * angle_offset.sin() + parent_dir.y * angle_offset.cos(),
264 );
265
266 let length = parent_start.distance(parent_end) * params.branch_length_decay;
267 let end_pos = start_pos + new_dir * length;
268
269 let branch_idx = branches.len();
270 branches.push(Branch {
271 start: start_pos,
272 end: end_pos,
273 thickness: parent_thickness * 0.7,
274 generation,
275 parent: Some(parent_idx),
276 });
277
278 if generation < params.max_generations && self.rng.gen_range(0.0..1.0) < params.branching_probability {
280 stack.push((branch_idx, generation + 1));
281 }
282 }
283 }
284
285 branches
286 }
287
288 fn generate_leaves(&mut self, branches: &[Branch], params: &GenerationParams) -> LeafCluster {
289 let mut leaves = Vec::new();
290
291 let max_gen = branches.iter().map(|b| b.generation).max().unwrap_or(0);
293
294 for branch in branches.iter().filter(|b| b.generation >= max_gen.saturating_sub(1)) {
295 let leaf_count = (branch.start.distance(branch.end) * params.leaf_density * 0.1) as usize;
296
297 for _ in 0..leaf_count {
298 let t = self.rng.gen_range(0.2..1.0);
299 let pos = branch.start.lerp(branch.end, t);
300
301 let offset = Vec2::new(
303 self.rng.gen_range(-8.0..8.0),
304 self.rng.gen_range(-8.0..8.0),
305 );
306
307 let green_variation = self.rng.gen_range(0.8..1.0);
308 leaves.push(Leaf {
309 pos: pos + offset,
310 rot: self.rng.gen_range(0.0..360.0),
311 scale: self.rng.gen_range(0.8..1.2),
312 leaf_type: self.rng.gen_range(0..4),
313 color: [
314 (0.2 * 255.0) as u8,
315 (green_variation * 255.0) as u8,
316 (0.1 * 255.0) as u8,
317 ],
318 });
319 }
320 }
321
322 LeafCluster { leaves }
323 }
324}
325
326pub fn generate_tree_deterministic(pos: Vec2, params: &GenerationParams) -> PixelTree {
328 let seed = hash_position(pos) ^ params.seed;
329 let mut generator = TreeGenerator::new(seed);
330 generator.generate(params)
331}
332
333#[inline]
334fn hash_position(pos: Vec2) -> u64 {
335 let x_bits = pos.x.to_bits() as u64;
336 let y_bits = pos.y.to_bits() as u64;
337 x_bits.wrapping_mul(0x45d9f3b).wrapping_add(y_bits.wrapping_mul(0x119de1f3))
338}
339
340use rayon::prelude::*;
345
346pub struct BatchTreeGenerator {
347 base_params: GenerationParams,
348}
349
350impl BatchTreeGenerator {
351 pub fn new(base_params: GenerationParams) -> Self {
352 Self { base_params }
353 }
354
355 pub fn generate_forest(&self, positions: &[Vec2], seed_offsets: &[u64]) -> Vec<(Vec2, PixelTree)> {
356 positions
357 .par_iter()
358 .zip(seed_offsets.par_iter())
359 .map(|(pos, seed_offset)| {
360 let mut params = self.base_params.clone();
361 params.seed = params.seed.wrapping_add(*seed_offset);
362
363 let mut generator = TreeGenerator::new(params.seed);
364 let tree = generator.generate(¶ms);
365
366 (*pos, tree)
367 })
368 .collect()
369 }
370}
371
372#[derive(Clone, Copy, PartialEq)]
377pub enum LodLevel {
378 High = 0,
379 Medium = 1,
380 Low = 2,
381 Minimal = 3,
382}
383
384#[derive(Resource)]
385pub struct LodConfig {
386 pub max_branches: [usize; 4],
387 pub leaf_density: [f32; 4],
388 pub detail_distance: [f32; 4],
389}
390
391impl Default for LodConfig {
392 fn default() -> Self {
393 Self {
394 max_branches: [64, 32, 8, 0],
395 leaf_density: [1.0, 0.6, 0.3, 0.0],
396 detail_distance: [50.0, 100.0, 200.0, 400.0],
397 }
398 }
399}
400
401pub fn calculate_lod_level(distance: f32, config: &LodConfig) -> LodLevel {
402 for (i, &max_dist) in config.detail_distance.iter().enumerate() {
403 if distance <= max_dist {
404 return match i {
405 0 => LodLevel::High,
406 1 => LodLevel::Medium,
407 2 => LodLevel::Low,
408 _ => LodLevel::Minimal,
409 };
410 }
411 }
412 LodLevel::Minimal
413}
414
415#[derive(Resource)]
420pub struct WindTable {
421 samples: [f32; 256],
422}
423
424impl Default for WindTable {
425 fn default() -> Self {
426 Self::new()
427 }
428}
429
430impl WindTable {
431 pub fn new() -> Self {
432 let mut samples = [0.0; 256];
433 for (i, s) in samples.iter_mut().enumerate() {
434 *s = (i as f32 * std::f32::consts::TAU / 256.0).sin();
435 }
436 Self { samples }
437 }
438
439 #[inline(always)]
440 pub fn sample(&self, phase: f32) -> f32 {
441 let idx = ((phase % std::f32::consts::TAU) * 256.0 / std::f32::consts::TAU) as usize;
442 self.samples[idx.min(255)]
443 }
444}
445
446#[inline(always)]
448fn calculate_wind_displacement(time: f32, wind: &WindParams, pos: Vec2, wind_table: &WindTable) -> Vec2 {
449 let phase = time * wind.frequency + pos.dot(Vec2::new(0.1, 0.15));
450 wind.direction * wind.strength * wind_table.sample(phase) * (1.0 + wind.turbulence)
451}
452
453#[derive(Resource)]
458pub struct TreeSpatialIndex {
459 cell_size: f32,
460 cells: HashMap<(i32, i32), Vec<Entity>>,
461}
462
463impl Default for TreeSpatialIndex {
464 fn default() -> Self {
465 Self::new(100.0)
466 }
467}
468
469impl TreeSpatialIndex {
470 pub fn new(cell_size: f32) -> Self {
471 Self {
472 cell_size,
473 cells: HashMap::new(),
474 }
475 }
476
477 pub fn insert(&mut self, entity: Entity, pos: Vec2) {
478 let cell = self.world_to_cell(pos);
479 self.cells.entry(cell).or_insert_with(Vec::new).push(entity);
480 }
481
482 pub fn get_visible_trees(&self, view_bounds: Rect) -> Vec<Entity> {
483 let min_cell = self.world_to_cell(view_bounds.min);
484 let max_cell = self.world_to_cell(view_bounds.max);
485
486 let mut visible = Vec::with_capacity(128);
487 for x in min_cell.0..=max_cell.0 {
488 for y in min_cell.1..=max_cell.1 {
489 if let Some(entities) = self.cells.get(&(x, y)) {
490 visible.extend(entities);
491 }
492 }
493 }
494 visible
495 }
496
497 #[inline]
498 fn world_to_cell(&self, pos: Vec2) -> (i32, i32) {
499 (
500 (pos.x / self.cell_size).floor() as i32,
501 (pos.y / self.cell_size).floor() as i32,
502 )
503 }
504}
505
506#[derive(Resource)]
511pub struct TreeTemplateCache {
512 templates: HashMap<TreeTemplate, TreeMeshData>,
513}
514
515impl Default for TreeTemplateCache {
516 fn default() -> Self {
517 Self {
518 templates: HashMap::new(),
519 }
520 }
521}
522
523pub struct TreeMeshData {
524 pub vertex_data: Vec<TreeVertex>,
525 pub index_data: Vec<u16>,
526}
527
528#[derive(Clone, Copy)]
529pub struct TreeVertex {
530 pub position: Vec2,
531 pub uv: Vec2,
532 pub color: [u8; 4],
533}
534
535pub struct PixelTreePlugin;
540
541impl Plugin for PixelTreePlugin {
542 fn build(&self, app: &mut App) {
543 app
544 .add_systems(Update, update_trees)
545 .init_resource::<LodConfig>()
546 .init_resource::<WindTable>()
547 .init_resource::<TreeSpatialIndex>()
548 .init_resource::<TreeTemplateCache>()
549 .register_type::<PixelTree>()
550 .register_type::<WindParams>()
551 .register_type::<LeafCluster>();
552 }
553}
554
555pub fn update_trees(
557 time: Res<Time>,
558 wind_table: Res<WindTable>,
559 camera_query: Query<&Transform, (With<Camera>, Without<PixelTree>)>,
560 mut tree_query: Query<(&mut Transform, &mut PixelTree)>,
561 lod_config: Res<LodConfig>,
562) {
563 if let Ok(camera_transform) = camera_query.single() {
564 let camera_pos = camera_transform.translation.truncate();
565 let elapsed = time.elapsed_secs();
566
567 for (mut transform, mut tree) in tree_query.iter_mut() {
568 let tree_pos = transform.translation.truncate();
569
570 let distance = camera_pos.distance(tree_pos);
572 let new_lod = calculate_lod_level(distance, &lod_config) as u8;
573
574 if tree.lod_level != new_lod {
575 tree.lod_level = new_lod;
576 }
578
579 let wind_offset = calculate_wind_displacement(
581 elapsed,
582 &tree.wind_params,
583 tree_pos,
584 &wind_table,
585 );
586
587 transform.rotation = Quat::from_rotation_z(wind_offset.x * 0.01);
588 }
589 }
590}
591
592pub fn spawn_tree(
597 commands: &mut Commands,
598 position: Vec3,
599 params: GenerationParams,
600 spatial_index: &mut TreeSpatialIndex,
601) -> Entity {
602 let mut generator = TreeGenerator::new(params.seed);
603 let tree = generator.generate(¶ms);
604
605 let entity = commands.spawn((
606 Transform::from_translation(position),
607 GlobalTransform::default(),
608 tree,
609 Visibility::default(),
610 InheritedVisibility::default(),
611 ViewVisibility::default(),
612 )).id();
613
614 spatial_index.insert(entity, position.truncate());
615 entity
616}
617
618pub fn spawn_forest(
619 commands: &mut Commands,
620 positions: Vec<Vec2>,
621 base_params: GenerationParams,
622 spatial_index: &mut TreeSpatialIndex,
623) {
624 let batch_generator = BatchTreeGenerator::new(base_params);
625 let seed_offsets: Vec<u64> = (0..positions.len()).map(|i| i as u64 * 1337).collect();
626
627 let trees = batch_generator.generate_forest(&positions, &seed_offsets);
628
629 for (pos, tree) in trees {
630 let entity = commands.spawn((
631 Transform::from_translation(pos.extend(0.0)),
632 GlobalTransform::default(),
633 tree,
634 Visibility::default(),
635 InheritedVisibility::default(),
636 ViewVisibility::default(),
637 )).id();
638
639 spatial_index.insert(entity, pos);
640 }
641}
642
643#[derive(Serialize, Deserialize)]
648pub struct PackedTree {
649 pub trunk_height: u16,
650 pub trunk_width: u8,
651 pub branch_data: Vec<u32>,
652 pub leaf_clusters: Vec<PackedLeafCluster>,
653}
654
655#[derive(Serialize, Deserialize)]
656pub struct PackedLeafCluster {
657 pub count: u16,
658 pub center: [i16; 2],
659 pub data: Vec<u32>, }
661
662impl PackedTree {
663 pub fn pack(tree: &PixelTree) -> Self {
664 let mut branch_data = Vec::with_capacity(tree.branches.len());
665 for branch in &tree.branches {
666 let packed = ((branch.start.x as i8 as u32) << 24)
669 | ((branch.start.y as i8 as u32) << 16)
670 | ((branch.end.x as i8 as u32) << 8)
671 | (branch.end.y as i8 as u32);
672 branch_data.push(packed);
673 }
674
675 Self {
676 trunk_height: tree.trunk.height as u16,
677 trunk_width: tree.trunk.base_width as u8,
678 branch_data,
679 leaf_clusters: vec![], }
681 }
682}
683
684#[cfg(test)]
689mod tests {
690 use super::*;
691
692 #[test]
693 fn test_tree_generation() {
694 let params = GenerationParams::default();
695 let mut generator = TreeGenerator::new(12345);
696 let tree = generator.generate(¶ms);
697
698 assert!(!tree.branches.is_empty());
699 assert!(!tree.leaves.leaves.is_empty());
700 assert_eq!(tree.lod_level, 0);
701 }
702
703 #[test]
704 fn test_batch_generation() {
705 let positions = vec![Vec2::ZERO, Vec2::new(100.0, 0.0), Vec2::new(0.0, 100.0)];
706 let seed_offsets = vec![0, 1000, 2000];
707 let batch_gen = BatchTreeGenerator::new(GenerationParams::default());
708
709 let forest = batch_gen.generate_forest(&positions, &seed_offsets);
710 assert_eq!(forest.len(), 3);
711 }
712
713 #[test]
714 fn test_deterministic_generation() {
715 let params = GenerationParams::default();
716 let tree1 = generate_tree_deterministic(Vec2::new(100.0, 50.0), ¶ms);
717 let tree2 = generate_tree_deterministic(Vec2::new(100.0, 50.0), ¶ms);
718
719 assert_eq!(tree1.branches.len(), tree2.branches.len());
720 assert_eq!(tree1.trunk.height, tree2.trunk.height);
721 }
722}
723
724pub fn setup_demo_forest(
726 mut commands: Commands,
727 mut spatial_index: ResMut<TreeSpatialIndex>,
728) {
729 let positions = vec![
730 Vec2::new(-100.0, 0.0),
731 Vec2::new(0.0, 0.0),
732 Vec2::new(100.0, 0.0),
733 Vec2::new(-50.0, 80.0),
734 Vec2::new(50.0, 80.0),
735 ];
736
737 let mut params = GenerationParams::default();
738 params.wind_params.strength = 1.5;
739 params.wind_params.frequency = 2.0;
740
741 spawn_forest(&mut commands, positions, params, &mut spatial_index);
742}