Common MeshBuilder trait (#13411)
				
					
				
			# Objective
- All `ShapeMeshBuilder`s have some methods/implementations in common.
These are `fn build(&self) -> Mesh` and this implementation:
```rust
impl From<ShapeMeshBuilder> for Mesh { 
    fn from(builder: ShapeMeshBuilder) -> { 
        builder.build() 
    } 
}
``` 
- For the sake of consistency, these can be moved into a shared trait
## Solution
- Add `trait MeshBuilder` containing a `fn build(&self) -> Mesh` and
implementing `MeshBuilder for ShapeMeshBuilder`
- Implement `From<T: MeshBuilder> for Mesh`
## Migration Guide
- When calling `.build()` you need to import
`bevy_render::mesh::primitives::MeshBuilder`
			
			
This commit is contained in:
		
							parent
							
								
									ee6dfd35c9
								
							
						
					
					
						commit
						450a9202d0
					
				| @ -43,7 +43,7 @@ pub mod prelude { | ||||
|             Camera, ClearColor, ClearColorConfig, OrthographicProjection, PerspectiveProjection, | ||||
|             Projection, | ||||
|         }, | ||||
|         mesh::{morph::MorphWeights, primitives::Meshable, Mesh}, | ||||
|         mesh::{morph::MorphWeights, primitives::MeshBuilder, primitives::Meshable, Mesh}, | ||||
|         render_resource::Shader, | ||||
|         spatial_bundle::SpatialBundle, | ||||
|         texture::{image_texture_conversion::IntoDynamicImageError, Image, ImagePlugin}, | ||||
|  | ||||
| @ -4,7 +4,7 @@ use crate::{ | ||||
|     render_asset::RenderAssetUsages, | ||||
| }; | ||||
| 
 | ||||
| use super::Meshable; | ||||
| use super::{MeshBuilder, Meshable}; | ||||
| use bevy_math::primitives::{ | ||||
|     Annulus, Capsule2d, Circle, Ellipse, Rectangle, RegularPolygon, Triangle2d, Triangle3d, | ||||
|     WindingOrder, | ||||
| @ -48,9 +48,10 @@ impl CircleMeshBuilder { | ||||
|         self.resolution = resolution; | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     /// Builds a [`Mesh`] based on the configuration in `self`.
 | ||||
|     pub fn build(&self) -> Mesh { | ||||
| impl MeshBuilder for CircleMeshBuilder { | ||||
|     fn build(&self) -> Mesh { | ||||
|         RegularPolygon::new(self.circle.radius, self.resolution).mesh() | ||||
|     } | ||||
| } | ||||
| @ -72,12 +73,6 @@ impl From<Circle> for Mesh { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<CircleMeshBuilder> for Mesh { | ||||
|     fn from(circle: CircleMeshBuilder) -> Self { | ||||
|         circle.build() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Meshable for RegularPolygon { | ||||
|     type Output = Mesh; | ||||
| 
 | ||||
| @ -133,9 +128,10 @@ impl EllipseMeshBuilder { | ||||
|         self.resolution = resolution; | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     /// Builds a [`Mesh`] based on the configuration in `self`.
 | ||||
|     pub fn build(&self) -> Mesh { | ||||
| impl MeshBuilder for EllipseMeshBuilder { | ||||
|     fn build(&self) -> Mesh { | ||||
|         let mut indices = Vec::with_capacity((self.resolution - 2) * 3); | ||||
|         let mut positions = Vec::with_capacity(self.resolution); | ||||
|         let normals = vec![[0.0, 0.0, 1.0]; self.resolution]; | ||||
| @ -188,12 +184,6 @@ impl From<Ellipse> for Mesh { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<EllipseMeshBuilder> for Mesh { | ||||
|     fn from(ellipse: EllipseMeshBuilder) -> Self { | ||||
|         ellipse.build() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// A builder for creating a [`Mesh`] with an [`Annulus`] shape.
 | ||||
| pub struct AnnulusMeshBuilder { | ||||
|     /// The [`Annulus`] shape.
 | ||||
| @ -229,9 +219,10 @@ impl AnnulusMeshBuilder { | ||||
|         self.resolution = resolution; | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     /// Builds a [`Mesh`] based on the configuration in `self`.
 | ||||
|     pub fn build(&self) -> Mesh { | ||||
| impl MeshBuilder for AnnulusMeshBuilder { | ||||
|     fn build(&self) -> Mesh { | ||||
|         let inner_radius = self.annulus.inner_circle.radius; | ||||
|         let outer_radius = self.annulus.outer_circle.radius; | ||||
| 
 | ||||
| @ -306,12 +297,6 @@ impl From<Annulus> for Mesh { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<AnnulusMeshBuilder> for Mesh { | ||||
|     fn from(builder: AnnulusMeshBuilder) -> Self { | ||||
|         builder.build() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Meshable for Triangle2d { | ||||
|     type Output = Mesh; | ||||
| 
 | ||||
| @ -423,9 +408,10 @@ impl Capsule2dMeshBuilder { | ||||
|         self.resolution = resolution; | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     /// Builds a [`Mesh`] based on the configuration in `self`.
 | ||||
|     pub fn build(&self) -> Mesh { | ||||
| impl MeshBuilder for Capsule2dMeshBuilder { | ||||
|     fn build(&self) -> Mesh { | ||||
|         // The resolution is the number of vertices for one semicircle
 | ||||
|         let resolution = self.resolution as u32; | ||||
|         let vertex_count = 2 * self.resolution; | ||||
| @ -517,12 +503,6 @@ impl From<Capsule2d> for Mesh { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Capsule2dMeshBuilder> for Mesh { | ||||
|     fn from(capsule: Capsule2dMeshBuilder) -> Self { | ||||
|         capsule.build() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use bevy_math::primitives::RegularPolygon; | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| use crate::{ | ||||
|     mesh::{Indices, Mesh, Meshable}, | ||||
|     mesh::{Indices, Mesh, MeshBuilder, Meshable}, | ||||
|     render_asset::RenderAssetUsages, | ||||
| }; | ||||
| use bevy_math::{primitives::Capsule3d, Vec2, Vec3}; | ||||
| @ -91,9 +91,10 @@ impl Capsule3dMeshBuilder { | ||||
|         self.uv_profile = uv_profile; | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     /// Builds a [`Mesh`] based on the configuration in `self`.
 | ||||
|     pub fn build(&self) -> Mesh { | ||||
| impl MeshBuilder for Capsule3dMeshBuilder { | ||||
|     fn build(&self) -> Mesh { | ||||
|         // code adapted from https://behreajj.medium.com/making-a-capsule-mesh-via-script-in-five-3d-environments-c2214abf02db
 | ||||
|         let Capsule3dMeshBuilder { | ||||
|             capsule, | ||||
| @ -437,9 +438,3 @@ impl From<Capsule3d> for Mesh { | ||||
|         capsule.mesh().build() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Capsule3dMeshBuilder> for Mesh { | ||||
|     fn from(capsule: Capsule3dMeshBuilder) -> Self { | ||||
|         capsule.build() | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2,7 +2,7 @@ use bevy_math::{primitives::Cone, Vec3}; | ||||
| use wgpu::PrimitiveTopology; | ||||
| 
 | ||||
| use crate::{ | ||||
|     mesh::{Indices, Mesh, Meshable}, | ||||
|     mesh::{Indices, Mesh, MeshBuilder, Meshable}, | ||||
|     render_asset::RenderAssetUsages, | ||||
| }; | ||||
| 
 | ||||
| @ -43,9 +43,10 @@ impl ConeMeshBuilder { | ||||
|         self.resolution = resolution; | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     /// Builds a [`Mesh`] based on the configuration in `self`.
 | ||||
|     pub fn build(&self) -> Mesh { | ||||
| impl MeshBuilder for ConeMeshBuilder { | ||||
|     fn build(&self) -> Mesh { | ||||
|         let half_height = self.cone.height / 2.0; | ||||
| 
 | ||||
|         // `resolution` vertices for the base, `resolution` vertices for the bottom of the lateral surface,
 | ||||
| @ -157,17 +158,11 @@ impl From<Cone> for Mesh { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<ConeMeshBuilder> for Mesh { | ||||
|     fn from(cone: ConeMeshBuilder) -> Self { | ||||
|         cone.build() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use bevy_math::{primitives::Cone, Vec2}; | ||||
| 
 | ||||
|     use crate::mesh::{Mesh, Meshable, VertexAttributeValues}; | ||||
|     use crate::mesh::{primitives::MeshBuilder, Mesh, Meshable, VertexAttributeValues}; | ||||
| 
 | ||||
|     /// Rounds floats to handle floating point error in tests.
 | ||||
|     fn round_floats<const N: usize>(points: &mut [[f32; N]]) { | ||||
|  | ||||
| @ -2,7 +2,7 @@ use bevy_math::primitives::Cylinder; | ||||
| use wgpu::PrimitiveTopology; | ||||
| 
 | ||||
| use crate::{ | ||||
|     mesh::{Indices, Mesh, Meshable}, | ||||
|     mesh::{Indices, Mesh, MeshBuilder, Meshable}, | ||||
|     render_asset::RenderAssetUsages, | ||||
| }; | ||||
| 
 | ||||
| @ -58,9 +58,10 @@ impl CylinderMeshBuilder { | ||||
|         self.segments = segments; | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     /// Builds a [`Mesh`] based on the configuration in `self`.
 | ||||
|     pub fn build(&self) -> Mesh { | ||||
| impl MeshBuilder for CylinderMeshBuilder { | ||||
|     fn build(&self) -> Mesh { | ||||
|         let resolution = self.resolution; | ||||
|         let segments = self.segments; | ||||
| 
 | ||||
| @ -176,9 +177,3 @@ impl From<Cylinder> for Mesh { | ||||
|         cylinder.mesh().build() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<CylinderMeshBuilder> for Mesh { | ||||
|     fn from(cylinder: CylinderMeshBuilder) -> Self { | ||||
|         cylinder.build() | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2,7 +2,7 @@ use bevy_math::{primitives::Plane3d, Dir3, Quat, Vec2, Vec3}; | ||||
| use wgpu::PrimitiveTopology; | ||||
| 
 | ||||
| use crate::{ | ||||
|     mesh::{Indices, Mesh, Meshable}, | ||||
|     mesh::{Indices, Mesh, MeshBuilder, Meshable}, | ||||
|     render_asset::RenderAssetUsages, | ||||
| }; | ||||
| 
 | ||||
| @ -65,9 +65,10 @@ impl PlaneMeshBuilder { | ||||
|         self.plane.half_size = Vec2::new(width, height) / 2.0; | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     /// Builds a [`Mesh`] based on the configuration in `self`.
 | ||||
|     pub fn build(&self) -> Mesh { | ||||
| impl MeshBuilder for PlaneMeshBuilder { | ||||
|     fn build(&self) -> Mesh { | ||||
|         let rotation = Quat::from_rotation_arc(Vec3::Y, *self.plane.normal); | ||||
|         let positions = vec![ | ||||
|             rotation * Vec3::new(self.plane.half_size.x, 0.0, -self.plane.half_size.y), | ||||
| @ -104,9 +105,3 @@ impl From<Plane3d> for Mesh { | ||||
|         plane.mesh().build() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<PlaneMeshBuilder> for Mesh { | ||||
|     fn from(plane: PlaneMeshBuilder) -> Self { | ||||
|         plane.build() | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| use std::f32::consts::PI; | ||||
| 
 | ||||
| use crate::{ | ||||
|     mesh::{Indices, Mesh, Meshable}, | ||||
|     mesh::{Indices, Mesh, MeshBuilder, Meshable}, | ||||
|     render_asset::RenderAssetUsages, | ||||
| }; | ||||
| use bevy_math::primitives::Sphere; | ||||
| @ -75,19 +75,6 @@ impl SphereMeshBuilder { | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Builds a [`Mesh`] according to the configuration in `self`.
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// Panics if the sphere is a [`SphereKind::Ico`] with a subdivision count
 | ||||
|     /// that is greater than or equal to `80` because there will be too many vertices.
 | ||||
|     pub fn build(&self) -> Mesh { | ||||
|         match self.kind { | ||||
|             SphereKind::Ico { subdivisions } => self.ico(subdivisions).unwrap(), | ||||
|             SphereKind::Uv { sectors, stacks } => self.uv(sectors, stacks), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Creates an icosphere mesh with the given number of subdivisions.
 | ||||
|     ///
 | ||||
|     /// The number of faces quadruples with each subdivision.
 | ||||
| @ -244,6 +231,21 @@ impl SphereMeshBuilder { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl MeshBuilder for SphereMeshBuilder { | ||||
|     /// Builds a [`Mesh`] according to the configuration in `self`.
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// Panics if the sphere is a [`SphereKind::Ico`] with a subdivision count
 | ||||
|     /// that is greater than or equal to `80` because there will be too many vertices.
 | ||||
|     fn build(&self) -> Mesh { | ||||
|         match self.kind { | ||||
|             SphereKind::Ico { subdivisions } => self.ico(subdivisions).unwrap(), | ||||
|             SphereKind::Uv { sectors, stacks } => self.uv(sectors, stacks), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Meshable for Sphere { | ||||
|     type Output = SphereMeshBuilder; | ||||
| 
 | ||||
| @ -260,9 +262,3 @@ impl From<Sphere> for Mesh { | ||||
|         sphere.mesh().build() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<SphereMeshBuilder> for Mesh { | ||||
|     fn from(sphere: SphereMeshBuilder) -> Self { | ||||
|         sphere.build() | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2,7 +2,7 @@ use bevy_math::{primitives::Torus, Vec3}; | ||||
| use wgpu::PrimitiveTopology; | ||||
| 
 | ||||
| use crate::{ | ||||
|     mesh::{Indices, Mesh, Meshable}, | ||||
|     mesh::{Indices, Mesh, MeshBuilder, Meshable}, | ||||
|     render_asset::RenderAssetUsages, | ||||
| }; | ||||
| 
 | ||||
| @ -65,9 +65,10 @@ impl TorusMeshBuilder { | ||||
|         self.major_resolution = resolution; | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     /// Builds a [`Mesh`] according to the configuration in `self`.
 | ||||
|     pub fn build(&self) -> Mesh { | ||||
| impl MeshBuilder for TorusMeshBuilder { | ||||
|     fn build(&self) -> Mesh { | ||||
|         // code adapted from http://apparat-engine.blogspot.com/2013/04/procedural-meshes-torus.html
 | ||||
| 
 | ||||
|         let n_vertices = (self.major_resolution + 1) * (self.minor_resolution + 1); | ||||
| @ -158,9 +159,3 @@ impl From<Torus> for Mesh { | ||||
|         torus.mesh().build() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<TorusMeshBuilder> for Mesh { | ||||
|     fn from(torus: TorusMeshBuilder) -> Self { | ||||
|         torus.build() | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -25,12 +25,26 @@ pub use dim2::{CircleMeshBuilder, EllipseMeshBuilder}; | ||||
| mod dim3; | ||||
| pub use dim3::*; | ||||
| 
 | ||||
| use super::Mesh; | ||||
| 
 | ||||
| /// A trait for shapes that can be turned into a [`Mesh`](super::Mesh).
 | ||||
| pub trait Meshable { | ||||
|     /// The output of [`Self::mesh`]. This can either be a [`Mesh`](super::Mesh)
 | ||||
|     /// or a builder used for creating a [`Mesh`](super::Mesh).
 | ||||
|     /// or a [`MeshBuilder`] used for creating a [`Mesh`](super::Mesh).
 | ||||
|     type Output; | ||||
| 
 | ||||
|     /// Creates a [`Mesh`](super::Mesh) for a shape.
 | ||||
|     fn mesh(&self) -> Self::Output; | ||||
| } | ||||
| 
 | ||||
| /// A trait used to build [`Mesh`]es from a configuration
 | ||||
| pub trait MeshBuilder { | ||||
|     /// Builds a [`Mesh`] based on the configuration in `self`.
 | ||||
|     fn build(&self) -> Mesh; | ||||
| } | ||||
| 
 | ||||
| impl<T: MeshBuilder> From<T> for Mesh { | ||||
|     fn from(builder: T) -> Self { | ||||
|         builder.build() | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Lynn
						Lynn