diff --git a/fonts/FiraSans-Bold.ttf b/fonts/FiraSans-Bold.ttf new file mode 100644 index 0000000..95e1660 Binary files /dev/null and b/fonts/FiraSans-Bold.ttf differ diff --git a/src/interop.rs b/src/interop.rs new file mode 100644 index 0000000..356270b --- /dev/null +++ b/src/interop.rs @@ -0,0 +1,60 @@ +use bevy::prelude::{Component, Reflect, Resource}; +use fj_core::objects::{Sketch, Solid}; +use fj_math::{Point, Vector}; +use fj_interop::mesh::Mesh as FjMesh; +use fj_core::storage::Handle as FjHandle; + +#[derive(Component)] +struct FjSolidWrapper { + handle: fj_core::storage::Handle, +} + +#[derive(Component, Debug)] +pub struct FjMeshWrapper { + pub mesh: FjMesh>, + pub handle: FjHandle, + pub sketch: Sketch, +} + +pub(crate) trait ToPoint3 { + fn to_point3(&self) -> Point<3>; +} + + +impl ToPoint3 for bevy::prelude::Vec3 { + fn to_point3(&self) -> fj_math::Point<3> { + fj_math::Point { coords: Vector { components: [fj_math::Scalar::from_u64(0),fj_math::Scalar::from_u64(0),fj_math::Scalar::from_u64(0)] } } + } +} + +pub(crate) trait ToVec3 { + fn to_vec3(&self) -> bevy::prelude::Vec3; +} + +impl ToVec3 for fj_math::Point<3> { + fn to_vec3(&self) -> bevy::prelude::Vec3 { + bevy::prelude::Vec3::new(self.x.into_f32(), self.y.into_f32(), self.z.into_f32()) + } +} + +impl ToVec3 for fj_math::Vector<3> { + fn to_vec3(&self) -> bevy::prelude::Vec3 { + bevy::prelude::Vec3::new(self.x.into_f32(), self.y.into_f32(), self.z.into_f32()) + } +} + +pub(crate) trait ToVec2 { + fn to_vec2(&self) -> bevy::prelude::Vec2; +} + +impl crate::interop::ToVec2 for fj_math::Point<2> { + fn to_vec2(&self) -> bevy::prelude::Vec2 { + bevy::prelude::Vec2::new(self.u.into_f32(), self.v.into_f32()) + } +} + +impl crate::interop::ToVec2 for fj_math::Vector<2> { + fn to_vec2(&self) -> bevy::prelude::Vec2 { + bevy::prelude::Vec2::new(self.u.into_f32(), self.v.into_f32()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 206fdeb..1516074 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,17 @@ +pub mod mesh; +pub mod interop; + // Importing Bevy ECS components and systems +use crate::mesh::create_rectangle_mesh; use bevy::ecs::component::Component; use bevy::ecs::system::{Commands, Query}; use bevy::ecs::entity::Entity; use bevy::ecs::query::Without; use bevy::ecs::system::ResMut; +use crate::interop::{ToPoint3, ToVec3}; // Importing Bevy assets and rendering related components -use bevy::prelude::{Mesh, shape}; +use bevy::prelude::*; use bevy::render::mesh::{Indices, PrimitiveTopology}; use bevy::asset::Assets; use bevy::pbr::{PbrBundle, StandardMaterial}; @@ -30,38 +35,17 @@ use fj_interop::mesh::Mesh as FjMesh; use fj_math::{Aabb, Line, Point, Scalar, Vector}; use std::ops::Deref; +use bevy::a11y::accesskit::Size; use bevy::math::Vec3; +use bevy::ui::ZIndex::Global; use fj_core::geometry::{GlobalPath, SurfacePath}; +use fj_core::objects::Object::Surface; +use fj_core::operations::holes::AddHole; +use fj_core::operations::split::{SplitEdge, SplitFace, SplitHalfEdge}; use rand::Rng; +use crate::interop::FjMeshWrapper; +use crate::mesh::{convert_mesh, LineList}; -trait ToVec3 { - fn to_vec3(&self) -> bevy::prelude::Vec3; -} - -impl ToVec3 for fj_math::Point<3> { - fn to_vec3(&self) -> bevy::prelude::Vec3 { - bevy::prelude::Vec3::new(self.x.into_f32(), self.y.into_f32(), self.z.into_f32()) - } -} - -impl ToVec3 for fj_math::Vector<3> { - fn to_vec3(&self) -> bevy::prelude::Vec3 { - bevy::prelude::Vec3::new(self.x.into_f32(), self.y.into_f32(), self.z.into_f32()) - } -} - - -#[derive(Component)] -struct FjSolidWrapper { - handle: fj_core::storage::Handle, -} - -#[derive(Component, Debug)] -pub struct FjMeshWrapper { - pub mesh: FjMesh>, - pub handle: FjHandle, - pub sketch: Sketch, -} #[derive(Component)] struct FjConvertedFlag; @@ -74,78 +58,16 @@ impl Plugin for FjRenderPlugin { } } - -fn generate_uv_mapping(fj_mesh: &FjMesh>) -> Vec<[f32; 2]> { - let mut uvs = Vec::new(); - - for vertex in fj_mesh.vertices() { - let x = vertex.coords.x.into_f32(); - let y = vertex.coords.y.into_f32(); - // Here we're using x and y coordinates as u and v - uvs.push([x, y]); - uvs.push([x, y]); - uvs.push([x, y]); - uvs.push([x, y]); - uvs.push([x, y]); - uvs.push([x, y]); - } - uvs -} - -fn generate_normals(fj_mesh: &FjMesh>) -> Vec<[f32; 3]> { - let mut normals = Vec::new(); - for triangle in fj_mesh.triangles() { - let normal = triangle.inner.normal(); - normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]); - normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]); - normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]); - } - normals -} - -fn generate_positions(fj_mesh: &FjMesh>) -> Vec<[f32; 3]> { - let mut positions = Vec::new(); - - // Iterate through each triangle - for triangle in fj_mesh.triangles() { - // For each vertex index in the triangle - for vertex_index in triangle.inner.points() { - positions.push([vertex_index.x.into_f32(), vertex_index.y.into_f32(), vertex_index.z.into_f32()]); - } - } - - positions -} - -fn generate_indices(fj_mesh: &FjMesh>) -> Vec { - let mut num_positions = 0; - - // Count the total number of positions (3 per triangle) - for triangle in fj_mesh.triangles() { - num_positions += 3; - } - - // Generate the indices [0, 1, 2, ..., num_positions-1] - (0..num_positions as u32).collect() -} - -// Need to take a solid, and return all the data needed to create a Bevy Mesh from scratch -pub fn convert_mesh(fj_mesh: &FjMesh>) -> Mesh { - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, generate_positions(&fj_mesh)); - mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, generate_normals(&fj_mesh)); - // mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, generate_uv_mapping(&fj_mesh)); - mesh.set_indices(Some(Indices::U32(generate_indices(&fj_mesh)))); - mesh -} - fn update_fj_model_system( mut commands: Commands, query: Query<(Entity, &FjMeshWrapper), Without>, mut meshes: ResMut>, mut materials: ResMut>, + asset_server: Res ) { + for (entity, solid) in &query { + let bevy_mesh = convert_mesh(&solid.mesh); commands.entity(entity).insert( ( @@ -161,144 +83,236 @@ fn update_fj_model_system( } } -fn render_line(mut commands: &mut Commands, - fj_model: &FjMeshWrapper, - mut meshes: &mut ResMut>, - mut materials: &mut ResMut>, - line: Line) { - let origin = line.origin().to_xyz(); - let direction = line.direction().to_xyz(); - let opposite_corner = Vec3::new( - origin.x.into_f32() + direction.x.into_f32() + 0.01, - origin.y.into_f32() + direction.y.into_f32() + 0.01, - origin.z.into_f32() + direction.z.into_f32() + 0.01, - ); - let mut rng = rand::thread_rng(); - let red: f32 = rng.gen_range(0.0..1.0); - let green: f32 = rng.gen_range(0.0..1.0); - let blue: f32 = rng.gen_range(0.0..1.0); - commands.spawn(( // line on base of sweep - PbrBundle { - mesh: meshes.add(Mesh::from(shape::Box::from_corners(opposite_corner.into(), origin.to_vec3()))), - material: materials.add(Color::rgb(red, green, blue).into()), - transform: Transform::from_xyz(0.0, 0.0, 0.0), - ..default() - }, - // RaycastPickTarget::default(), // <- Needed for the raycast backend. - // PickableBundle::default() // <- This one too - )); - commands.spawn(( // vertex - PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 0.025 })), - material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()), - transform: Transform::from_xyz(origin.to_xyz().x.into_f32(), origin.to_xyz().y.into_f32(), origin.to_xyz().z.into_f32()), - ..default() - }, - // RaycastPickTarget::default(), // <- Needed for the raycast backend. - // PickableBundle::default() // <- This one too - )); -} fn add_debug_info_to_entity(mut commands: &mut Commands, fj_model: &FjMeshWrapper, mut meshes: &mut ResMut>, mut materials: &mut ResMut>, ) { - for region in fj_model.sketch.regions() { - for cycle in region.all_cycles() { - for half_edge in cycle.half_edges() { - match half_edge.path() { - SurfacePath::Circle(x) => {} - SurfacePath::Line(x) => { - render_line(commands, fj_model, meshes, materials, x) - } - } - // 1d point - half_edge.boundary().inner[0].coords; - half_edge.boundary().inner[1].coords; - } - } + // for region in fj_model.sketch.regions() { + // for cycle in region.all_cycles() { + // for half_edge in cycle.half_edges() { + // match half_edge.path() { + // SurfacePath::Circle(x) => {} + // SurfacePath::Line(x) => { + // render_line(commands, fj_model, meshes, materials, x) + // } + // } + // // 1d point + // half_edge.boundary().inner[0].coords; + // half_edge.boundary().inner[1].coords; + // } + // } + // + // } + + for shell in fj_model.handle.shells(){ + } for shell in fj_model.handle.shells() { println!("{:?}. shell", shell); for face in shell.faces() { + println!("{:?}. face", face); let surface = face.surface(); let geom = surface.geometry(); + let geom_sweep_vector = geom.v; + + for edge in face.region().exterior().half_edges(){ + match edge.path() { + SurfacePath::Circle(_) => {} + SurfacePath::Line(x) => { + let mut rng = rand::thread_rng(); + let color = Color::rgb(1.0, 0.,0.).into(); + + let mut origin = geom.point_from_surface_coords(x.origin()).to_xyz(); + let direction = geom.vector_from_surface_coords(x.direction()).to_xyz(); + let opposite_corner = Point{ coords: Vector{ + components: [ + Scalar::from(origin.x.into_f32()) + direction.x, + Scalar::from(origin.y.into_f32()) + direction.y, + Scalar::from(origin.z.into_f32()) + direction.z, + ] + }}; + let line = GlobalPath::line_from_points([origin, opposite_corner]); + match line.0 { + GlobalPath::Circle(_) => {} + GlobalPath::Line(x) => { + render_line(commands, fj_model, meshes, materials, x, color); + } + } + } + } + } + match geom.u { GlobalPath::Circle(x) => { x.aabb(); } GlobalPath::Line(x) => { - render_line(commands, fj_model, meshes, materials, x); - - let origin = x.origin(); - let direction = x.direction(); - let opposite_corner = Vec3::new( - origin.x.into_f32() + direction.x.into_f32() + 0.01, - origin.y.into_f32() + direction.y.into_f32() + 0.01, - origin.z.into_f32() + direction.z.into_f32() + 0.01, - ); + let mut rng = rand::thread_rng(); let red: f32 = rng.gen_range(0.0..1.0); let green: f32 = rng.gen_range(0.0..1.0); let blue: f32 = rng.gen_range(0.0..1.0); - commands.spawn(( // line on top of sweep, just offset by the sweep vector - PbrBundle { - mesh: meshes.add(Mesh::from(shape::Box::from_corners(opposite_corner.into(), origin.to_vec3()))), - material: materials.add(Color::rgb(red, green, blue).into()), - transform: Transform::from_xyz(geom_sweep_vector.x.into_f32(), geom_sweep_vector.y.into_f32(), geom_sweep_vector.z.into_f32()), - ..default() - }, - // RaycastPickTarget::default(), // <- Needed for the raycast backend. - // PickableBundle::default() // <- This one too - )); - commands.spawn(( // line following the sweep - PbrBundle { - mesh: meshes.add(Mesh::from(shape::Box::from_corners(origin.to_vec3() + geom_sweep_vector.to_vec3(), origin.to_vec3() + 0.01))), - material: materials.add(Color::rgb(red, green, blue).into()), - transform: Transform::from_xyz(0.0, 0.0, 0.0), - ..default() - }, - // RaycastPickTarget::default(), // <- Needed for the raycast backend. - // PickableBundle::default() // <- This one too - )); - commands.spawn(( // swept vertex - PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 0.025 })), - material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()), - transform: Transform::from_xyz( - origin.to_xyz().x.into_f32() + geom_sweep_vector.to_vec3().x, - origin.to_xyz().y.into_f32() + geom_sweep_vector.to_vec3().y, - origin.to_xyz().z.into_f32() + geom_sweep_vector.to_vec3().z, - ), - // transform: Transform::from_scale(origin.to_vec3() + geom_sweep_vector.to_vec3()), - ..default() - }, - // RaycastPickTarget::default(), // <- Needed for the raycast backend. - // PickableBundle::default(), // <- This one too - - // I want to insert a component when dragging starts, that contains - // data about the exact ray hit location. - // On::>::run(run), - // On::>::target_insert(DragCaster::default()), - // On::>::target_component_mut::(|drag, mut caster| { - // caster.hit_location = drag.hit.position.unwrap(); - // - // // (*transform).translation = transform.translation + Vec3::new(-drag.delta.x / 100.0, 0.0, drag.delta.y / 100.0); - // }), - // On::>::target_component_mut::(|drag, mut caster| { - // drag.distance - // // (*transform).translation = transform.translation + Vec3::new(-drag.delta.x / 100.0, 0.0, drag.delta.y / 100.0); - // }), - )); + // let color = Color::rgb(red, green, blue).into(); + // render_line(commands, fj_model, meshes, materials, x, color); + + // let origin = x.origin(); + // let direction = x.direction(); + // let opposite_corner = Vec3::new( + // origin.x.into_f32() + direction.x.into_f32(), + // origin.y.into_f32() + direction.y.into_f32(), + // origin.z.into_f32() + direction.z.into_f32(), + // ); + // + // commands.spawn(( // line on top of sweep, just offset by the sweep vector + // PbrBundle { + // // mesh: meshes.add(Mesh::from(LineList { + // // lines: vec![ + // // (opposite_corner.into(), origin.to_vec3()), + // // ], + // // })), + // mesh: meshes.add(create_rectangle_mesh(opposite_corner, origin.to_vec3(), 0.01, 0.01)), + // material: materials.add(Color::rgb(red, green, blue).into()), + // transform: Transform::from_xyz(geom_sweep_vector.x.into_f32(), geom_sweep_vector.y.into_f32(), geom_sweep_vector.z.into_f32()), + // ..default() + // }, + // // RaycastPickTarget::default(), // <- Needed for the raycast backend. + // // PickableBundle::default() // <- This one too + // )); + // commands.spawn(( // line following the sweep + // PbrBundle { + // mesh: meshes.add(create_rectangle_mesh(origin.to_vec3() + geom_sweep_vector.to_vec3(), origin.to_vec3(), 0.01, 0.01)), + // material: materials.add(Color::rgb(red, green, blue).into()), + // transform: Transform::from_xyz(0.0, 0.0, 0.0), + // ..default() + // }, + // // RaycastPickTarget::default(), // <- Needed for the raycast backend. + // // PickableBundle::default() // <- This one too + // )); + // commands.spawn(( // swept vertex + // PbrBundle { + // mesh: meshes.add(Mesh::from(shape::Cube { size: 0.025 })), + // material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()), + // transform: Transform::from_xyz( + // origin.to_xyz().x.into_f32() + geom_sweep_vector.to_vec3().x, + // origin.to_xyz().y.into_f32() + geom_sweep_vector.to_vec3().y, + // origin.to_xyz().z.into_f32() + geom_sweep_vector.to_vec3().z, + // ), + // // transform: Transform::from_scale(origin.to_vec3() + geom_sweep_vector.to_vec3()), + // ..default() + // }, + // // RaycastPickTarget::default(), // <- Needed for the raycast backend. + // // PickableBundle::default(), // <- This one too + // + // // I want to insert a component when dragging starts, that contains + // // data about the exact ray hit location. + // // On::>::run(run), + // // On::>::target_insert(DragCaster::default()), + // // On::>::target_component_mut::(|drag, mut caster| { + // // caster.hit_location = drag.hit.position.unwrap(); + // // + // // // (*transform).translation = transform.translation + Vec3::new(-drag.delta.x / 100.0, 0.0, drag.delta.y / 100.0); + // // }), + // // On::>::target_component_mut::(|drag, mut caster| { + // // drag.distance + // // // (*transform).translation = transform.translation + Vec3::new(-drag.delta.x / 100.0, 0.0, drag.delta.y / 100.0); + // // }), + // )); } } } } } +fn render_line(mut commands: &mut Commands, + fj_model: &FjMeshWrapper, + mut meshes: &mut ResMut>, + mut materials: &mut ResMut>, + line: Line, + color: Color) { + let origin = line.origin().to_xyz(); + let direction = line.direction().to_xyz(); + let opposite_corner = Vec3::new( + origin.x.into_f32() + direction.x.into_f32(), + origin.y.into_f32() + direction.y.into_f32(), + origin.z.into_f32() + direction.z.into_f32(), + ); + + commands.spawn(( // line on base of sweep + PbrBundle { + mesh: meshes.add(Mesh::from(create_rectangle_mesh(opposite_corner, origin.to_vec3(), 0.01, 0.01))), + // mesh: meshes.add(Mesh::from(shape::Box::from_corners(opposite_corner.into(), origin.to_vec3()))), + material: materials.add(StandardMaterial::from(color)), + transform: Transform::from_xyz(0.0, 0.0, 0.0), + ..default() + }, + // RaycastPickTarget::default(), // <- Needed for the raycast backend. + // PickableBundle::default() // <- This one too + )); + commands.spawn(( // vertex + PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 0.025 })), + material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()), + transform: Transform::from_xyz(origin.to_xyz().x.into_f32(), origin.to_xyz().y.into_f32(), origin.to_xyz().z.into_f32()), + ..default() + }, + // RaycastPickTarget::default(), // <- Needed for the raycast backend. + // PickableBundle::default() // <- This one too + )); +} + +/* + + +// Root node + commands.spawn(NodeBundle { + style: Style { + flex_direction: FlexDirection::Column, + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + ..Default::default() + }, + ..Default::default() + }).with_children(|parent| { + // Text node + parent.spawn(TextBundle { + text: Text::from_section( + "This is some text", + TextStyle { + font: asset_server.load("fonts/FiraMono-Medium.ttf"), + font_size: 40.0, + color: Color::WHITE, + }, + ), + ..Default::default() + }); + + // Checkbox (custom implementation) + parent.spawn(NodeBundle { + style: Style { + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + ..Default::default() + }, + ..Default::default() + }).with_children(|parent| { + parent.spawn(TextBundle { + text: Text::from_section( + "X", // Simulating a checked state + TextStyle { + font: asset_server.load("fonts/FiraSans-Bold.ttf"), + font_size: 20.0, + color: Color::BLACK, + }, + ), + ..Default::default() + }); + }); + }); + */ \ No newline at end of file diff --git a/src/mesh.rs b/src/mesh.rs new file mode 100644 index 0000000..b92aa51 --- /dev/null +++ b/src/mesh.rs @@ -0,0 +1,162 @@ +use bevy::math::Vec3; +use bevy::prelude::Mesh; +use bevy::render::mesh::{Indices, PrimitiveTopology}; +use fj_math::Point; +use fj_interop::mesh::Mesh as FjMesh; +use fj_core::storage::Handle as FjHandle; + +#[derive(Debug, Clone)] +pub struct LineList { + pub lines: Vec<(Vec3, Vec3)>, +} + +impl From for Mesh { + fn from(line: LineList) -> Self { + let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect(); + + // This tells wgpu that the positions are list of lines + // where every pair is a start and end point + Mesh::new(PrimitiveTopology::LineList) + // Add the vertices positions as an attribute + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices) + } +} + +fn generate_uv_mapping(fj_mesh: &FjMesh>) -> Vec<[f32; 2]> { + let mut uvs = Vec::new(); + + for vertex in fj_mesh.vertices() { + let x = vertex.coords.x.into_f32(); + let y = vertex.coords.y.into_f32(); + // Here we're using x and y coordinates as u and v + uvs.push([x, y]); + uvs.push([x, y]); + uvs.push([x, y]); + uvs.push([x, y]); + uvs.push([x, y]); + uvs.push([x, y]); + } + uvs +} + +fn generate_normals(fj_mesh: &FjMesh>) -> Vec<[f32; 3]> { + let mut normals = Vec::new(); + for triangle in fj_mesh.triangles() { + let normal = triangle.inner.normal(); + normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]); + normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]); + normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]); + } + normals +} + +fn generate_positions(fj_mesh: &FjMesh>) -> Vec<[f32; 3]> { + let mut positions = Vec::new(); + + // Iterate through each triangle + for triangle in fj_mesh.triangles() { + // For each vertex index in the triangle + for vertex_index in triangle.inner.points() { + positions.push([vertex_index.x.into_f32(), vertex_index.y.into_f32(), vertex_index.z.into_f32()]); + } + } + + positions +} + +fn generate_indices(fj_mesh: &FjMesh>) -> Vec { + let mut num_positions = 0; + + // Count the total number of positions (3 per triangle) + for triangle in fj_mesh.triangles() { + num_positions += 3; + } + + // Generate the indices [0, 1, 2, ..., num_positions-1] + (0..num_positions as u32).collect() +} + +// Need to take a solid, and return all the data needed to create a Bevy Mesh from scratch +pub fn convert_mesh(fj_mesh: &FjMesh>) -> Mesh { + let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, generate_positions(&fj_mesh)); + mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, generate_normals(&fj_mesh)); + // mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, generate_uv_mapping(&fj_mesh)); + mesh.set_indices(Some(Indices::U32(generate_indices(&fj_mesh)))); + mesh +} + +pub fn create_rectangle_mesh(p1: Vec3, p2: Vec3, width: f32, height: f32) -> Mesh { + // Calculate direction vector from p1 to p2 + let direction = (p2 - p1).normalize(); + // Create arbitrary up vector + let up = Vec3::Y; + // Right vector for the rectangle width + let right = direction.cross(up).normalize() * width * 0.5; + // Up vector for rectangle height, ensuring it's perpendicular + let up_perpendicular = direction.cross(right).normalize() * height * 0.5; + + let vertices = [ + p1 - right - up_perpendicular, // Bottom-left of p1 face + p1 + right - up_perpendicular, // Bottom-right of p1 face + p1 + right + up_perpendicular, // Top-right of p1 face + p1 - right + up_perpendicular, // Top-left of p1 face + p2 - right - up_perpendicular, // Bottom-left of p2 face + p2 + right - up_perpendicular, // Bottom-right of p2 face + p2 + right + up_perpendicular, // Top-right of p2 face + p2 - right + up_perpendicular, // Top-left of p2 face + ]; + + // Calculate face normals + let front_normal = (vertices[1] - vertices[0]).cross(vertices[3] - vertices[0]).normalize(); + let back_normal = (vertices[5] - vertices[4]).cross(vertices[7] - vertices[4]).normalize(); + let right_normal = (vertices[5] - vertices[1]).cross(vertices[2] - vertices[1]).normalize(); + let left_normal = (vertices[0] - vertices[4]).cross(vertices[7] - vertices[4]).normalize(); + let top_normal = (vertices[2] - vertices[3]).cross(vertices[7] - vertices[3]).normalize(); + let bottom_normal = (vertices[1] - vertices[0]).cross(vertices[4] - vertices[0]).normalize(); + + let normals = vec![ + bottom_normal, bottom_normal, top_normal, top_normal, // Bottom and top for first quad + bottom_normal, bottom_normal, top_normal, top_normal, // Bottom and top for second quad + front_normal, front_normal, front_normal, front_normal, // Front face normals + back_normal, back_normal, back_normal, back_normal, // Back face normals + right_normal, right_normal, right_normal, right_normal, // Right face normals + left_normal, left_normal, left_normal, left_normal, // Left face normals + ]; + + + // Convert positions and normals to the format expected by Bevy + + // Creating a mesh + let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); + + // Insert positions + let positions: Vec<[f32; 3]> = vertices.iter().map(|v| [v.x, v.y, v.z]).collect(); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); + + // Insert normals (optional here, assuming all outwards for simplicity) + let normals: Vec<[f32; 3]> = normals.iter().map(|n| [n.x, n.y, n.z]).collect(); + mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); + + // Define two triangles for each face of the rectangle mesh + + let indices = Indices::U32(vec![ + // Front face + 0, 2, 1, 0, 3, 2, + // Back face + 4, 6, 5, 4, 7, 6, + // Top face + 2, 3, 6, 6, 3, 7, + // Bottom face + 0, 1, 5, 0, 5, 4, + // Right face + 1, 2, 6, 1, 6, 5, + // Left face + 0, 4, 7, 0, 7, 3, + ]); + + + mesh.set_indices(Some(indices)); + + mesh +} \ No newline at end of file