bevy_procedural_tree/
lib.rs1pub mod enums;
2pub mod settings;
3pub mod errors;
4
5pub mod meshgen;
6
7use bevy::{ecs::{lifecycle::HookContext, world::DeferredWorld}, prelude::*};
8use fastrand::Rng;
9
10use crate::{meshgen::generate_tree_meshes, settings::TreeMeshSettings};
11
12
13pub struct TreeProceduralGenerationPlugin;
14
15impl Plugin for TreeProceduralGenerationPlugin {
16 fn build(&self, app: &mut App) {
17 app.init_resource::<TreeMeshSettings>();
18 app.register_type::<TreeMeshSettings>();
19 app.init_resource::<TreeDefaultMaterials>();
20 app.register_type::<TreeDefaultMaterials>();
21 app.register_type::<Tree>();
22 app.register_type::<Leaves>();
23
24 app.add_systems(PostUpdate, update_all_tree_meshes_with_global_settings.run_if(resource_changed::<TreeMeshSettings>));
25 app.add_systems(PostUpdate, update_all_tree_meshes_with_local_settings);
26 }
27}
28
29
30
31#[derive(Component, Reflect, Clone, Debug)]
32#[component(on_add = new_tree_component_added)]
33pub struct Tree {
34 pub seed: u64,
37 pub tree_mesh_settings_override: Option<TreeMeshSettings>,
39 pub bark_material_override: Option<MeshMaterial3d<StandardMaterial>>,
41 pub leaf_material_override: Option<MeshMaterial3d<StandardMaterial>>,
44}
45
46
47#[derive(Resource, Reflect)]
48struct TreeDefaultMaterials {
49 pub bark_material: MeshMaterial3d<StandardMaterial>,
51 pub leaf_material: MeshMaterial3d<StandardMaterial>,
54}
55
56impl FromWorld for TreeDefaultMaterials {
57 fn from_world(world: &mut World) -> Self {
58 let mut materials = world.get_resource_mut::<Assets<StandardMaterial>>().unwrap();
59 Self {
60 bark_material: MeshMaterial3d(materials.add(Color::WHITE)),
61 leaf_material: MeshMaterial3d(materials.add(Color::LinearRgba(LinearRgba { red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0 })))
62 }
63 }
64}
65
66#[derive(Component, Reflect)]
67struct Leaves(Entity);
68
69fn new_tree_component_added(mut world: DeferredWorld, context: HookContext) {
70 let tree_entity = context.entity;
71
72 let tree: Tree = (*world.entity(tree_entity).components::<&Tree>()).clone();
75 let tree_mesh_settings = tree.tree_mesh_settings_override.or_else(
76 || {
77 world.get_resource::<TreeMeshSettings>().cloned()
78 }
79 ).unwrap();
80
81 let mut rng: Rng = Rng::with_seed(tree.seed);
82
83 match generate_tree_meshes(&tree_mesh_settings, &mut rng) {
84 Ok((branches_mesh, leaves_mesh)) => {
85 let mut meshes = world.get_resource_mut::<Assets<Mesh>>().unwrap();
87
88 let branches_mesh = Mesh3d(meshes.add(branches_mesh));
90 let leaves_mesh = Mesh3d(meshes.add(leaves_mesh));
91
92 let default_materials = world.get_resource::<TreeDefaultMaterials>().unwrap();
93 let branch_material = tree.bark_material_override.clone().unwrap_or_else(|| default_materials.bark_material.clone());
95 let leaf_material = tree.leaf_material_override.clone().unwrap_or_else(|| default_materials.leaf_material.clone());
97
98 let mut commands = world.commands();
100 let leaves_id = commands.spawn((
101 Name::new("ProcGenTreeLeaves"),
102 leaves_mesh,
103 leaf_material,
104 )).id();
105
106 let mut tree_commands = commands.entity(tree_entity);
107
108 tree_commands.insert((
109 Name::new("ProcGenTreeBranches"),
110 Leaves(leaves_id),
111 branches_mesh,
112 branch_material
113 )).add_child(leaves_id);
114 },
115 Err(err) => error!("Error during tree mesh generation: {}", err),
116 }
117}
118
119
120fn update_all_tree_meshes_with_local_settings(
121 trees: Query<(Entity, &Tree, &MeshMaterial3d<StandardMaterial>, &Leaves), Changed<Tree>>,
122 mesh_materials: Query<&MeshMaterial3d<StandardMaterial>>,
123 mut meshes: ResMut<Assets<Mesh>>,
124 global_tree_settings: Res<TreeMeshSettings>,
125 default_materials: Res<TreeDefaultMaterials>,
126 mut commands: Commands,
127)
128{
129 for (tree_entity, tree, current_bark_material, leaves_entity) in trees.iter() {
132 let tree_settings: &TreeMeshSettings = match tree.tree_mesh_settings_override {
133 Some(ref tree_settings) => tree_settings,
134 None => global_tree_settings.as_ref(),
135 };
136
137 let mut rng: Rng = Rng::with_seed(tree.seed);
138
139 match generate_tree_meshes(tree_settings, &mut rng) {
140 Ok((branches_mesh, leaves_mesh)) => {
141 let branches_mesh = Mesh3d(meshes.add(branches_mesh));
142 let leaves_mesh = Mesh3d(meshes.add(leaves_mesh));
143
144 commands.entity(tree_entity).insert(branches_mesh);
145 commands.entity(leaves_entity.0).insert(leaves_mesh);
146
147 match tree.bark_material_override { Some(ref bark_material_from_local_settings) => {
150 if !current_bark_material.eq(bark_material_from_local_settings) {
151 commands.entity(tree_entity).insert(bark_material_from_local_settings.clone());
152 }
153 },
154 None => {
155 if !current_bark_material.eq(&default_materials.bark_material) {
156 commands.entity(tree_entity).insert(default_materials.bark_material.clone());
157 }
158 },
159 }
160
161 if let Ok(current_leaf_material) = mesh_materials.get(leaves_entity.0) {
162 match tree.leaf_material_override { Some(ref leaf_material_from_local_settings) => {
164 if !current_leaf_material.eq(leaf_material_from_local_settings) {
165 commands.entity(leaves_entity.0).insert(leaf_material_from_local_settings.clone());
166 }
167 },
168 None => {
169 if !current_leaf_material.eq(&default_materials.leaf_material) {
170 commands.entity(leaves_entity.0).insert(default_materials.leaf_material.clone());
171 }
172 },
173 }
174 }
175 },
176 Err(err) => error!("Error during tree mesh generation: {}", err),
177 }
178 }
179}
180
181fn update_all_tree_meshes_with_global_settings(
182 trees: Query<(Entity, &Tree, &Leaves)>,
183 tree_settings: Res<TreeMeshSettings>,
184 mut meshes: ResMut<Assets<Mesh>>,
185 mut commands: Commands,
186) {
187 for (tree_entity, tree, leaves_entity) in trees.iter() {
191 if tree.tree_mesh_settings_override.is_none() {
192 let mut rng: Rng = Rng::with_seed(tree.seed);
193
194 match generate_tree_meshes(&tree_settings, &mut rng) {
195 Ok((branches_mesh, leaves_mesh)) => {
196 let branches_mesh = Mesh3d(meshes.add(branches_mesh));
197 let leaves_mesh = Mesh3d(meshes.add(leaves_mesh));
198
199 commands.entity(tree_entity).insert(branches_mesh);
200 commands.entity(leaves_entity.0).insert(leaves_mesh);
201 },
202 Err(err) => error!("Error during tree mesh generation: {}", err),
203 }
204 }
205 }
206
207}