pub mod enums;
pub mod settings;
pub mod errors;
pub mod meshgen;
use bevy::{ecs::{lifecycle::HookContext, world::DeferredWorld}, prelude::*};
use fastrand::Rng;
use crate::{meshgen::generate_tree_meshes, settings::TreeMeshSettings};
pub struct TreeProceduralGenerationPlugin;
impl Plugin for TreeProceduralGenerationPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<TreeMeshSettings>();
app.register_type::<TreeMeshSettings>();
app.init_resource::<TreeDefaultMaterials>();
app.register_type::<TreeDefaultMaterials>();
app.register_type::<Tree>();
app.register_type::<Leaves>();
app.add_systems(PostUpdate, update_all_tree_meshes_with_global_settings.run_if(resource_changed::<TreeMeshSettings>));
app.add_systems(PostUpdate, update_all_tree_meshes_with_local_settings);
}
}
#[derive(Component, Reflect, Clone, Debug)]
#[component(on_add = new_tree_component_added)]
pub struct Tree {
pub seed: u64,
pub tree_mesh_settings_override: Option<TreeMeshSettings>,
pub bark_material_override: Option<MeshMaterial3d<StandardMaterial>>,
pub leaf_material_override: Option<MeshMaterial3d<StandardMaterial>>,
}
#[derive(Resource, Reflect)]
struct TreeDefaultMaterials {
pub bark_material: MeshMaterial3d<StandardMaterial>,
pub leaf_material: MeshMaterial3d<StandardMaterial>,
}
impl FromWorld for TreeDefaultMaterials {
fn from_world(world: &mut World) -> Self {
let mut materials = world.get_resource_mut::<Assets<StandardMaterial>>().unwrap();
Self {
bark_material: MeshMaterial3d(materials.add(Color::WHITE)),
leaf_material: MeshMaterial3d(materials.add(Color::LinearRgba(LinearRgba { red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0 })))
}
}
}
#[derive(Component, Reflect)]
struct Leaves(Entity);
fn new_tree_component_added(mut world: DeferredWorld, context: HookContext) {
let tree_entity = context.entity;
let tree: Tree = (*world.entity(tree_entity).components::<&Tree>()).clone();
let tree_mesh_settings = tree.tree_mesh_settings_override.or_else(
|| {
world.get_resource::<TreeMeshSettings>().cloned()
}
).unwrap();
let mut rng: Rng = Rng::with_seed(tree.seed);
match generate_tree_meshes(&tree_mesh_settings, &mut rng) {
Ok((branches_mesh, leaves_mesh)) => {
let mut meshes = world.get_resource_mut::<Assets<Mesh>>().unwrap();
let branches_mesh = Mesh3d(meshes.add(branches_mesh));
let leaves_mesh = Mesh3d(meshes.add(leaves_mesh));
let default_materials = world.get_resource::<TreeDefaultMaterials>().unwrap();
let branch_material = tree.bark_material_override.clone().unwrap_or_else(|| default_materials.bark_material.clone());
let leaf_material = tree.leaf_material_override.clone().unwrap_or_else(|| default_materials.leaf_material.clone());
let mut commands = world.commands();
let leaves_id = commands.spawn((
Name::new("ProcGenTreeLeaves"),
leaves_mesh,
leaf_material,
)).id();
let mut tree_commands = commands.entity(tree_entity);
tree_commands.insert((
Name::new("ProcGenTreeBranches"),
Leaves(leaves_id),
branches_mesh,
branch_material
)).add_child(leaves_id);
},
Err(err) => error!("Error during tree mesh generation: {}", err),
}
}
fn update_all_tree_meshes_with_local_settings(
trees: Query<(Entity, &Tree, &MeshMaterial3d<StandardMaterial>, &Leaves), Changed<Tree>>,
mesh_materials: Query<&MeshMaterial3d<StandardMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
global_tree_settings: Res<TreeMeshSettings>,
default_materials: Res<TreeDefaultMaterials>,
mut commands: Commands,
)
{
for (tree_entity, tree, current_bark_material, leaves_entity) in trees.iter() {
let tree_settings: &TreeMeshSettings = match tree.tree_mesh_settings_override {
Some(ref tree_settings) => tree_settings,
None => global_tree_settings.as_ref(),
};
let mut rng: Rng = Rng::with_seed(tree.seed);
match generate_tree_meshes(tree_settings, &mut rng) {
Ok((branches_mesh, leaves_mesh)) => {
let branches_mesh = Mesh3d(meshes.add(branches_mesh));
let leaves_mesh = Mesh3d(meshes.add(leaves_mesh));
commands.entity(tree_entity).insert(branches_mesh);
commands.entity(leaves_entity.0).insert(leaves_mesh);
match tree.bark_material_override { Some(ref bark_material_from_local_settings) => {
if !current_bark_material.eq(bark_material_from_local_settings) {
commands.entity(tree_entity).insert(bark_material_from_local_settings.clone());
}
},
None => {
if !current_bark_material.eq(&default_materials.bark_material) {
commands.entity(tree_entity).insert(default_materials.bark_material.clone());
}
},
}
if let Ok(current_leaf_material) = mesh_materials.get(leaves_entity.0) {
match tree.leaf_material_override { Some(ref leaf_material_from_local_settings) => {
if !current_leaf_material.eq(leaf_material_from_local_settings) {
commands.entity(leaves_entity.0).insert(leaf_material_from_local_settings.clone());
}
},
None => {
if !current_leaf_material.eq(&default_materials.leaf_material) {
commands.entity(leaves_entity.0).insert(default_materials.leaf_material.clone());
}
},
}
}
},
Err(err) => error!("Error during tree mesh generation: {}", err),
}
}
}
fn update_all_tree_meshes_with_global_settings(
trees: Query<(Entity, &Tree, &Leaves)>,
tree_settings: Res<TreeMeshSettings>,
mut meshes: ResMut<Assets<Mesh>>,
mut commands: Commands,
) {
for (tree_entity, tree, leaves_entity) in trees.iter() {
if tree.tree_mesh_settings_override.is_none() {
let mut rng: Rng = Rng::with_seed(tree.seed);
match generate_tree_meshes(&tree_settings, &mut rng) {
Ok((branches_mesh, leaves_mesh)) => {
let branches_mesh = Mesh3d(meshes.add(branches_mesh));
let leaves_mesh = Mesh3d(meshes.add(leaves_mesh));
commands.entity(tree_entity).insert(branches_mesh);
commands.entity(leaves_entity.0).insert(leaves_mesh);
},
Err(err) => error!("Error during tree mesh generation: {}", err),
}
}
}
}