use core::f32;
use std::f32::consts::PI;
use bevy::mesh::Indices;
use bevy::mesh::PrimitiveTopology;
use bevy::{asset::RenderAssetUsages, prelude::*};
use fastrand::Rng;
use crate::{enums::TreeType, settings::TreeMeshSettings};
use crate::errors::IndicesOverflowError;
#[derive(Debug, Clone)]
struct BranchGenState {
pub origin: Vec3,
pub orientation: Quat,
pub length: f32,
pub start_radius: f32,
pub taper: f32,
pub twist: f32,
pub gnarliness: f32,
pub level: usize,
pub recursion_count: usize,
pub sections: usize,
pub segments: usize,
}
#[derive(Debug, Clone)]
struct SectionData {
pub origin: Vec3,
pub orientation: Quat,
pub radius: f32
}
#[cfg(not(feature = "u32_indices"))]
#[derive(Debug, Default)]
struct MeshAttributes {
positions: Vec<[f32; 3]>,
normals: Vec<[f32; 3]>,
uvs: Vec<[f32; 2]>,
indices: Vec<u16>
}
#[cfg(feature = "u32_indices")]
#[derive(Debug, Default)]
struct MeshAttributes {
positions: Vec<[f32; 3]>,
normals: Vec<[f32; 3]>,
uvs: Vec<[f32; 2]>,
indices: Vec<u32>
}
pub fn generate_tree_meshes(settings: &TreeMeshSettings, rng: &mut Rng) -> Result<(Mesh, Mesh), BevyError> {
let state: BranchGenState = BranchGenState {
origin: Vec3::ZERO,
orientation: Quat::IDENTITY,
length: settings.branch.length[0],
start_radius: settings.branch.trunk_base_radius,
taper: settings.branch.taper[0],
twist: settings.branch.twist[0],
gnarliness: settings.branch.gnarliness[0],
level: 0,
recursion_count: 0,
sections: settings.branch.sections[0] as usize,
segments: settings.branch.segments[0] as usize,
};
generate_branches_internal(settings, state, rng)
}
fn generate_branches_internal(settings: &TreeMeshSettings, state: BranchGenState, rng: &mut Rng) -> Result<(Mesh, Mesh), BevyError> {
let mut branches_attributes: MeshAttributes = MeshAttributes::default();
let mut leaves_attributes: MeshAttributes = MeshAttributes::default();
recurse_a_branch(settings, state, rng, &mut branches_attributes, &mut leaves_attributes)?;
let mut branches_mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD);
branches_mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, branches_attributes.positions);
branches_mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, branches_attributes.normals);
branches_mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, branches_attributes.uvs);
#[cfg(not(feature = "u32_indices"))]
branches_mesh.insert_indices(Indices::U16(branches_attributes.indices));
#[cfg(feature = "u32_indices")]
branches_mesh.insert_indices(Indices::U32(branches_attributes.indices));
branches_mesh.generate_tangents()?;
let mut leaves_mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD);
leaves_mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, leaves_attributes.positions);
leaves_mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, leaves_attributes.normals);
leaves_mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, leaves_attributes.uvs);
#[cfg(not(feature = "u32_indices"))]
leaves_mesh.insert_indices(Indices::U16(leaves_attributes.indices));
#[cfg(feature = "u32_indices")]
leaves_mesh.insert_indices(Indices::U32(leaves_attributes.indices));
leaves_mesh.generate_tangents()?;
Ok((branches_mesh, leaves_mesh))
}
#[allow(clippy::too_many_arguments)]
fn recurse_a_branch(
settings: &TreeMeshSettings,
state: BranchGenState,
rng: &mut Rng,
branches_attributes: &mut MeshAttributes,
leaves_attributes: &mut MeshAttributes
) -> Result<(), BevyError>
{
#[cfg(not(feature = "u32_indices"))]
let indices_start: u16 = branches_attributes.positions.len() as u16;
#[cfg(feature = "u32_indices")]
let indices_start: u32 = branches_attributes.positions.len() as u32;
let approx_amount_of_indices_of_this_branch: usize = state.sections * state.segments * 6;
#[cfg(not(feature = "u32_indices"))]
if branches_attributes.indices.len() >= (u16::MAX as usize - approx_amount_of_indices_of_this_branch) {
return Err(IndicesOverflowError.into());
}
#[cfg(feature = "u32_indices")]
if branches_attributes.indices.len() >= (u32::MAX as usize - approx_amount_of_indices_of_this_branch) {
return Err(IndicesOverflowError.into());
}
let mut sections: Vec<SectionData> = Vec::with_capacity(state.sections);
let target_pieces: f32 = (1..=(settings.branch.levels as usize+1)).sum::<usize>() as f32;
let factor_for_length: f32 = if state.level > 0 {1.0} else {
match settings.tree_type {
TreeType::Deciduous => (settings.branch.levels as usize - state.recursion_count + 1) as f32 / target_pieces,
TreeType::Evergreen => 1.0,
}
};
let section_length = state.length / state.sections as f32 * factor_for_length;
let mut section_orientation = state.orientation;
let mut section_origin = state.origin;
let mut section_radius = state.start_radius;
let branch_force_dir = if settings.branch.force.direction.length_squared() >= f32::EPSILON {
settings.branch.force.direction.normalize()
} else {
Vec3::Y
};
let branch_force_quat = Quat::from_rotation_arc(Vec3::Y, branch_force_dir);
let taper_amount_per_section = match settings.tree_type {
TreeType::Deciduous => f32::powf(1.0 - state.taper.clamp(0.0, 0.9999), (1.0/state.sections as f32) / (f32::from(settings.branch.levels) + 1.0)),
TreeType::Evergreen => f32::powf(1.0 - state.taper.clamp(0.0, 0.9999), 1.0/state.sections as f32),
};
for section_counter in 0..=state.sections {
if section_counter == state.sections && !((state.level == 0) && matches!(settings.tree_type, TreeType::Deciduous)) {
section_radius = f32::EPSILON;
}
let mut first_pos = Vec3::ZERO;
let mut first_nrm = Vec3::ZERO;
let mut first_v = 0.0;
for segment_counter in 0..state.segments {
let angle = (2.0 * PI * segment_counter as f32) / state.segments as f32;
let (sin, cos) = angle.sin_cos();
let local_pos = Vec3::new(cos * section_radius, 0.0, sin * section_radius);
let local_normal = Vec3::new(cos, 0.0, sin);
let vertex = (section_orientation * local_pos) + section_origin;
let normal = (section_orientation * local_normal).normalize();
let u = segment_counter as f32 / state.segments as f32;
let v = if section_counter % 2 == 0 { 0.0 } else { 1.0 };
if segment_counter == 0 {
first_pos = vertex;
first_nrm = normal;
first_v = v;
}
branches_attributes.positions.push(vertex.to_array());
branches_attributes.normals.push(normal.to_array());
branches_attributes.uvs.push([u,v]);
}
branches_attributes.positions.push(first_pos.to_array());
branches_attributes.normals.push(first_nrm.to_array());
branches_attributes.uvs.push([1.0, first_v]);
sections.push(SectionData {
origin: section_origin,
orientation: section_orientation,
radius: section_radius
});
if section_counter < state.sections {
let gn = state.gnarliness * 0.4 / section_radius.sqrt(); let dx = (rng.f32() - 0.5) * gn;
let dz = (rng.f32() - 0.5) * gn;
let q_gnarl = Quat::from_euler(EulerRot::XYZ, dx, 0.0, dz);
let q_twist = Quat::from_axis_angle(Vec3::Y, state.twist);
section_orientation = (q_gnarl * section_orientation) * q_twist;
let radius_factor = 1.0 - (section_radius / settings.branch.force.radius_cutoff).clamp(0.0, 1.0);
let strength_per_radius = (settings.branch.force.strength * radius_factor / 2.0).clamp(0.0, 1.0); section_orientation = section_orientation.slerp(branch_force_quat, strength_per_radius);
section_radius *= taper_amount_per_section;
let up = section_orientation * Vec3::Y;
section_origin += up * section_length;
}
}
#[cfg(not(feature = "u32_indices"))]
{
let ring_stride: u16 = state.segments as u16 + 1;
for i in 0..state.sections as u16 {
for j in 0..state.segments as u16 {
let a: u16 = i * ring_stride + j + indices_start;
let b: u16 = i * ring_stride + (j + 1) + indices_start;
let c: u16 = a + ring_stride;
let d: u16 = b + ring_stride;
branches_attributes.indices.extend_from_slice(&[a, c, b, b, c, d]);
}
}
}
#[cfg(feature = "u32_indices")]
{
let ring_stride: u32 = state.segments as u32 + 1;
for i in 0..state.sections as u32 {
for j in 0..state.segments as u32 {
let a: u32 = i * ring_stride + j + indices_start;
let b: u32 = i * ring_stride + (j + 1) + indices_start;
let c: u32 = a + ring_stride;
let d: u32 = b + ring_stride;
branches_attributes.indices.extend_from_slice(&[a, c, b, b, c, d]);
}
}
}
if matches!(settings.tree_type, TreeType::Deciduous) && state.level == 0 {
if state.recursion_count < settings.branch.levels as usize {
let additional_trunk_part = BranchGenState {
origin: section_origin,
orientation: section_orientation,
length: state.length,
start_radius: section_radius,
taper: state.taper,
twist: state.twist,
gnarliness: state.gnarliness,
level: state.level,
recursion_count: state.recursion_count + 1,
sections: state.sections,
segments: state.segments,
};
recurse_a_branch(settings, additional_trunk_part, rng, branches_attributes, leaves_attributes)?;
}
else {
generate_leaf(settings, section_origin, section_orientation, rng, leaves_attributes)?;
}
}
if state.recursion_count == settings.branch.levels as usize {
generate_leaves(§ions, settings, rng, leaves_attributes)?;
}
else {
for child_branch_state in generate_child_branches(
settings.branch.children[state.recursion_count],
state.recursion_count + 1,
§ions,
settings,
rng
) {
recurse_a_branch(settings, child_branch_state, rng, branches_attributes, leaves_attributes)?;
}
}
Ok(())
}
fn generate_child_branches (
count: u8,
level: usize,
parent_sections: &[SectionData],
settings: &TreeMeshSettings,
rng: &mut Rng,
) -> Vec<BranchGenState> {
if count == 0 || parent_sections.is_empty(){
return Vec::new();
}
let radial_offset: f32 = rng.f32();
let section_count_minus_one: usize = parent_sections.len().saturating_sub(1);
let mut out = Vec::with_capacity(count as usize);
for i in 0..count {
let child_start_factor = f32::lerp(settings.branch.start[level].clamp(0.0, 1.0), 1.0, rng.f32());
let child_branch_pos = child_start_factor * section_count_minus_one as f32;
let section_index = child_branch_pos.floor() as usize;
let branch_height_factor = (child_branch_pos - section_index as f32).clamp(0.0, 1.0);
let section_a_index = section_index;
let section_b_index = (section_index + 1).min(section_count_minus_one);
let section_a = &parent_sections[section_a_index];
let section_b = &parent_sections[section_b_index];
let child_branch_origin = section_a.origin.lerp(section_b.origin, branch_height_factor);
let radius_setting = settings.branch.radius_factor[level];
let parent_radius = f32::lerp(section_a.radius, section_b.radius, branch_height_factor);
let child_branch_radius = radius_setting * parent_radius;
let parent_orientation = section_b.orientation.slerp(section_a.orientation, branch_height_factor);
let radial_angle = 2.0 * std::f32::consts::PI * (radial_offset + (i as f32) / (count as f32));
let angle_rad = settings.branch.angle[level].to_radians();
let q1 = Quat::from_axis_angle(Vec3::X, angle_rad);
let q2 = Quat::from_axis_angle(Vec3::Y, radial_angle);
let child_quat = parent_orientation * q2 * q1;
let mut child_len = settings.branch.length[level];
if settings.tree_type == TreeType::Evergreen {
child_len *= 1.0 - child_start_factor;
}
out.push(BranchGenState {
origin: child_branch_origin,
orientation: child_quat,
length: child_len,
start_radius: child_branch_radius,
level,
recursion_count: level,
taper: settings.branch.taper[level],
twist: settings.branch.twist[level],
gnarliness: settings.branch.gnarliness[level],
sections: settings.branch.sections[level].into(),
segments: settings.branch.segments[level].into()
});
}
out
}
fn generate_leaves(
sections: &[SectionData],
settings: &TreeMeshSettings,
rng: &mut Rng,
leaves_attributes: &mut MeshAttributes
) -> Result<(), BevyError>
{
let approx_amount_of_indices_of_this_leaf: usize = settings.leaves.count as usize * 6;
#[cfg(not(feature = "u32_indices"))]
if leaves_attributes.indices.len() >= (u16::MAX as usize - approx_amount_of_indices_of_this_leaf) {
return Err(IndicesOverflowError.into());
}
#[cfg(feature = "u32_indices")]
if leaves_attributes.indices.len() >= (u32::MAX as usize - approx_amount_of_indices_of_this_leaf) {
return Err(IndicesOverflowError.into());
}
let radial_offset: f32 = rng.f32();
let section_count_minus_one: usize = sections.len().saturating_sub(1);
for i in 0..settings.leaves.count {
let leaf_start = f32::lerp(settings.leaves.start.clamp(0.0, 1.0), 1.0, rng.f32());
let leaf_pos = leaf_start * section_count_minus_one as f32;
let section_index = leaf_pos.floor() as usize;
let leaf_height_factor = (leaf_pos - section_index as f32).clamp(0.0, 1.0);
let section_a_index = section_index;
let section_b_index = (section_index + 1).min(section_count_minus_one);
let section_a = §ions[section_a_index];
let section_b = §ions[section_b_index];
let leaf_origin = section_a.origin.lerp(section_b.origin, leaf_height_factor);
let parent_orientation = section_b.orientation.slerp(section_a.orientation, leaf_height_factor);
let radial_angle = 2.0 * std::f32::consts::PI * (radial_offset + (i as f32) / (settings.leaves.count as f32));
let angle_rad = settings.leaves.angle.to_radians();
let q1 = Quat::from_axis_angle(Vec3::X, angle_rad);
let q2 = Quat::from_axis_angle(Vec3::Y, radial_angle);
let child_quat = parent_orientation * q2 * q1;
generate_leaf(settings, leaf_origin, child_quat, rng, leaves_attributes)?;
}
Ok(())
}
fn generate_leaf(
settings: &TreeMeshSettings,
origin: Vec3,
orientation: Quat,
rng: &mut Rng,
leaves_attributes: &mut MeshAttributes
) -> Result<(), BevyError>
{
#[cfg(not(feature = "u32_indices"))]
let mut indices_start: u16 = leaves_attributes.positions.len() as u16;
#[cfg(feature = "u32_indices")]
let mut indices_start: u32 = leaves_attributes.positions.len() as u32;
let leaf_size_variance = (2.0 * rng.f32() - 1.0) * settings.leaves.size_variance.max(0.0);
let leaf_size = settings.leaves.size * (1.0 + leaf_size_variance);
let leaf_size_half = leaf_size / 2.0;
let rotations: &[f32] = match settings.leaves.leaf_billboard {
crate::enums::LeafBillboard::Single => &[0.0],
crate::enums::LeafBillboard::Double => &[0.0, f32::consts::FRAC_PI_2],
};
for rotation in rotations.iter() {
let leaf_orientation = orientation * Quat::from_euler(EulerRot::XYX, 0.0, *rotation, 0.0);
let vertices: Vec<[f32;3]> = [
Vec3::new(-leaf_size_half, leaf_size, 0.0),
Vec3::new(-leaf_size_half, 0.0, 0.0),
Vec3::new(leaf_size_half, 0.0, 0.0),
Vec3::new(leaf_size_half, leaf_size, 0.0),
].into_iter().map(|v| (leaf_orientation * v + origin).to_array()).collect();
leaves_attributes.positions.extend_from_slice(&vertices);
let normal: [f32;3] = (leaf_orientation * Vec3::new(0.0, 0.0, 1.0)).to_array();
leaves_attributes.normals.push(normal);
leaves_attributes.normals.push(normal);
leaves_attributes.normals.push(normal);
leaves_attributes.normals.push(normal);
leaves_attributes.uvs.extend_from_slice(&[[0.0, 0.0],[0.0, 1.0],[1.0, 1.0],[1.0, 0.0]]);
leaves_attributes.indices.extend_from_slice(&[indices_start, indices_start+1, indices_start+2, indices_start, indices_start+2, indices_start+3]);
indices_start += 4;
}
Ok(())
}