diff --git a/src/main.rs b/src/main.rs index a950107..2fc662c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,15 @@ use std::f32::consts::PI; use bevy::asset::RenderAssetUsages; +use bevy::pbr::wireframe::{WireframeConfig, WireframePlugin}; use bevy::prelude::*; use bevy::render::mesh::{PrimitiveTopology, VertexAttributeValues}; -use bevy::window::PresentMode; -use tree::{ConeSegment, TreeNode}; mod camera; mod debugging; mod tree; mod tree_rendering; +mod tree_generation; fn main() { App::new() @@ -17,6 +17,11 @@ 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 + // }) .run(); } @@ -64,230 +69,29 @@ fn setup( rotation: Quat::from_rotation_x(-PI / 4.) * Quat::from_rotation_y(-PI / 4.), ..default() }, - // Transform::from_xyz(0.0, 16.0, 0.0), )); - // let cone_mesh = tree::generate_truncated_cone(1., 2., 2., 200); - - let mut cone_mesh = Mesh::new( - PrimitiveTopology::TriangleList, - RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD, - ); - // tree::append_connected_truncated_cones_to_mesh( - // &mut cone_mesh, - // &[ - // tree::ConeSegment { - // radius: 2., - // offset: 0., - // }, - // tree::ConeSegment { - // radius: 1., - // offset: 2., - // }, - // tree::ConeSegment { - // radius: 1., - // offset: 1., - // }, - // tree::ConeSegment { - // radius: 2., - // offset: 2., - // }, - // tree::ConeSegment { - // radius: 2., - // offset: 0.5, - // }, - // tree::ConeSegment { - // radius: 1., - // offset: 2., - // }, - // tree::ConeSegment { - // radius: 0., - // offset: 1., - // }, - // ], - // 20, - // ); - // tree::append_connected_truncated_cones_to_mesh( - // &mut cone_mesh, - // &[ - // tree::ConeSegment { - // radius: 0.5, - // offset: 0., - // angle: 0., - // rotation: 0., - // }, - // tree::ConeSegment { - // radius: 0.5, - // offset: 2., - // angle: 0., - // rotation: 0., - // }, - // tree::ConeSegment { - // radius: 0.5, - // offset: 2., - // angle: 0.2, - // rotation: PI/2., - // }, - // tree::ConeSegment { - // radius: 0.5, - // offset: 2., - // angle: 0.2, - // rotation: PI/2., - // }, - // tree::ConeSegment { - // radius: 0.5, - // offset: 2., - // angle: 0.2, - // rotation: PI/2., - // }, - // tree::ConeSegment { - // radius: 0.5, - // offset: 2., - // angle: 0.2, - // rotation: PI/2., - // }, - // tree::ConeSegment { - // radius: 0.5, - // offset: 2., - // angle: 0.2, - // rotation: PI/2., - // }, - // tree::ConeSegment { - // radius: 0.4, - // offset: 2., - // angle: 0.2, - // rotation: PI/2., - // }, - // tree::ConeSegment { - // radius: 0.3, - // offset: 2., - // angle: 0.2, - // rotation: PI/2., - // }, - // tree::ConeSegment { - // radius: 0.2, - // offset: 2., - // angle: 0.2, - // rotation: PI/2., - // }, - // ], - // 10, - // ); - - // commands.spawn(( - // Mesh3d(meshes.add(cone_mesh)), - // MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))), - // Transform::from_xyz(0.0, 0.5, 0.0), - // )); - - // let tree_mesh = tree_rendering::generate_branch_mesh(&[ - // &tree::TreeNode { - // id: 0, - // kind: tree::TreeNodeKind::Extension, - // parent_id: 0, - // radius: 0.5, - // offset: 0., - // angle: 0., - // rotation: 0., - // }, - // &tree::TreeNode { - // id: 0, - // kind: tree::TreeNodeKind::Extension, - // parent_id: 0, - // radius: 0.5, - // offset: 2., - // angle: 0., - // rotation: 0., - // }, - // &tree::TreeNode { - // id: 0, - // kind: tree::TreeNodeKind::Extension, - // parent_id: 0, - // radius: 0.5, - // offset: 2., - // angle: 0.2, - // rotation: PI / 2., - // }, - // &tree::TreeNode { - // id: 0, - // kind: tree::TreeNodeKind::Extension, - // parent_id: 0, - // radius: 0.5, - // offset: 2., - // angle: 0.2, - // rotation: PI / 2., - // }, - // &tree::TreeNode { - // id: 0, - // kind: tree::TreeNodeKind::Extension, - // parent_id: 0, - // radius: 0.5, - // offset: 2., - // angle: 0.2, - // rotation: PI / 2., - // }, - // &tree::TreeNode { - // id: 0, - // kind: tree::TreeNodeKind::Extension, - // parent_id: 0, - // radius: 0.5, - // offset: 2., - // angle: 0.2, - // rotation: PI / 2., - // }, - // &tree::TreeNode { - // id: 0, - // kind: tree::TreeNodeKind::Extension, - // parent_id: 0, - // radius: 0.5, - // offset: 2., - // angle: 0.2, - // rotation: PI / 2., - // }, - // &tree::TreeNode { - // id: 0, - // kind: tree::TreeNodeKind::Extension, - // parent_id: 0, - // radius: 0.4, - // offset: 2., - // angle: 0.2, - // rotation: PI / 2., - // }, - // &tree::TreeNode { - // id: 0, - // kind: tree::TreeNodeKind::Extension, - // parent_id: 0, - // radius: 0.3, - // offset: 2., - // angle: -0.4, - // rotation: PI / 2., - // }, - // &tree::TreeNode { - // id: 0, - // kind: tree::TreeNodeKind::EndCut, - // parent_id: 0, - // radius: 0.2, - // offset: 2., - // angle: -0.4, - // rotation: PI / 2., - // }, - // ]); - - // // let normal_material = materials.add(Color::srgb_u8(255, 0, 0)); - // // draw_normals(&mut meshes, &mut commands, &tree_mesh, &normal_material, &Transform::from_xyz(0.0, 0.5, 0.0)); - - // commands.spawn(( - // Mesh3d(meshes.add(tree_mesh)), - // MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))), - // Transform::from_xyz(0.0, 0.5, 0.0), - // )); - let tree = initial_tree(); let tree_mesh = tree_rendering::generate_tree(&tree); + // let normal_material = materials.add(Color::srgb_u8(255, 0, 0)); + // draw_normals( + // &mut meshes, + // &mut commands, + // &tree_mesh, + // &normal_material, + // &Transform::from_xyz(0.0, 0.5, 0.0), + // ); + commands.spawn(( + tree, Mesh3d(meshes.add(tree_mesh)), MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))), + // MeshMaterial3d(materials.add(StandardMaterial { + // base_color: Color::srgba_u8(124, 144, 255, 255), + // alpha_mode: AlphaMode::Blend, + // ..default() + // })), Transform::from_xyz(0.0, 0.5, 0.0), )); } @@ -325,29 +129,6 @@ fn draw_normals( } } -// fn draw_normals( -// meshes: Res>, -// mesh_handle: Res>, -// mut lines: ResMut, -// ) { -// if let Some(mesh) = meshes.get(mesh_handle) { -// let positions = match mesh.attribute(Mesh::ATTRIBUTE_POSITION).unwrap() { -// VertexAttributeValues::Float32x3(positions) => positions, -// _ => return, -// }; -// let normals = match mesh.attribute(Mesh::ATTRIBUTE_NORMAL).unwrap() { -// VertexAttributeValues::Float32x3(normals) => normals, -// _ => return, -// }; - -// for (pos, normal) in positions.iter().zip(normals.iter()) { -// let start = Vec3::from(*pos); -// let end = start + Vec3::from(*normal) * 0.2; -// lines.line_colored(start, end, 0.0, Color::GREEN); -// } -// } -// } - fn initial_tree() -> tree::Tree { let mut tree = tree::Tree::default(); tree.nodes.insert( @@ -357,7 +138,7 @@ fn initial_tree() -> tree::Tree { kind: tree::TreeNodeKind::Extension, parent_id: 0, radius: 0.5, - offset: 0., + offset: 2., angle: 0., rotation: 0., }, @@ -378,9 +159,9 @@ fn initial_tree() -> tree::Tree { 2, tree::TreeNode { id: 2, - kind: tree::TreeNodeKind::EndCut, + kind: tree::TreeNodeKind::Extension, parent_id: 1, - radius: 0.2, + radius: 0.25, offset: 2., angle: -0.4, rotation: PI / 2., @@ -390,38 +171,61 @@ fn initial_tree() -> tree::Tree { 3, tree::TreeNode { id: 3, - kind: tree::TreeNodeKind::StartBranch, - parent_id: 1, - radius: 0.1, - offset: 2., - angle: -0.6, - rotation: -PI / 2., + kind: tree::TreeNodeKind::EndCut, + parent_id: 2, + radius: 0.2, + offset: 0., + angle: 0.4, + rotation: PI / 2., }, ); tree.nodes.insert( 4, tree::TreeNode { id: 4, - kind: tree::TreeNodeKind::EndCut, - parent_id: 3, - radius: 0.05, + kind: tree::TreeNodeKind::StartBranch, + parent_id: 1, + radius: 0.2, offset: 2., - angle: -0.4, - rotation: PI / 2., + angle: -0.6, + rotation: -PI / 2., }, ); tree.nodes.insert( 5, tree::TreeNode { id: 5, - kind: tree::TreeNodeKind::EndBranch, - parent_id: 3, - radius: 0.05, - offset: 2., - angle: -0.4, + kind: tree::TreeNodeKind::Extension, + parent_id: 4, + radius: 0.1, + offset: 1., + angle: 0.2, rotation: -PI / 2., }, ); - + tree.nodes.insert( + 6, + tree::TreeNode { + id: 6, + kind: tree::TreeNodeKind::EndMeristem, + parent_id: 5, + radius: 0.05, + offset: 0.03, + angle: 0.2, + rotation: -PI / 2., + }, + ); + tree.nodes.insert( + 7, + tree::TreeNode { + id: 7, + kind: tree::TreeNodeKind::EndBranch, + parent_id: 4, + radius: 0.04, + offset: 0.5, + angle: -0.8, + rotation: PI / 4., + }, + ); tree } diff --git a/src/tree.rs b/src/tree.rs index 0571d9a..fbc2140 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -51,11 +51,12 @@ impl Tree { let mut transform = Transform::IDENTITY; let mut curr_node = &self.nodes[&id]; loop { - transform = curr_node.get_transform() * transform; if curr_node.id == curr_node.parent_id { break; } curr_node = &self.nodes[&curr_node.parent_id]; + info!("transform: {:?}", curr_node); + transform = curr_node.get_transform() * transform; } transform } @@ -84,6 +85,18 @@ impl TreeNode { transform } + pub fn get_rotation(&self) -> Transform { + let mut transform = Transform::IDENTITY; + transform.rotate_local_y(self.rotation); + transform.rotate_local_x(self.angle); + transform.rotate_local_y(-self.rotation); + transform + } + + pub fn get_translation(&self) -> Transform { + Transform::from_translation(Vec3::Y * self.offset) + } + pub fn is_end(&self) -> bool { matches!( self.kind, @@ -106,232 +119,3 @@ impl TreeNode { ) } } - -// pub fn generate_truncated_cone( -// top_radius: f32, -// bottom_radius: f32, -// height: f32, -// resolution: usize, -// ) -> Mesh { -// let mut positions = Vec::new(); -// let mut normals = Vec::new(); -// let mut uvs = Vec::new(); -// let mut indices = Vec::new(); - -// // === Side Vertices === -// for i in 0..=resolution { -// let theta = i as f32 / resolution as f32 * 2.0 * PI; -// let (cos, sin) = (theta.cos(), theta.sin()); - -// // Bottom ring -// let xb = bottom_radius * cos; -// let zb = bottom_radius * sin; -// positions.push([xb, 0.0, zb]); -// normals.push( -// Vec3::new(cos, (bottom_radius - top_radius) / height, sin) -// .normalize() -// .into(), -// ); -// uvs.push([i as f32 / resolution as f32, 0.0]); - -// // Top ring -// let xt = top_radius * cos; -// let zt = top_radius * sin; -// positions.push([xt, height, zt]); -// normals.push( -// Vec3::new(cos, (bottom_radius - top_radius) / height, sin) -// .normalize() -// .into(), -// ); -// uvs.push([i as f32 / resolution as f32, 1.0]); -// } - -// // === Side Indices === -// for i in 0..resolution { -// let base = (i * 2) as u32; -// indices.extend_from_slice(&[base, base + 1, base + 2, base + 1, base + 3, base + 2]); -// } - -// // === Bottom Cap Vertices (Separate) === -// let bottom_start = positions.len() as u32; -// for i in 0..resolution { -// let theta = i as f32 / resolution as f32 * 2.0 * PI; -// let (cos, sin) = (theta.cos(), theta.sin()); -// positions.push([bottom_radius * cos, 0.0, bottom_radius * sin]); -// normals.push([0.0, -1.0, 0.0]); -// uvs.push([0.5 + 0.5 * cos, 0.5 + 0.5 * sin]); -// } -// let bottom_center_index = positions.len() as u32; -// positions.push([0.0, 0.0, 0.0]); -// normals.push([0.0, -1.0, 0.0]); -// uvs.push([0.5, 0.5]); - -// for i in 0..resolution { -// let current = bottom_start + i as u32; -// let next = bottom_start + ((i + 1) % resolution) as u32; -// indices.extend_from_slice(&[bottom_center_index, current, next]); -// } - -// // === Top Cap Vertices (Separate) === -// let top_start = positions.len() as u32; -// for i in 0..resolution { -// let theta = i as f32 / resolution as f32 * 2.0 * PI; -// let (cos, sin) = (theta.cos(), theta.sin()); -// positions.push([top_radius * cos, height, top_radius * sin]); -// normals.push([0.0, 1.0, 0.0]); -// uvs.push([0.5 + 0.5 * cos, 0.5 + 0.5 * sin]); -// } -// let top_center_index = positions.len() as u32; -// positions.push([0.0, height, 0.0]); -// normals.push([0.0, 1.0, 0.0]); -// uvs.push([0.5, 0.5]); - -// for i in 0..resolution { -// let current = top_start + i as u32; -// let next = top_start + ((i + 1) % resolution) as u32; -// indices.extend_from_slice(&[top_center_index, next, current]); // correct winding -// } - -// Mesh::new( -// PrimitiveTopology::TriangleList, -// RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD, -// ) -// .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) -// .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) -// .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) -// .with_inserted_indices(Indices::U32(indices)) -// } - -pub struct ConeSegment { - pub radius: f32, - pub offset: f32, - pub rotation: f32, - pub angle: f32, -} - -pub fn append_connected_truncated_cones_to_mesh( - mesh: &mut Mesh, - segments: &[ConeSegment], - resolution: usize, -) { - use bevy::math::Vec3; - use bevy::render::mesh::{Indices, VertexAttributeValues}; - use std::f32::consts::PI; - - let verts_per_ring = resolution + 1; - - let base_index = match mesh.attribute(Mesh::ATTRIBUTE_POSITION) { - Some(VertexAttributeValues::Float32x3(v)) => v.len() as u32, - _ => 0, - }; - - let mut new_positions = Vec::new(); - let mut new_normals: Vec<[f32; 3]> = Vec::new(); - let mut new_uvs = Vec::new(); - let mut new_indices = Vec::new(); - - let mut transform = Transform::IDENTITY; - - for (i, seg) in segments.iter().enumerate() { - let prev_segment = if i != 0 { segments.get(i - 1) } else { None }; - let next_segment = segments.get(i + 1); - - let prev_y_normal = prev_segment.map(|p| (p.radius - seg.radius) / seg.offset); - let next_y_normal = next_segment.map(|n| (seg.radius - n.radius) / n.offset); - - let segment_y_normal = match (prev_y_normal, next_y_normal) { - (Some(prev), Some(next)) => (prev + next) / 2., - (None, Some(next)) => next, - (Some(prev), None) => prev, - (None, None) => 0., - }; - - transform.rotate_local_y(seg.rotation); - transform.rotate_local_x(seg.angle); - transform.rotate_local_y(-seg.rotation); - transform.translation += transform.up() * seg.offset; - - for j in 0..=resolution { - let theta = j as f32 / resolution as f32 * 2.0 * PI; - let (cos, sin) = (theta.cos(), theta.sin()); - - let normal = Vec3::new(cos, segment_y_normal, sin).normalize(); - let normal = transform.rotation * normal; - // info!("normal: {}", normal); - let position = - transform.transform_point(Vec3::new(seg.radius * cos, 0., seg.radius * sin)); - - new_positions.push(position.to_array()); - new_normals.push(normal.into()); - new_uvs.push([ - j as f32 / resolution as f32, - i as f32 / segments.len() as f32, - ]); - } - } - - for i in 0..segments.len() - 1 { - let ring_start = base_index + (i * verts_per_ring) as u32; - for j in 0..resolution { - let curr = ring_start + j as u32; - let next = curr + 1; - let above = curr + verts_per_ring as u32; - let above_next = next + verts_per_ring as u32; - - new_indices.extend_from_slice(&[curr, above, next, next, above, above_next]); - } - } - - let top_point = transform.transform_point(Vec3::ZERO); - let top_normal = transform.rotation * Vec3::new(0.0, 1.0, 0.0); - let cap_top_index = new_positions.len() as u32; - let top_ring_start = - base_index + (segments.len() * verts_per_ring) as u32 - verts_per_ring as u32; - - new_positions.push(top_point.to_array()); - new_normals.push(top_normal.to_array()); - new_uvs.push([0.5, 0.5]); - - for j in 0..resolution { - let i0 = top_ring_start + j as u32; - let i1 = top_ring_start + ((j + 1) % resolution) as u32; - new_indices.extend_from_slice(&[cap_top_index, i1, i0]); - } - - match mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION) { - Some(VertexAttributeValues::Float32x3(v)) => v.extend(new_positions), - _ => { - mesh.insert_attribute( - Mesh::ATTRIBUTE_POSITION, - VertexAttributeValues::Float32x3(new_positions), - ); - } - } - - match mesh.attribute_mut(Mesh::ATTRIBUTE_NORMAL) { - Some(VertexAttributeValues::Float32x3(v)) => v.extend(new_normals), - _ => { - mesh.insert_attribute( - Mesh::ATTRIBUTE_NORMAL, - VertexAttributeValues::Float32x3(new_normals), - ); - } - } - - match mesh.attribute_mut(Mesh::ATTRIBUTE_UV_0) { - Some(VertexAttributeValues::Float32x2(v)) => v.extend(new_uvs), - _ => { - mesh.insert_attribute( - Mesh::ATTRIBUTE_UV_0, - VertexAttributeValues::Float32x2(new_uvs), - ); - } - } - - match mesh.indices_mut() { - Some(Indices::U32(i)) => i.extend(new_indices), - _ => { - mesh.insert_indices(Indices::U32(new_indices)); - } - } -} diff --git a/src/tree_generation.rs b/src/tree_generation.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/tree_rendering.rs b/src/tree_rendering.rs index e494f3e..bb67225 100644 --- a/src/tree_rendering.rs +++ b/src/tree_rendering.rs @@ -2,7 +2,7 @@ use crate::tree::{Tree, TreeNode, TreeNodeKind}; use bevy::{ asset::RenderAssetUsages, prelude::*, - render::mesh::{self, Indices, PrimitiveTopology}, + render::mesh::{Indices, PrimitiveTopology}, }; use std::f32::consts::PI; @@ -41,34 +41,24 @@ impl TreeMeshData { pub fn generate_tree(tree: &Tree) -> Mesh { let mut mesh = TreeMeshData::default().to_mesh(); - info!("{:?}", tree.nodes); let mut branches = Vec::new(); - for (id, node) in tree.nodes.iter().filter(|(_, n)| n.is_end()) { + for (_, node) in tree.nodes.iter().filter(|(_, n)| n.is_end()) { let mut curr_node = node; loop { - info!("{:?}", curr_node); branches.push(curr_node.clone()); if curr_node.id == curr_node.parent_id { break; } - let next_node = &tree.nodes[&curr_node.parent_id]; + if curr_node.is_branch_begin() { - let mut modified_node = next_node.clone(); - modified_node.offset = 0.; - modified_node.angle = 0.; - modified_node.rotation = 0.; - modified_node.radius = curr_node.radius*1.1; - branches.push(modified_node); break; } - curr_node = next_node; + curr_node = &tree.nodes[&curr_node.parent_id]; } branches.reverse(); - info!("{:?}", branches); let mut branch_mesh = generate_branch_mesh(branches.as_slice()); let transform = tree.get_accumulated_transform(branches[0].id); - info!("{:?}", transform); branch_mesh.transform_by(transform); mesh.merge(&branch_mesh).unwrap(); // TODO: unwrap branches.clear(); @@ -77,119 +67,109 @@ pub fn generate_tree(tree: &Tree) -> Mesh { mesh } +/// Adds node extension to mesh data +/// +/// Assumes next node exists with same resolution +fn add_extension( + mesh_data: &mut TreeMeshData, + node: &TreeNode, + _next_node: &TreeNode, + // prev_node: Option<&TreeNode>, + transform: &mut Transform, +) { + *transform = transform.mul_transform(node.get_rotation()); + add_circle_points( + mesh_data, + RESOLUTION, + *transform, + node.radius, + Some(0.), // TODO: fix normal + ); + + let next_circle_start_index = mesh_data.positions.len(); + let current_circle_start_index = next_circle_start_index - (RESOLUTION + 1); + connect_circles( + mesh_data, + RESOLUTION, + current_circle_start_index as u32, + next_circle_start_index as u32, + ); + *transform = transform.mul_transform(node.get_translation()); +} + +fn add_end(mesh_data: &mut TreeMeshData, node: &TreeNode, transform: &mut Transform) { + *transform = transform.mul_transform(node.get_rotation()); + add_circle_points( + mesh_data, + RESOLUTION, + *transform, + node.radius, + Some(0.), // TODO: fix normal + ); + if matches!(node.kind, TreeNodeKind::EndCut) { + add_circle_points(mesh_data, RESOLUTION, *transform, node.radius, None); + } else if node.offset > node.radius { + let new_radius = + node.radius - node.radius * 0.5 * (node.offset - node.radius) / node.offset; + let new_length = node.offset - new_radius; + *transform = transform.mul_transform(Transform::from_translation(Vec3::Y * new_length)); + let next_circle_start_index = mesh_data.positions.len(); + add_circle_points( + mesh_data, + RESOLUTION, + *transform, + new_radius, + Some(0.), // TODO: fix normal + ); + let current_circle_start_index = next_circle_start_index - (RESOLUTION + 1); + connect_circles( + mesh_data, + RESOLUTION, + current_circle_start_index as u32, + next_circle_start_index as u32, + ); + *transform = transform.mul_transform(Transform::from_translation( + Vec3::Y * (node.offset - new_length), + )); + } else { + *transform = transform.mul_transform(node.get_translation()); + } + + let circle_index_start = (mesh_data.positions.len() - (RESOLUTION + 1)) as u32; + + let center_point = transform.transform_point(Vec3::ZERO); + let center_point_index = mesh_data.positions.len() as u32; + + mesh_data.positions.push(center_point.to_array()); + mesh_data + .normals + .push((transform.rotation * Vec3::Y).to_array()); + mesh_data.uvs.push([0.5, 0.5]); // TODO: UVs + + for j in 0..RESOLUTION { + let i0 = circle_index_start + j as u32; + let i1 = circle_index_start + ((j + 1) % RESOLUTION) as u32; + mesh_data + .indices + .extend_from_slice(&[center_point_index, i1, i0]); + } +} + pub fn generate_branch_mesh(nodes: &[TreeNode]) -> Mesh { let mut mesh_data = TreeMeshData::default(); let mut transform = Transform::IDENTITY; for (i, node) in nodes.iter().enumerate() { - let prev_node = if i != 0 { nodes.get(i - 1) } else { None }; let next_node = nodes.get(i + 1); - - let prev_y_normal = prev_node.map(|p| (p.radius - node.radius) / node.offset); - let next_y_normal = next_node.map(|n| (node.radius - n.radius) / n.offset); - - // TODO: this normal is wrong (at least not including angle) - let segment_y_normal = match (prev_y_normal, next_y_normal) { - (Some(prev), Some(next)) => (prev + next) / 2., - (None, Some(next)) => next, - (Some(prev), None) => prev, - (None, None) => 0., - }; - transform = transform * node.get_transform(); - // transform.rotate_local_y(seg.rotation); - // transform.rotate_local_x(seg.angle); - // transform.rotate_local_y(-seg.rotation); - // transform.translation += transform.up() * seg.offset; - - add_circle_points( - &mut mesh_data, - RESOLUTION, - transform, - node.radius, - Some(segment_y_normal), - ); - - if i != 0 { - let current_circle_start_index = mesh_data.positions.len() - (RESOLUTION + 1); - let prev_circle_start_index = current_circle_start_index - (RESOLUTION + 1); - connect_circles( - &mut mesh_data, - RESOLUTION, - prev_circle_start_index as u32, - current_circle_start_index as u32, - ) + if node.is_extension() { + let next_node = next_node.unwrap(); // TODO: unwrap + add_extension(&mut mesh_data, &node, &next_node, &mut transform); + } else if node.is_end() { + add_end(&mut mesh_data, &node, &mut transform); } } - // for i in 0..nodes.len() - 1 { - // let ring_start = (i * verts_per_ring) as u32; - // for j in 0..RESOLUTION { - // let curr = ring_start + j as u32; - // let next = curr + 1; - // let above = curr + verts_per_ring as u32; - // let above_next = next + verts_per_ring as u32; - - // mesh_data - // .indices - // .extend_from_slice(&[curr, above, next, next, above, above_next]); - // } - // } - - // match nodes.last() { - // Some(node) => { - // let top_point = transform.transform_point(Vec3::ZERO); - // let top_normal = transform.rotation * Vec3::new(0.0, 1.0, 0.0); - // let cap_top_index = mesh_data.positions.len() as u32; - // let top_ring_start = (nodes.len() * verts_per_ring) as u32 - verts_per_ring as u32; - - // mesh_data.positions.push(top_point.to_array()); - // mesh_data.normals.push(top_normal.to_array()); - // mesh_data.uvs.push([0.5, 0.5]); - - // match node.kind { - // TreeNodeKind::EndMeristem => {} - // TreeNodeKind::EndCut => {} - // _ => {} - // } - // } - // _ => {} - // } - - if let Some(node) = nodes.last() { - let last_circle_index = match node.kind { - TreeNodeKind::EndMeristem => { - Some((mesh_data.positions.len() - (RESOLUTION + 1)) as u32) - } - TreeNodeKind::EndCut => None, - _ => None, - }; - close_circle( - &mut mesh_data, - RESOLUTION, - last_circle_index, - transform, - node.radius, - ); - } - - // let top_point = transform.transform_point(Vec3::ZERO); - // let top_normal = transform.rotation * Vec3::new(0.0, 1.0, 0.0); - // let cap_top_index = mesh_data.positions.len() as u32; - // let top_ring_start = (nodes.len() * verts_per_ring) as u32 - verts_per_ring as u32; - - // mesh_data.positions.push(top_point.to_array()); - // mesh_data.normals.push(top_normal.to_array()); - // mesh_data.uvs.push([0.5, 0.5]); - - // for j in 0..RESOLUTION { - // let i0 = top_ring_start + j as u32; - // let i1 = top_ring_start + ((j + 1) % RESOLUTION) as u32; - // mesh_data - // .indices - // .extend_from_slice(&[cap_top_index, i1, i0]); - // } - mesh_data.to_mesh() } @@ -236,37 +216,3 @@ fn connect_circles( .extend_from_slice(&[curr, above, next, next, above, above_next]); } } - -fn close_circle( - mesh_data: &mut TreeMeshData, - resolution: usize, - circle_index: Option, - transform: Transform, - radius: f32, -) { - let circle_index_start = match circle_index { - Some(index) => index, - None => { - add_circle_points(mesh_data, resolution, transform, radius, None); - (mesh_data.positions.len() - (resolution + 1)) as u32 - } - }; - - let center_point = transform.transform_point(Vec3::ZERO); - let center_point_index = mesh_data.positions.len() as u32; - - mesh_data.positions.push(center_point.to_array()); - mesh_data - .normals - .push((transform.rotation * Vec3::Y).to_array()); - // TODO: UVs - mesh_data.uvs.push([0.5, 0.5]); - - for j in 0..RESOLUTION { - let i0 = circle_index_start + j as u32; - let i1 = circle_index_start + ((j + 1) % RESOLUTION) as u32; - mesh_data - .indices - .extend_from_slice(&[center_point_index, i1, i0]); - } -}