pub mod enums;
pub mod errors;
pub mod settings;
pub mod meshgen;
use bevy::{
ecs::{lifecycle::HookContext, world::DeferredWorld},
prelude::*,
};
use fastrand::Rng;
use crate::{
meshgen::generate_tree_meshes,
settings::{GlobalTreeMeshSettings, TreeMeshSettings},
};
pub struct TreeProceduralGenerationPlugin;
impl Plugin for TreeProceduralGenerationPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<GlobalTreeMeshSettings>();
app.register_type::<GlobalTreeMeshSettings>();
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::<GlobalTreeMeshSettings>),
);
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::<GlobalTreeMeshSettings>()
.map(|global| {
<GlobalTreeMeshSettings as Into<TreeMeshSettings>>::into(global.clone())
})
})
.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<GlobalTreeMeshSettings>,
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 => &<GlobalTreeMeshSettings as Into<TreeMeshSettings>>::into(
global_tree_settings.clone(),
),
};
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)>,
global_tree_settings: Res<GlobalTreeMeshSettings>,
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);
let tree_settings = <GlobalTreeMeshSettings as Into<TreeMeshSettings>>::into(
global_tree_settings.clone(),
);
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),
}
}
}
}