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",
|
||||
"itertools 0.14.0",
|
||||
"libm",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"rand_distr",
|
||||
"serde",
|
||||
"smallvec",
|
||||
@ -2170,7 +2170,7 @@ checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"libm",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -3451,8 +3451,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_chacha 0.3.1",
|
||||
"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]]
|
||||
@ -3462,7 +3472,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"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]]
|
||||
@ -3474,6 +3494,15 @@ dependencies = [
|
||||
"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]]
|
||||
name = "rand_distr"
|
||||
version = "0.4.3"
|
||||
@ -3481,7 +3510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4111,6 +4140,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"iyes_perf_ui",
|
||||
"rand 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -6,6 +6,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
bevy = "0.16"
|
||||
iyes_perf_ui = "0.5.0"
|
||||
rand = "0.9.1"
|
||||
|
||||
|
||||
[profile.dev]
|
||||
|
||||
87
src/main.rs
87
src/main.rs
@ -1,15 +1,24 @@
|
||||
use std::f32::consts::PI;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use bevy::asset::RenderAssetUsages;
|
||||
use bevy::pbr::wireframe::{WireframeConfig, WireframePlugin};
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::mesh::{PrimitiveTopology, VertexAttributeValues};
|
||||
use bevy::render::primitives::Aabb;
|
||||
use bevy::time::common_conditions::on_timer;
|
||||
|
||||
mod camera;
|
||||
mod debugging;
|
||||
mod tree;
|
||||
mod tree_rendering;
|
||||
mod tree_generation;
|
||||
mod tree_rendering;
|
||||
|
||||
#[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
enum RunningGrowthState {
|
||||
Stopped,
|
||||
Running,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
@ -17,14 +26,76 @@ fn main() {
|
||||
.add_plugins(debugging::DebuggingPlugin)
|
||||
.add_plugins(camera::CameraPlugin)
|
||||
.add_systems(Startup, setup)
|
||||
// .add_plugins(WireframePlugin::default())
|
||||
// .insert_resource(WireframeConfig {
|
||||
// default_color: Color::BLACK,
|
||||
// global: true, // or false if you want per-object control
|
||||
// })
|
||||
// .add_systems(Update, update_tree)
|
||||
.add_systems(Update, update_tree_grow_state)
|
||||
.add_systems(
|
||||
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();
|
||||
}
|
||||
|
||||
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(
|
||||
mut commands: Commands,
|
||||
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 normal_material = materials.add(Color::srgb_u8(255, 0, 0));
|
||||
@ -92,7 +163,7 @@ fn setup(
|
||||
// alpha_mode: AlphaMode::Blend,
|
||||
// ..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 age: f32,
|
||||
pub nodes: HashMap<u32, TreeNode>,
|
||||
next_node_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@ -42,11 +43,26 @@ impl Default for Tree {
|
||||
Tree {
|
||||
age: 0.,
|
||||
nodes: HashMap::new(),
|
||||
next_node_id: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
let mut transform = Transform::IDENTITY;
|
||||
let mut curr_node = &self.nodes[&id];
|
||||
@ -55,14 +71,13 @@ impl Tree {
|
||||
break;
|
||||
}
|
||||
curr_node = &self.nodes[&curr_node.parent_id];
|
||||
info!("transform: {:?}", curr_node);
|
||||
// info!("transform: {:?}", curr_node);
|
||||
transform = curr_node.get_transform() * transform;
|
||||
}
|
||||
transform
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl TreeNode {
|
||||
pub fn new() -> Self {
|
||||
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 {
|
||||
let mut transform = Transform::IDENTITY;
|
||||
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 {
|
||||
matches!(
|
||||
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