From c7f7d906ca4f765a19d00ecd7146b03748398934 Mon Sep 17 00:00:00 2001 From: Matty Date: Wed, 22 May 2024 08:22:11 -0400 Subject: [PATCH] Tetrahedron mesh (#13463) # Objective Allow the `Tetrahedron` primitive to be used for mesh generation. This is part of ongoing work to bring unify the capabilities of `bevy_math` primitives. ## Solution `Tetrahedron` implements `Meshable`. Essentially, each face is just meshed as a `Triangle3d`, but first there is an inversion step when the signed volume of the tetrahedron is negative to ensure that the faces all actually point outward. ## Testing I loaded up some examples and hackily exchanged existing meshes with the new one to see that it works as expected. --- .../src/mesh/primitives/dim3/mod.rs | 1 + .../src/mesh/primitives/dim3/tetrahedron.rs | 55 +++++++++++++++++++ .../src/mesh/primitives/dim3/triangle3d.rs | 8 ++- examples/3d/3d_shapes.rs | 1 + 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 crates/bevy_render/src/mesh/primitives/dim3/tetrahedron.rs diff --git a/crates/bevy_render/src/mesh/primitives/dim3/mod.rs b/crates/bevy_render/src/mesh/primitives/dim3/mod.rs index 82b418f6588f4..a43b2d1862a0d 100644 --- a/crates/bevy_render/src/mesh/primitives/dim3/mod.rs +++ b/crates/bevy_render/src/mesh/primitives/dim3/mod.rs @@ -4,6 +4,7 @@ mod cuboid; mod cylinder; mod plane; mod sphere; +mod tetrahedron; mod torus; pub(crate) mod triangle3d; diff --git a/crates/bevy_render/src/mesh/primitives/dim3/tetrahedron.rs b/crates/bevy_render/src/mesh/primitives/dim3/tetrahedron.rs new file mode 100644 index 0000000000000..dcb4535a4e542 --- /dev/null +++ b/crates/bevy_render/src/mesh/primitives/dim3/tetrahedron.rs @@ -0,0 +1,55 @@ +use super::triangle3d; +use crate::{ + mesh::{Indices, Mesh, Meshable}, + render_asset::RenderAssetUsages, +}; +use bevy_math::primitives::{Tetrahedron, Triangle3d}; +use wgpu::PrimitiveTopology; + +impl Meshable for Tetrahedron { + type Output = Mesh; + + fn mesh(&self) -> Self::Output { + let mut faces: Vec<_> = self.faces().into(); + + // If the tetrahedron has negative orientation, reverse all the triangles so that + // they still face outward. + if self.signed_volume().is_sign_negative() { + faces.iter_mut().for_each(Triangle3d::reverse); + } + + let mut positions = vec![]; + let mut normals = vec![]; + let mut uvs = vec![]; + + // Each face is meshed as a `Triangle3d`, and we just shove the data into the + // vertex attributes sequentially. + for face in faces { + positions.extend(face.vertices); + + let face_normal = triangle3d::normal_vec(&face); + normals.extend(vec![face_normal; 3]); + + let face_uvs = triangle3d::uv_coords(&face); + uvs.extend(face_uvs); + } + + // There are four faces and none of them share vertices. + let indices = Indices::U32(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + + Mesh::new( + PrimitiveTopology::TriangleList, + RenderAssetUsages::default(), + ) + .with_inserted_indices(indices) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + } +} + +impl From for Mesh { + fn from(tetrahedron: Tetrahedron) -> Self { + tetrahedron.mesh() + } +} diff --git a/crates/bevy_render/src/mesh/primitives/dim3/triangle3d.rs b/crates/bevy_render/src/mesh/primitives/dim3/triangle3d.rs index 4eb0fba32f9a3..ccbc897e0a2ef 100644 --- a/crates/bevy_render/src/mesh/primitives/dim3/triangle3d.rs +++ b/crates/bevy_render/src/mesh/primitives/dim3/triangle3d.rs @@ -14,7 +14,7 @@ impl Meshable for Triangle3d { let uvs: Vec<_> = uv_coords(self).into(); // Every vertex has the normal of the face of the triangle (or zero if the triangle is degenerate). - let normal: Vec3 = self.normal().map_or(Vec3::ZERO, |n| n.into()); + let normal: Vec3 = normal_vec(self); let normals = vec![normal; 3]; let indices = Indices::U32(vec![0, 1, 2]); @@ -30,6 +30,12 @@ impl Meshable for Triangle3d { } } +/// The normal of a [`Triangle3d`] with zeroing so that a [`Vec3`] is always obtained for meshing. +#[inline] +pub(crate) fn normal_vec(triangle: &Triangle3d) -> Vec3 { + triangle.normal().map_or(Vec3::ZERO, |n| n.into()) +} + /// Unskewed uv-coordinates for a [`Triangle3d`]. #[inline] pub(crate) fn uv_coords(triangle: &Triangle3d) -> [[f32; 2]; 3] { diff --git a/examples/3d/3d_shapes.rs b/examples/3d/3d_shapes.rs index fda061bd5e421..183536b04dce7 100644 --- a/examples/3d/3d_shapes.rs +++ b/examples/3d/3d_shapes.rs @@ -39,6 +39,7 @@ fn setup( let shapes = [ meshes.add(Cuboid::default()), + meshes.add(Tetrahedron::default()), meshes.add(Capsule3d::default()), meshes.add(Torus::default()), meshes.add(Cylinder::default()),