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:
Lynn 2024-05-18 13:58:11 +02:00 committed by GitHub
parent ee6dfd35c9
commit 450a9202d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 66 additions and 101 deletions

View file

@ -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},

View file

@ -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;

View file

@ -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()
}
}

View file

@ -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]]) {

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -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()
}
}