use bevy::math::Vec3;
use crate::foliage::{distributed_leaf_count, node_leaf_scale};
use crate::mesh::branch::{collar_geometry, primary_root, root_segment_emitted};
use crate::mesh::trunk::{TrunkRing, trunk_cone_lift, trunk_pieces, trunk_rings};
use crate::params::{FoliagePlacement, TreeParams};
use crate::pipeline::aged_skeleton;
use crate::skeleton::{Node, NodeKind, Skeleton};
pub(crate) const WOOD: u32 = 1;
pub(crate) const COLLAR: u32 = 2;
pub(crate) const BASE: u32 = 4;
pub(crate) const BASE_COLLAR: u32 = 8;
pub(crate) const TOP_CAP: u32 = 1;
pub(crate) const CONE: u32 = 2;
pub(crate) const HEADER_VEC4S: usize = 1;
pub(crate) const NODE_VEC4S: usize = 5;
pub(crate) const RING_VEC4S: usize = 2;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub(crate) struct NodeState {
pub start: Vec3,
pub length: f32,
pub base_radius: f32,
pub tip_radius: f32,
pub leaf_scale: f32,
pub leaf_count: f32,
pub collar_start: Vec3,
pub collar_start_radius: f32,
pub collar_end: Vec3,
pub collar_end_radius: f32,
pub root: Vec3,
pub flags: u32,
}
impl NodeState {
fn unborn(at: Vec3) -> Self {
Self {
start: at,
leaf_count: -1.0,
..Self::default()
}
}
fn of(node: &Node, params: &TreeParams) -> Self {
let mut state = Self {
start: node.start,
length: node.length,
base_radius: node.base_radius,
tip_radius: node.tip_radius,
leaf_count: -1.0,
..Self::default()
};
if node.kind == NodeKind::Trunk {
return state;
}
if params.foliage.enabled && node.length > 0.0 {
state.leaf_scale = node_leaf_scale(node, ¶ms.growth);
if params.foliage.placement == FoliagePlacement::AllBranches {
state.leaf_count = distributed_leaf_count(¶ms.foliage, node.length, node.height_ratio) as f32;
}
}
if node.length <= 1e-4 || node.base_radius <= 0.0 {
return state;
}
state.flags |= WOOD;
if node.kind != NodeKind::Primary {
return state;
}
let root = primary_root(node);
let base_exists = |at: Vec3| root.is_none_or(|root| root_segment_emitted(root, at, node.direction));
state.root = root.unwrap_or(node.start);
if base_exists(node.start) {
state.flags |= BASE;
}
if params.mesh.collar_enabled {
let collar = collar_geometry(node, node.base_radius * params.mesh.collar_length);
state.collar_start = collar.start;
state.collar_start_radius = collar.start_radius;
state.collar_end = collar.end;
state.collar_end_radius = collar.end_radius;
state.flags |= COLLAR;
if base_exists(collar.start) {
state.flags |= BASE_COLLAR;
}
}
state
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct GrowthState {
pub nodes: Vec<NodeState>,
pub rings: Vec<TrunkRing>,
pub trunk_flags: u32,
pub cone_lift: f32,
pub trunk_progress: f32,
present: Vec<bool>,
}
impl GrowthState {
pub fn update(&mut self, full: &Skeleton, params: &TreeParams, age: f32) {
let aged = aged_skeleton(full, params, age);
let count = full.nodes.len();
self.present.clear();
self.present.resize(count, false);
self.nodes.clear();
self.nodes.resize(count, NodeState::default());
for node in &aged.nodes {
let id = node.id as usize;
self.nodes[id] = NodeState::of(node, params);
self.present[id] = true;
}
for id in 1..count {
if !self.present[id] {
let parent = full.nodes[id].parent.map_or(0, |p| p as usize);
self.nodes[id] = NodeState::unborn(self.nodes[parent].start);
}
}
let trunk = &aged.nodes[0];
trunk_rings(¶ms.trunk, trunk, params.seed, &mut self.rings);
let pieces = trunk_pieces(¶ms.trunk, trunk);
self.trunk_flags = if pieces.top_cap { TOP_CAP } else { 0 } | if pieces.cone { CONE } else { 0 };
self.cone_lift = trunk_cone_lift(¶ms.trunk, trunk);
self.trunk_progress = trunk.progress;
}
pub fn vec4_len(&self) -> usize {
HEADER_VEC4S + NODE_VEC4S * self.nodes.len() + RING_VEC4S * self.rings.len()
}
pub fn encode(&self, out: &mut [[f32; 4]]) {
assert_eq!(out.len(), self.vec4_len(), "growth buffer has the wrong size");
let v = |v: Vec3, w: f32| [v.x, v.y, v.z, w];
out[0] = [
self.nodes.len() as f32,
self.rings.len() as f32,
self.trunk_flags as f32,
self.cone_lift,
];
let (nodes, rings) = out[HEADER_VEC4S..].split_at_mut(NODE_VEC4S * self.nodes.len());
for (slot, node) in nodes.as_chunks_mut::<NODE_VEC4S>().0.iter_mut().zip(&self.nodes) {
slot[0] = v(node.start, node.length);
slot[1] = [node.base_radius, node.tip_radius, node.leaf_scale, node.leaf_count];
slot[2] = v(node.collar_start, node.collar_start_radius);
slot[3] = v(node.collar_end, node.collar_end_radius);
slot[4] = v(node.root, node.flags as f32);
}
for (slot, ring) in rings.as_chunks_mut::<RING_VEC4S>().0.iter_mut().zip(&self.rings) {
slot[0] = v(ring.center, ring.radius);
slot[1] = [ring.bulge, ring.twist, 0.0, 0.0];
}
}
}