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,
|
Camera, ClearColor, ClearColorConfig, OrthographicProjection, PerspectiveProjection,
|
||||||
Projection,
|
Projection,
|
||||||
},
|
},
|
||||||
mesh::{morph::MorphWeights, primitives::Meshable, Mesh},
|
mesh::{morph::MorphWeights, primitives::MeshBuilder, primitives::Meshable, Mesh},
|
||||||
render_resource::Shader,
|
render_resource::Shader,
|
||||||
spatial_bundle::SpatialBundle,
|
spatial_bundle::SpatialBundle,
|
||||||
texture::{image_texture_conversion::IntoDynamicImageError, Image, ImagePlugin},
|
texture::{image_texture_conversion::IntoDynamicImageError, Image, ImagePlugin},
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use crate::{
|
|||||||
render_asset::RenderAssetUsages,
|
render_asset::RenderAssetUsages,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Meshable;
|
use super::{MeshBuilder, Meshable};
|
||||||
use bevy_math::primitives::{
|
use bevy_math::primitives::{
|
||||||
Annulus, Capsule2d, Circle, Ellipse, Rectangle, RegularPolygon, Triangle2d, Triangle3d,
|
Annulus, Capsule2d, Circle, Ellipse, Rectangle, RegularPolygon, Triangle2d, Triangle3d,
|
||||||
WindingOrder,
|
WindingOrder,
|
||||||
@ -48,9 +48,10 @@ impl CircleMeshBuilder {
|
|||||||
self.resolution = resolution;
|
self.resolution = resolution;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a [`Mesh`] based on the configuration in `self`.
|
impl MeshBuilder for CircleMeshBuilder {
|
||||||
pub fn build(&self) -> Mesh {
|
fn build(&self) -> Mesh {
|
||||||
RegularPolygon::new(self.circle.radius, self.resolution).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 {
|
impl Meshable for RegularPolygon {
|
||||||
type Output = Mesh;
|
type Output = Mesh;
|
||||||
|
|
||||||
@ -133,9 +128,10 @@ impl EllipseMeshBuilder {
|
|||||||
self.resolution = resolution;
|
self.resolution = resolution;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a [`Mesh`] based on the configuration in `self`.
|
impl MeshBuilder for EllipseMeshBuilder {
|
||||||
pub fn build(&self) -> Mesh {
|
fn build(&self) -> Mesh {
|
||||||
let mut indices = Vec::with_capacity((self.resolution - 2) * 3);
|
let mut indices = Vec::with_capacity((self.resolution - 2) * 3);
|
||||||
let mut positions = Vec::with_capacity(self.resolution);
|
let mut positions = Vec::with_capacity(self.resolution);
|
||||||
let normals = vec![[0.0, 0.0, 1.0]; 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.
|
/// A builder for creating a [`Mesh`] with an [`Annulus`] shape.
|
||||||
pub struct AnnulusMeshBuilder {
|
pub struct AnnulusMeshBuilder {
|
||||||
/// The [`Annulus`] shape.
|
/// The [`Annulus`] shape.
|
||||||
@ -229,9 +219,10 @@ impl AnnulusMeshBuilder {
|
|||||||
self.resolution = resolution;
|
self.resolution = resolution;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a [`Mesh`] based on the configuration in `self`.
|
impl MeshBuilder for AnnulusMeshBuilder {
|
||||||
pub fn build(&self) -> Mesh {
|
fn build(&self) -> Mesh {
|
||||||
let inner_radius = self.annulus.inner_circle.radius;
|
let inner_radius = self.annulus.inner_circle.radius;
|
||||||
let outer_radius = self.annulus.outer_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 {
|
impl Meshable for Triangle2d {
|
||||||
type Output = Mesh;
|
type Output = Mesh;
|
||||||
|
|
||||||
@ -423,9 +408,10 @@ impl Capsule2dMeshBuilder {
|
|||||||
self.resolution = resolution;
|
self.resolution = resolution;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a [`Mesh`] based on the configuration in `self`.
|
impl MeshBuilder for Capsule2dMeshBuilder {
|
||||||
pub fn build(&self) -> Mesh {
|
fn build(&self) -> Mesh {
|
||||||
// The resolution is the number of vertices for one semicircle
|
// The resolution is the number of vertices for one semicircle
|
||||||
let resolution = self.resolution as u32;
|
let resolution = self.resolution as u32;
|
||||||
let vertex_count = 2 * self.resolution;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use bevy_math::primitives::RegularPolygon;
|
use bevy_math::primitives::RegularPolygon;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
mesh::{Indices, Mesh, Meshable},
|
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||||
render_asset::RenderAssetUsages,
|
render_asset::RenderAssetUsages,
|
||||||
};
|
};
|
||||||
use bevy_math::{primitives::Capsule3d, Vec2, Vec3};
|
use bevy_math::{primitives::Capsule3d, Vec2, Vec3};
|
||||||
@ -91,9 +91,10 @@ impl Capsule3dMeshBuilder {
|
|||||||
self.uv_profile = uv_profile;
|
self.uv_profile = uv_profile;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a [`Mesh`] based on the configuration in `self`.
|
impl MeshBuilder for Capsule3dMeshBuilder {
|
||||||
pub fn build(&self) -> Mesh {
|
fn build(&self) -> Mesh {
|
||||||
// code adapted from https://behreajj.medium.com/making-a-capsule-mesh-via-script-in-five-3d-environments-c2214abf02db
|
// code adapted from https://behreajj.medium.com/making-a-capsule-mesh-via-script-in-five-3d-environments-c2214abf02db
|
||||||
let Capsule3dMeshBuilder {
|
let Capsule3dMeshBuilder {
|
||||||
capsule,
|
capsule,
|
||||||
@ -437,9 +438,3 @@ impl From<Capsule3d> for Mesh {
|
|||||||
capsule.mesh().build()
|
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 wgpu::PrimitiveTopology;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
mesh::{Indices, Mesh, Meshable},
|
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||||
render_asset::RenderAssetUsages,
|
render_asset::RenderAssetUsages,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,9 +43,10 @@ impl ConeMeshBuilder {
|
|||||||
self.resolution = resolution;
|
self.resolution = resolution;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a [`Mesh`] based on the configuration in `self`.
|
impl MeshBuilder for ConeMeshBuilder {
|
||||||
pub fn build(&self) -> Mesh {
|
fn build(&self) -> Mesh {
|
||||||
let half_height = self.cone.height / 2.0;
|
let half_height = self.cone.height / 2.0;
|
||||||
|
|
||||||
// `resolution` vertices for the base, `resolution` vertices for the bottom of the lateral surface,
|
// `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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use bevy_math::{primitives::Cone, Vec2};
|
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.
|
/// Rounds floats to handle floating point error in tests.
|
||||||
fn round_floats<const N: usize>(points: &mut [[f32; N]]) {
|
fn round_floats<const N: usize>(points: &mut [[f32; N]]) {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use bevy_math::primitives::Cylinder;
|
|||||||
use wgpu::PrimitiveTopology;
|
use wgpu::PrimitiveTopology;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
mesh::{Indices, Mesh, Meshable},
|
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||||
render_asset::RenderAssetUsages,
|
render_asset::RenderAssetUsages,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -58,9 +58,10 @@ impl CylinderMeshBuilder {
|
|||||||
self.segments = segments;
|
self.segments = segments;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a [`Mesh`] based on the configuration in `self`.
|
impl MeshBuilder for CylinderMeshBuilder {
|
||||||
pub fn build(&self) -> Mesh {
|
fn build(&self) -> Mesh {
|
||||||
let resolution = self.resolution;
|
let resolution = self.resolution;
|
||||||
let segments = self.segments;
|
let segments = self.segments;
|
||||||
|
|
||||||
@ -176,9 +177,3 @@ impl From<Cylinder> for Mesh {
|
|||||||
cylinder.mesh().build()
|
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 wgpu::PrimitiveTopology;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
mesh::{Indices, Mesh, Meshable},
|
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||||
render_asset::RenderAssetUsages,
|
render_asset::RenderAssetUsages,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,9 +65,10 @@ impl PlaneMeshBuilder {
|
|||||||
self.plane.half_size = Vec2::new(width, height) / 2.0;
|
self.plane.half_size = Vec2::new(width, height) / 2.0;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a [`Mesh`] based on the configuration in `self`.
|
impl MeshBuilder for PlaneMeshBuilder {
|
||||||
pub fn build(&self) -> Mesh {
|
fn build(&self) -> Mesh {
|
||||||
let rotation = Quat::from_rotation_arc(Vec3::Y, *self.plane.normal);
|
let rotation = Quat::from_rotation_arc(Vec3::Y, *self.plane.normal);
|
||||||
let positions = vec![
|
let positions = vec![
|
||||||
rotation * Vec3::new(self.plane.half_size.x, 0.0, -self.plane.half_size.y),
|
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()
|
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 std::f32::consts::PI;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
mesh::{Indices, Mesh, Meshable},
|
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||||
render_asset::RenderAssetUsages,
|
render_asset::RenderAssetUsages,
|
||||||
};
|
};
|
||||||
use bevy_math::primitives::Sphere;
|
use bevy_math::primitives::Sphere;
|
||||||
@ -75,19 +75,6 @@ impl SphereMeshBuilder {
|
|||||||
self
|
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.
|
/// Creates an icosphere mesh with the given number of subdivisions.
|
||||||
///
|
///
|
||||||
/// The number of faces quadruples with each subdivision.
|
/// 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 {
|
impl Meshable for Sphere {
|
||||||
type Output = SphereMeshBuilder;
|
type Output = SphereMeshBuilder;
|
||||||
|
|
||||||
@ -260,9 +262,3 @@ impl From<Sphere> for Mesh {
|
|||||||
sphere.mesh().build()
|
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 wgpu::PrimitiveTopology;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
mesh::{Indices, Mesh, Meshable},
|
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||||
render_asset::RenderAssetUsages,
|
render_asset::RenderAssetUsages,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,9 +65,10 @@ impl TorusMeshBuilder {
|
|||||||
self.major_resolution = resolution;
|
self.major_resolution = resolution;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a [`Mesh`] according to the configuration in `self`.
|
impl MeshBuilder for TorusMeshBuilder {
|
||||||
pub fn build(&self) -> Mesh {
|
fn build(&self) -> Mesh {
|
||||||
// code adapted from http://apparat-engine.blogspot.com/2013/04/procedural-meshes-torus.html
|
// 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);
|
let n_vertices = (self.major_resolution + 1) * (self.minor_resolution + 1);
|
||||||
@ -158,9 +159,3 @@ impl From<Torus> for Mesh {
|
|||||||
torus.mesh().build()
|
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;
|
mod dim3;
|
||||||
pub use dim3::*;
|
pub use dim3::*;
|
||||||
|
|
||||||
|
use super::Mesh;
|
||||||
|
|
||||||
/// A trait for shapes that can be turned into a [`Mesh`](super::Mesh).
|
/// A trait for shapes that can be turned into a [`Mesh`](super::Mesh).
|
||||||
pub trait Meshable {
|
pub trait Meshable {
|
||||||
/// The output of [`Self::mesh`]. This can either be a [`Mesh`](super::Mesh)
|
/// 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;
|
type Output;
|
||||||
|
|
||||||
/// Creates a [`Mesh`](super::Mesh) for a shape.
|
/// Creates a [`Mesh`](super::Mesh) for a shape.
|
||||||
fn mesh(&self) -> Self::Output;
|
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