Skip to content

Commit

Permalink
Common MeshBuilder trait (bevyengine#13411)
Browse files Browse the repository at this point in the history
# 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`
  • Loading branch information
lynn-lumen committed May 18, 2024
1 parent ee6dfd3 commit 450a920
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 101 deletions.
2 changes: 1 addition & 1 deletion crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
46 changes: 13 additions & 33 deletions crates/bevy_render/src/mesh/primitives/dim2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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()
}
}
Expand All @@ -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;

Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
13 changes: 4 additions & 9 deletions crates/bevy_render/src/mesh/primitives/dim3/capsule.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -437,9 +438,3 @@ impl From<Capsule3d> for Mesh {
capsule.mesh().build()
}
}

impl From<Capsule3dMeshBuilder> for Mesh {
fn from(capsule: Capsule3dMeshBuilder) -> Self {
capsule.build()
}
}
15 changes: 5 additions & 10 deletions crates/bevy_render/src/mesh/primitives/dim3/cone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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]]) {
Expand Down
13 changes: 4 additions & 9 deletions crates/bevy_render/src/mesh/primitives/dim3/cylinder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -176,9 +177,3 @@ impl From<Cylinder> for Mesh {
cylinder.mesh().build()
}
}

impl From<CylinderMeshBuilder> for Mesh {
fn from(cylinder: CylinderMeshBuilder) -> Self {
cylinder.build()
}
}
13 changes: 4 additions & 9 deletions crates/bevy_render/src/mesh/primitives/dim3/plane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -104,9 +105,3 @@ impl From<Plane3d> for Mesh {
plane.mesh().build()
}
}

impl From<PlaneMeshBuilder> for Mesh {
fn from(plane: PlaneMeshBuilder) -> Self {
plane.build()
}
}
36 changes: 16 additions & 20 deletions crates/bevy_render/src/mesh/primitives/dim3/sphere.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand All @@ -260,9 +262,3 @@ impl From<Sphere> for Mesh {
sphere.mesh().build()
}
}

impl From<SphereMeshBuilder> for Mesh {
fn from(sphere: SphereMeshBuilder) -> Self {
sphere.build()
}
}
13 changes: 4 additions & 9 deletions crates/bevy_render/src/mesh/primitives/dim3/torus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -158,9 +159,3 @@ impl From<Torus> for Mesh {
torus.mesh().build()
}
}

impl From<TorusMeshBuilder> for Mesh {
fn from(torus: TorusMeshBuilder) -> Self {
torus.build()
}
}
16 changes: 15 additions & 1 deletion crates/bevy_render/src/mesh/primitives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

0 comments on commit 450a920

Please sign in to comment.