simple growth generation
This commit is contained in:
parent
23bac13a1e
commit
c8417714cc
42
Cargo.lock
generated
42
Cargo.lock
generated
@ -809,7 +809,7 @@ dependencies = [
|
|||||||
"glam",
|
"glam",
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"libm",
|
"libm",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
"rand_distr",
|
"rand_distr",
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@ -2170,7 +2170,7 @@ checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"libm",
|
"libm",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3451,8 +3451,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha 0.3.1",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha 0.9.0",
|
||||||
|
"rand_core 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3462,7 +3472,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3474,6 +3494,15 @@ dependencies = [
|
|||||||
"getrandom 0.2.16",
|
"getrandom 0.2.16",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_distr"
|
name = "rand_distr"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@ -3481,7 +3510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
|
checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4111,6 +4140,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
"iyes_perf_ui",
|
"iyes_perf_ui",
|
||||||
|
"rand 0.9.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -6,6 +6,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = "0.16"
|
bevy = "0.16"
|
||||||
iyes_perf_ui = "0.5.0"
|
iyes_perf_ui = "0.5.0"
|
||||||
|
rand = "0.9.1"
|
||||||
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|||||||
87
src/main.rs
87
src/main.rs
@ -1,15 +1,24 @@
|
|||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use bevy::asset::RenderAssetUsages;
|
use bevy::asset::RenderAssetUsages;
|
||||||
use bevy::pbr::wireframe::{WireframeConfig, WireframePlugin};
|
use bevy::pbr::wireframe::{WireframeConfig, WireframePlugin};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::render::mesh::{PrimitiveTopology, VertexAttributeValues};
|
use bevy::render::mesh::{PrimitiveTopology, VertexAttributeValues};
|
||||||
|
use bevy::render::primitives::Aabb;
|
||||||
|
use bevy::time::common_conditions::on_timer;
|
||||||
|
|
||||||
mod camera;
|
mod camera;
|
||||||
mod debugging;
|
mod debugging;
|
||||||
mod tree;
|
mod tree;
|
||||||
mod tree_rendering;
|
|
||||||
mod tree_generation;
|
mod tree_generation;
|
||||||
|
mod tree_rendering;
|
||||||
|
|
||||||
|
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
enum RunningGrowthState {
|
||||||
|
Stopped,
|
||||||
|
Running,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
@ -17,14 +26,76 @@ fn main() {
|
|||||||
.add_plugins(debugging::DebuggingPlugin)
|
.add_plugins(debugging::DebuggingPlugin)
|
||||||
.add_plugins(camera::CameraPlugin)
|
.add_plugins(camera::CameraPlugin)
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
// .add_plugins(WireframePlugin::default())
|
// .add_systems(Update, update_tree)
|
||||||
// .insert_resource(WireframeConfig {
|
.add_systems(Update, update_tree_grow_state)
|
||||||
// default_color: Color::BLACK,
|
.add_systems(
|
||||||
// global: true, // or false if you want per-object control
|
Update,
|
||||||
// })
|
update_tree
|
||||||
|
.run_if(in_state(RunningGrowthState::Running))
|
||||||
|
.run_if(on_timer(Duration::from_secs_f32(0.2))),
|
||||||
|
)
|
||||||
|
.add_plugins(WireframePlugin::default())
|
||||||
|
.insert_resource(WireframeConfig {
|
||||||
|
default_color: Color::BLACK,
|
||||||
|
global: true, // or false if you want per-object control
|
||||||
|
})
|
||||||
|
.insert_state(RunningGrowthState::Stopped)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_tree_grow_state(
|
||||||
|
keyboard: Res<ButtonInput<KeyCode>>,
|
||||||
|
state: Res<State<RunningGrowthState>>,
|
||||||
|
mut next_state: ResMut<NextState<RunningGrowthState>>,
|
||||||
|
) {
|
||||||
|
if keyboard.just_pressed(KeyCode::KeyR) {
|
||||||
|
if *state.get() == RunningGrowthState::Running {
|
||||||
|
next_state.set(RunningGrowthState::Stopped);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if *state.get() == RunningGrowthState::Stopped {
|
||||||
|
next_state.set(RunningGrowthState::Running);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_tree(
|
||||||
|
mut query: Query<(Entity, &mut tree::Tree, &mut Mesh3d)>,
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
) {
|
||||||
|
// info!("{:?}", *state.get());
|
||||||
|
// if keyboard.just_pressed(KeyCode::KeyR) {
|
||||||
|
// if *state.get() == RunningGrowthState::Running {
|
||||||
|
// next_state.set(RunningGrowthState::Stopped);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if *state.get() == RunningGrowthState::Stopped {
|
||||||
|
// next_state.set(RunningGrowthState::Running);
|
||||||
|
// }
|
||||||
|
// } else if *state.get() == RunningGrowthState::Stopped {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
info!("here");
|
||||||
|
for (entity, mut tree, mut mesh) in query {
|
||||||
|
tree_generation::grow_tree(tree.as_mut());
|
||||||
|
|
||||||
|
let t1 = SystemTime::now();
|
||||||
|
let tree_mesh = tree_rendering::generate_tree(&tree);
|
||||||
|
let t2 = SystemTime::now();
|
||||||
|
meshes.remove(&mesh.0);
|
||||||
|
mesh.0 = meshes.add(tree_mesh);
|
||||||
|
let t3 = SystemTime::now();
|
||||||
|
commands.entity(entity).remove::<Aabb>();
|
||||||
|
let t4 = SystemTime::now();
|
||||||
|
let generate_duration = t2.duration_since(t1).unwrap();
|
||||||
|
let mesh_duration = t3.duration_since(t2).unwrap();
|
||||||
|
let aabb_duration = t4.duration_since(t3).unwrap();
|
||||||
|
info!("{:?} {:?} {:?}", generate_duration, mesh_duration, aabb_duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
@ -71,7 +142,7 @@ fn setup(
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
let tree = initial_tree();
|
let tree = tree_generation::initial_tree();
|
||||||
let tree_mesh = tree_rendering::generate_tree(&tree);
|
let tree_mesh = tree_rendering::generate_tree(&tree);
|
||||||
|
|
||||||
// let normal_material = materials.add(Color::srgb_u8(255, 0, 0));
|
// let normal_material = materials.add(Color::srgb_u8(255, 0, 0));
|
||||||
@ -92,7 +163,7 @@ fn setup(
|
|||||||
// alpha_mode: AlphaMode::Blend,
|
// alpha_mode: AlphaMode::Blend,
|
||||||
// ..default()
|
// ..default()
|
||||||
// })),
|
// })),
|
||||||
Transform::from_xyz(0.0, 0.5, 0.0),
|
// Transform::from_xyz(0.0, 0.5, 0.0),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
src/tree.rs
30
src/tree.rs
@ -15,6 +15,7 @@ use bevy::{
|
|||||||
pub struct Tree {
|
pub struct Tree {
|
||||||
pub age: f32,
|
pub age: f32,
|
||||||
pub nodes: HashMap<u32, TreeNode>,
|
pub nodes: HashMap<u32, TreeNode>,
|
||||||
|
next_node_id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@ -42,11 +43,26 @@ impl Default for Tree {
|
|||||||
Tree {
|
Tree {
|
||||||
age: 0.,
|
age: 0.,
|
||||||
nodes: HashMap::new(),
|
nodes: HashMap::new(),
|
||||||
|
next_node_id: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tree {
|
impl Tree {
|
||||||
|
pub fn add_node(&mut self, mut tree_node: TreeNode) {
|
||||||
|
tree_node.id = self.next_node_id;
|
||||||
|
self.nodes.insert(self.next_node_id, tree_node);
|
||||||
|
self.next_node_id += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_node(&self, id: u32) -> Option<&TreeNode> {
|
||||||
|
self.nodes.get(&id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_node_mut(&mut self, id: u32) -> Option<&mut TreeNode> {
|
||||||
|
self.nodes.get_mut(&id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_accumulated_transform(&self, id: u32) -> Transform {
|
pub fn get_accumulated_transform(&self, id: u32) -> Transform {
|
||||||
let mut transform = Transform::IDENTITY;
|
let mut transform = Transform::IDENTITY;
|
||||||
let mut curr_node = &self.nodes[&id];
|
let mut curr_node = &self.nodes[&id];
|
||||||
@ -55,14 +71,13 @@ impl Tree {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
curr_node = &self.nodes[&curr_node.parent_id];
|
curr_node = &self.nodes[&curr_node.parent_id];
|
||||||
info!("transform: {:?}", curr_node);
|
// info!("transform: {:?}", curr_node);
|
||||||
transform = curr_node.get_transform() * transform;
|
transform = curr_node.get_transform() * transform;
|
||||||
}
|
}
|
||||||
transform
|
transform
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl TreeNode {
|
impl TreeNode {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
TreeNode {
|
TreeNode {
|
||||||
@ -76,6 +91,10 @@ impl TreeNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_root(&self) -> bool {
|
||||||
|
return self.id == self.parent_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_transform(&self) -> Transform {
|
pub fn get_transform(&self) -> Transform {
|
||||||
let mut transform = Transform::IDENTITY;
|
let mut transform = Transform::IDENTITY;
|
||||||
transform.rotate_local_y(self.rotation);
|
transform.rotate_local_y(self.rotation);
|
||||||
@ -104,6 +123,13 @@ impl TreeNode {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_meristem(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self.kind,
|
||||||
|
TreeNodeKind::EndMeristem | TreeNodeKind::EndBranch
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_extension(&self) -> bool {
|
pub fn is_extension(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self.kind,
|
self.kind,
|
||||||
|
|||||||
@ -0,0 +1,92 @@
|
|||||||
|
use std::f32::consts::TAU;
|
||||||
|
|
||||||
|
use crate::tree::{self, Tree, TreeNode, TreeNodeKind};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use rand::{distr::StandardUniform, Rng};
|
||||||
|
|
||||||
|
pub fn initial_tree() -> tree::Tree {
|
||||||
|
let mut tree = tree::Tree::default();
|
||||||
|
tree.add_node(tree::TreeNode {
|
||||||
|
id: 0,
|
||||||
|
kind: tree::TreeNodeKind::EndMeristem,
|
||||||
|
parent_id: 0,
|
||||||
|
radius: 0.05,
|
||||||
|
offset: 0.5,
|
||||||
|
angle: 0.,
|
||||||
|
rotation: 0.,
|
||||||
|
});
|
||||||
|
tree
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_MERISTEM_LENGTH: f32 = 1.;
|
||||||
|
|
||||||
|
pub fn grow_tree(tree: &mut tree::Tree) {
|
||||||
|
let meristems_ids: Vec<u32> = tree
|
||||||
|
.nodes
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, n)| n.is_meristem())
|
||||||
|
.map(|(i, _)| *i)
|
||||||
|
.collect();
|
||||||
|
for meristem_id in meristems_ids {
|
||||||
|
grow_meristem(meristem_id, tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grow_meristem(node_id: u32, tree: &mut Tree) {
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
|
||||||
|
let accumulated_transform = tree.get_accumulated_transform(node_id);
|
||||||
|
|
||||||
|
let grow_factor = 0.001;
|
||||||
|
let node = tree.get_node_mut(node_id).unwrap();
|
||||||
|
node.offset += 0.1;
|
||||||
|
|
||||||
|
let original_rotation = node.get_rotation().rotation;
|
||||||
|
let original_vector = original_rotation * Vec3::Y;
|
||||||
|
|
||||||
|
let accumulated_rotation_inverse = accumulated_transform.rotation.inverse();
|
||||||
|
let up_vector = accumulated_rotation_inverse * Vec3::Y;
|
||||||
|
let towards_up_vector = up_vector - original_vector;
|
||||||
|
|
||||||
|
let random_angle = rng.random::<f32>() * TAU;
|
||||||
|
let random_vector = Vec3::new(random_angle.cos(), 0.0, random_angle.sin());
|
||||||
|
|
||||||
|
let random_magnitude = rng.sample::<f32, StandardUniform>(StandardUniform) - 0.5;
|
||||||
|
let new_vector = (original_vector + towards_up_vector*0.05 + random_vector*random_magnitude*0.1).normalize();
|
||||||
|
|
||||||
|
let r = new_vector.length();
|
||||||
|
let inclination = (new_vector.y / r).acos();
|
||||||
|
let azimuth = new_vector.x.atan2(new_vector.z);
|
||||||
|
|
||||||
|
node.angle = inclination;
|
||||||
|
node.rotation = azimuth;
|
||||||
|
|
||||||
|
if node.offset > MAX_MERISTEM_LENGTH + node.radius {
|
||||||
|
let new_meristem_length = node.offset - MAX_MERISTEM_LENGTH;
|
||||||
|
let new_meristem_radius = node.radius * 0.5;
|
||||||
|
node.offset -= new_meristem_length;
|
||||||
|
node.kind = TreeNodeKind::Extension;
|
||||||
|
let parent_id = node.id;
|
||||||
|
let angle = node.angle;
|
||||||
|
let rotation = node.rotation;
|
||||||
|
tree.add_node(TreeNode {
|
||||||
|
id: 0,
|
||||||
|
kind: TreeNodeKind::EndMeristem,
|
||||||
|
parent_id: parent_id,
|
||||||
|
radius: new_meristem_radius,
|
||||||
|
offset: new_meristem_length,
|
||||||
|
angle: angle,
|
||||||
|
rotation: rotation,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut growth_node_id = node_id;
|
||||||
|
loop {
|
||||||
|
let node = tree.get_node_mut(growth_node_id).unwrap();
|
||||||
|
node.radius = (node.radius.powi(2) + grow_factor).sqrt();
|
||||||
|
if node.is_root() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
growth_node_id = node.parent_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user