diff --git a/crates/bevy_math/src/primitives/dim2.rs b/crates/bevy_math/src/primitives/dim2.rs index fbb7811cfbb2e..934e85ae590b3 100644 --- a/crates/bevy_math/src/primitives/dim2.rs +++ b/crates/bevy_math/src/primitives/dim2.rs @@ -66,6 +66,12 @@ pub struct Circle { } impl Primitive2d for Circle {} +impl Default for Circle { + fn default() -> Self { + Self { radius: 0.5 } + } +} + /// An ellipse primitive #[derive(Clone, Copy, Debug)] pub struct Ellipse { @@ -76,6 +82,15 @@ pub struct Ellipse { } impl Primitive2d for Ellipse {} +impl Default for Ellipse { + fn default() -> Self { + Self { + half_width: 1.0, + half_height: 0.5, + } + } +} + impl Ellipse { /// Create a new `Ellipse` from a "width" and a "height" pub fn new(width: f32, height: f32) -> Self { @@ -95,6 +110,14 @@ pub struct Plane2d { } impl Primitive2d for Plane2d {} +impl Default for Plane2d { + fn default() -> Self { + Self { + normal: Direction2d::from_normalized(Vec2::Y), + } + } +} + impl Plane2d { /// Create a new `Plane2d` from a normal /// @@ -220,16 +243,24 @@ impl BoxedPolyline2d { } /// A triangle in 2D space -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Triangle2d { /// The vertices of the triangle pub vertices: [Vec2; 3], } impl Primitive2d for Triangle2d {} +impl Default for Triangle2d { + fn default() -> Self { + Self { + vertices: [Vec2::Y * 0.5, Vec2::new(-0.5, -0.5), Vec2::new(0.5, -0.5)], + } + } +} + impl Triangle2d { /// Create a new `Triangle2d` from points `a`, `b`, and `c` - pub fn new(a: Vec2, b: Vec2, c: Vec2) -> Self { + pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self { Self { vertices: [a, b, c], } @@ -267,6 +298,15 @@ pub struct Rectangle { } impl Primitive2d for Rectangle {} +impl Default for Rectangle { + fn default() -> Self { + Self { + half_width: 0.5, + half_height: 0.5, + } + } +} + impl Rectangle { /// Create a rectangle from a full width and height pub fn new(width: f32, height: f32) -> Self { @@ -347,18 +387,27 @@ pub struct RegularPolygon { } impl Primitive2d for RegularPolygon {} +impl Default for RegularPolygon { + fn default() -> Self { + Self { + circumcircle: Circle { radius: 0.5 }, + sides: 6, + } + } +} + impl RegularPolygon { /// Create a new `RegularPolygon` - /// from the radius of the circumcircle and number of sides + /// from the radius of the circumcircle and a number of sides /// /// # Panics /// - /// Panics if `circumcircle_radius` is non-positive - pub fn new(circumcircle_radius: f32, sides: usize) -> Self { - assert!(circumcircle_radius > 0.0); + /// Panics if `circumradius` is non-positive + pub fn new(circumradius: f32, sides: usize) -> Self { + assert!(circumradius > 0.0); Self { circumcircle: Circle { - radius: circumcircle_radius, + radius: circumradius, }, sides, } diff --git a/crates/bevy_math/src/primitives/dim3.rs b/crates/bevy_math/src/primitives/dim3.rs index 0177d03fc3620..57d4781558811 100644 --- a/crates/bevy_math/src/primitives/dim3.rs +++ b/crates/bevy_math/src/primitives/dim3.rs @@ -66,6 +66,12 @@ pub struct Sphere { } impl Primitive3d for Sphere {} +impl Default for Sphere { + fn default() -> Self { + Self { radius: 0.5 } + } +} + /// An unbounded plane in 3D space. It forms a separating surface through the origin, /// stretching infinitely far #[derive(Clone, Copy, Debug)] @@ -75,6 +81,14 @@ pub struct Plane3d { } impl Primitive3d for Plane3d {} +impl Default for Plane3d { + fn default() -> Self { + Self { + normal: Direction3d::from_normalized(Vec3::Y), + } + } +} + impl Plane3d { /// Create a new `Plane3d` from a normal /// @@ -206,6 +220,14 @@ pub struct Cuboid { } impl Primitive3d for Cuboid {} +impl Default for Cuboid { + fn default() -> Self { + Self { + half_extents: Vec3::splat(0.5), + } + } +} + impl Cuboid { /// Create a cuboid from a full x, y, and z length pub fn new(x_length: f32, y_length: f32, z_length: f32) -> Self { @@ -230,6 +252,15 @@ pub struct Cylinder { } impl Primitive3d for Cylinder {} +impl Default for Cylinder { + fn default() -> Self { + Self { + radius: 0.5, + half_height: 0.5, + } + } +} + impl Cylinder { /// Create a cylinder from a radius and full height pub fn new(radius: f32, height: f32) -> Self { @@ -252,6 +283,15 @@ pub struct Capsule { impl super::Primitive2d for Capsule {} impl Primitive3d for Capsule {} +impl Default for Capsule { + fn default() -> Self { + Self { + radius: 0.5, + half_length: 0.5, + } + } +} + impl Capsule { /// Create a new `Capsule` from a radius and length pub fn new(radius: f32, length: f32) -> Self { @@ -272,6 +312,15 @@ pub struct Cone { } impl Primitive3d for Cone {} +impl Default for Cone { + fn default() -> Self { + Self { + radius: 0.5, + height: 1.0, + } + } +} + /// A conical frustum primitive. /// A conical frustum can be created /// by slicing off a section of a cone. @@ -286,6 +335,18 @@ pub struct ConicalFrustum { } impl Primitive3d for ConicalFrustum {} +impl Default for ConicalFrustum { + fn default() -> Self { + Self { + // This produces the same shape as the default Cone, + // but trancated to half the height. + radius_top: 0.25, + radius_bottom: 0.5, + height: 0.5, + } + } +} + /// The type of torus determined by the minor and major radii #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum TorusKind { @@ -320,6 +381,15 @@ pub struct Torus { } impl Primitive3d for Torus {} +impl Default for Torus { + fn default() -> Self { + Self { + minor_radius: 0.25, + major_radius: 0.75, + } + } +} + impl Torus { /// Create a new `Torus` from an inner and outer radius. /// diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index a8243422d607d..aecc4003e161c 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -29,7 +29,7 @@ pub mod prelude { pub use crate::{ camera::{Camera, OrthographicProjection, PerspectiveProjection, Projection}, color::Color, - mesh::{morph::MorphWeights, shape, Mesh}, + mesh::{morph::MorphWeights, shape, shape::*, Mesh}, render_resource::Shader, spatial_bundle::SpatialBundle, texture::{Image, ImagePlugin}, diff --git a/crates/bevy_render/src/mesh/mod.rs b/crates/bevy_render/src/mesh/mod.rs index 39c9b19cc9e5b..cf2703389ce47 100644 --- a/crates/bevy_render/src/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mod.rs @@ -1,7 +1,6 @@ #[allow(clippy::module_inception)] mod mesh; pub mod morph; -/// Generation for some primitive shape meshes. pub mod shape; pub use mesh::*; diff --git a/crates/bevy_render/src/mesh/shape/capsule.rs b/crates/bevy_render/src/mesh/shape/capsule.rs index d438ed28bc3ee..f455d1133d973 100644 --- a/crates/bevy_render/src/mesh/shape/capsule.rs +++ b/crates/bevy_render/src/mesh/shape/capsule.rs @@ -1,38 +1,10 @@ +use super::Meshable; use crate::mesh::{Indices, Mesh}; -use bevy_math::{Vec2, Vec3}; +use bevy_math::{primitives::Capsule, Vec2, Vec3}; use wgpu::PrimitiveTopology; -/// A cylinder with hemispheres at the top and bottom -#[derive(Debug, Copy, Clone)] -pub struct Capsule { - /// Radius on the `XZ` plane. - pub radius: f32, - /// Number of sections in cylinder between hemispheres. - pub rings: usize, - /// Height of the middle cylinder on the `Y` axis, excluding the hemispheres. - pub depth: f32, - /// Number of latitudes, distributed by inclination. Must be even. - pub latitudes: usize, - /// Number of longitudes, or meridians, distributed by azimuth. - pub longitudes: usize, - /// Manner in which UV coordinates are distributed vertically. - pub uv_profile: CapsuleUvProfile, -} -impl Default for Capsule { - fn default() -> Self { - Capsule { - radius: 0.5, - rings: 0, - depth: 1.0, - latitudes: 16, - longitudes: 32, - uv_profile: CapsuleUvProfile::Aspect, - } - } -} - -#[derive(Debug, Default, Clone, Copy)] /// Manner in which UV coordinates are distributed vertically. +#[derive(Clone, Copy, Debug, Default)] pub enum CapsuleUvProfile { /// UV space is distributed by how much of the capsule consists of the hemispheres. #[default] @@ -44,18 +16,93 @@ pub enum CapsuleUvProfile { Fixed, } -impl From for Mesh { - #[allow(clippy::needless_range_loop)] - fn from(capsule: Capsule) -> Self { - // code adapted from https://behreajj.medium.com/making-a-capsule-mesh-via-script-in-five-3d-environments-c2214abf02db +/// A builder used for creating a [`Mesh`] with a [`Capsule`] shape. +#[derive(Clone, Copy, Debug)] +pub struct CapsuleMesh { + /// The [`Capsule`] shape. + pub capsule: Capsule, + /// The number of horizontal lines subdividing the cylindrical part of the capsule. + /// The default is `0`. + pub rings: usize, + /// The number of vertical lines subdividing the hemispheres of the capsule. + /// The default is `32`. + pub longitudes: usize, + /// The number of horizontal lines subdividing the hemispheres of the capsule. + /// The default is `16`. + pub latitudes: usize, + /// The manner in which UV coordinates are distributed vertically. + /// The default is [`CapsuleUvProfile::Aspect`]. + pub uv_profile: CapsuleUvProfile, +} - let Capsule { - radius, - rings, - depth, +impl Default for CapsuleMesh { + fn default() -> Self { + Self { + capsule: Capsule::default(), + rings: 0, + longitudes: 32, + latitudes: 16, + uv_profile: CapsuleUvProfile::default(), + } + } +} + +impl CapsuleMesh { + /// Creates a new [`CapsuleMesh`] from a given radius, height, longitudes and latitudes. + /// + /// Note that `height` is the distance between the centers of the hemispheres. + /// `radius` will be added to both ends to get the real height of the mesh. + #[inline] + pub fn new(radius: f32, height: f32, longitudes: usize, latitudes: usize) -> Self { + Self { + capsule: Capsule::new(radius, height), + longitudes, latitudes, + ..Default::default() + } + } + + /// Sets the number of horizontal lines subdividing the cylindrical part of the capsule. + #[inline] + pub const fn rings(mut self, rings: usize) -> Self { + self.rings = rings; + self + } + + /// Sets the number of vertical lines subdividing the hemispheres of the capsule. + #[inline] + pub const fn longitudes(mut self, longitudes: usize) -> Self { + self.longitudes = longitudes; + self + } + + /// Sets the number of horizontal lines subdividing the hemispheres of the capsule. + #[inline] + pub const fn latitudes(mut self, latitudes: usize) -> Self { + self.latitudes = latitudes; + self + } + + /// Sets the manner in which UV coordinates are distributed vertically. + #[inline] + pub const fn uv_profile(mut self, uv_profile: CapsuleUvProfile) -> Self { + self.uv_profile = uv_profile; + self + } + + /// Builds a [`Mesh`] based on the configuration in `self`. + pub fn build(&self) -> Mesh { + // code adapted from https://behreajj.medium.com/making-a-capsule-mesh-via-script-in-five-3d-environments-c2214abf02db + let CapsuleMesh { + capsule, + rings, longitudes, + latitudes, uv_profile, + } = *self; + let Capsule { + radius, + half_length, } = capsule; let calc_middle = rings > 0; @@ -64,8 +111,7 @@ impl From for Mesh { let half_latsn2 = half_lats - 2; let ringsp1 = rings + 1; let lonsp1 = longitudes + 1; - let half_depth = depth * 0.5; - let summit = half_depth + radius; + let summit = half_length + radius; // Vertex index offsets. let vert_offset_north_hemi = longitudes; @@ -93,7 +139,7 @@ impl From for Mesh { let to_tex_vertical = 1.0 / half_lats as f32; let vt_aspect_ratio = match uv_profile { - CapsuleUvProfile::Aspect => radius / (depth + radius + radius), + CapsuleUvProfile::Aspect => radius / (2.0 * half_length + radius + radius), CapsuleUvProfile::Uniform => half_lats as f32 / (ringsp1 + latitudes) as f32, CapsuleUvProfile::Fixed => 1.0 / 3.0, }; @@ -128,9 +174,9 @@ impl From for Mesh { } // Equatorial vertices. - for j in 0..lonsp1 { + for (j, s_texture_cache_j) in s_texture_cache.iter_mut().enumerate().take(lonsp1) { let s_texture = 1.0 - j as f32 * to_tex_horizontal; - s_texture_cache[j] = s_texture; + *s_texture_cache_j = s_texture; // Wrap to first element upon reaching last. let j_mod = j % longitudes; @@ -139,13 +185,13 @@ impl From for Mesh { // North equator. let idxn = vert_offset_north_equator + j; - vs[idxn] = Vec3::new(rtc.x, half_depth, -rtc.y); + vs[idxn] = Vec3::new(rtc.x, half_length, -rtc.y); vts[idxn] = Vec2::new(s_texture, vt_aspect_north); vns[idxn] = Vec3::new(tc.x, 0.0, -tc.y); // South equator. let idxs = vert_offset_south_equator + j; - vs[idxs] = Vec3::new(rtc.x, -half_depth, -rtc.y); + vs[idxs] = Vec3::new(rtc.x, -half_length, -rtc.y); vts[idxs] = Vec2::new(s_texture, vt_aspect_south); vns[idxs] = Vec3::new(tc.x, 0.0, -tc.y); } @@ -166,11 +212,11 @@ impl From for Mesh { let rho_cos_phi_north = radius * cos_phi_north; let rho_sin_phi_north = radius * sin_phi_north; - let z_offset_north = half_depth - rho_sin_phi_north; + let z_offset_north = half_length - rho_sin_phi_north; let rho_cos_phi_south = radius * cos_phi_south; let rho_sin_phi_south = radius * sin_phi_south; - let z_offset_sout = -half_depth - rho_sin_phi_south; + let z_offset_sout = -half_length - rho_sin_phi_south; // For texture coordinates. let t_tex_fac = ip1f * to_tex_vertical; @@ -182,10 +228,9 @@ impl From for Mesh { let vert_curr_lat_north = vert_offset_north_hemi + i_lonsp1; let vert_curr_lat_south = vert_offset_south_hemi + i_lonsp1; - for j in 0..lonsp1 { + for (j, s_texture) in s_texture_cache.iter().enumerate().take(lonsp1) { let j_mod = j % longitudes; - let s_texture = s_texture_cache[j]; let tc = theta_cartesian[j_mod]; // North hemisphere. @@ -195,7 +240,7 @@ impl From for Mesh { z_offset_north, -rho_cos_phi_north * tc.y, ); - vts[idxn] = Vec2::new(s_texture, t_tex_north); + vts[idxn] = Vec2::new(*s_texture, t_tex_north); vns[idxn] = Vec3::new(cos_phi_north * tc.x, -sin_phi_north, -cos_phi_north * tc.y); // South hemisphere. @@ -205,7 +250,7 @@ impl From for Mesh { z_offset_sout, -rho_cos_phi_south * tc.y, ); - vts[idxs] = Vec2::new(s_texture, t_tex_south); + vts[idxs] = Vec2::new(*s_texture, t_tex_south); vns[idxs] = Vec3::new(cos_phi_south * tc.x, -sin_phi_south, -cos_phi_south * tc.y); } } @@ -221,16 +266,15 @@ impl From for Mesh { let fac = h as f32 * to_fac; let cmpl_fac = 1.0 - fac; let t_texture = cmpl_fac * vt_aspect_north + fac * vt_aspect_south; - let z = half_depth - depth * fac; + let z = half_length - 2.0 * half_length * fac; - for j in 0..lonsp1 { + for (j, s_texture) in s_texture_cache.iter().enumerate().take(lonsp1) { let j_mod = j % longitudes; let tc = theta_cartesian[j_mod]; let rtc = rho_theta_cartesian[j_mod]; - let s_texture = s_texture_cache[j]; vs[idx_cyl_lat] = Vec3::new(rtc.x, z, -rtc.y); - vts[idx_cyl_lat] = Vec2::new(s_texture, t_texture); + vts[idx_cyl_lat] = Vec2::new(*s_texture, t_texture); vns[idx_cyl_lat] = Vec3::new(tc.x, 0.0, -tc.y); idx_cyl_lat += 1; @@ -371,3 +415,26 @@ impl From for Mesh { .with_indices(Some(Indices::U32(tris))) } } + +impl Meshable for Capsule { + type Output = CapsuleMesh; + + fn mesh(&self) -> Self::Output { + CapsuleMesh { + capsule: *self, + ..Default::default() + } + } +} + +impl From for Mesh { + fn from(capsule: Capsule) -> Self { + capsule.mesh().build() + } +} + +impl From for Mesh { + fn from(capsule: CapsuleMesh) -> Self { + capsule.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/circle.rs b/crates/bevy_render/src/mesh/shape/circle.rs new file mode 100644 index 0000000000000..5e88b24f2e1ef --- /dev/null +++ b/crates/bevy_render/src/mesh/shape/circle.rs @@ -0,0 +1,101 @@ +use crate::mesh::Mesh; + +use super::{Facing, MeshFacingExtension, Meshable}; +use bevy_math::primitives::{Circle, RegularPolygon}; + +/// A builder used for creating a [`Mesh`] with a [`Circle`] shape. +#[derive(Clone, Copy, Debug)] +pub struct CircleMesh { + /// The [`Circle`] shape. + pub circle: Circle, + /// The number of vertices used for the circle mesh. + /// The default is `32`. + #[doc(alias = "vertices")] + pub resolution: usize, + /// The XYZ direction that the mesh is facing. + /// The default is [`Facing::Z`]. + pub facing: Facing, +} + +impl Default for CircleMesh { + fn default() -> Self { + Self { + circle: Circle::default(), + resolution: 32, + facing: Facing::Z, + } + } +} + +impl MeshFacingExtension for CircleMesh { + #[inline] + fn facing(mut self, facing: Facing) -> Self { + self.facing = facing; + self + } +} + +impl CircleMesh { + /// Creates a new [`CircleMesh`] from a given radius and vertex count. + #[inline] + pub const fn new(radius: f32, resolution: usize) -> Self { + Self { + circle: Circle { radius }, + resolution, + facing: Facing::Z, + } + } + + /// Sets the number of resolution used for the circle mesh. + #[inline] + #[doc(alias = "vertices")] + pub const fn resolution(mut self, resolution: usize) -> Self { + self.resolution = resolution; + self + } + + /// Builds a [`Mesh`] based on the configuration in `self`. + pub fn build(&self) -> Mesh { + RegularPolygon::new(self.circle.radius, self.resolution) + .mesh() + .facing(self.facing) + .build() + } + + pub(super) fn build_mesh_data( + &self, + translation: [f32; 3], + indices: &mut Vec, + positions: &mut Vec<[f32; 3]>, + normals: &mut Vec<[f32; 3]>, + uvs: &mut Vec<[f32; 2]>, + ) { + RegularPolygon::new(self.circle.radius, self.resolution) + .mesh() + .facing(self.facing) + .build_mesh_data(translation, indices, positions, normals, uvs); + } +} + +impl Meshable for Circle { + type Output = CircleMesh; + + fn mesh(&self) -> Self::Output { + CircleMesh { + circle: *self, + ..Default::default() + } + } +} + +impl From for Mesh { + fn from(circle: Circle) -> Self { + circle.mesh().build() + } +} + +impl From for Mesh { + fn from(circle: CircleMesh) -> Self { + circle.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/cone.rs b/crates/bevy_render/src/mesh/shape/cone.rs new file mode 100644 index 0000000000000..11ac1f89d1412 --- /dev/null +++ b/crates/bevy_render/src/mesh/shape/cone.rs @@ -0,0 +1,124 @@ +use super::{CircleMesh, MeshFacingExtension, Meshable}; +use crate::mesh::{Indices, Mesh}; +use bevy_math::primitives::Cone; +use wgpu::PrimitiveTopology; + +/// A builder used for creating a [`Mesh`] with a [`Cone`] shape. +#[derive(Clone, Copy, Debug)] +pub struct ConeMesh { + /// The [`Cone`] shape. + pub cone: Cone, + /// The number of vertices used for the base of the cone. + /// The default is `32`. + pub resolution: usize, +} + +impl Default for ConeMesh { + fn default() -> Self { + Self { + cone: Cone::default(), + resolution: 32, + } + } +} + +impl ConeMesh { + /// Creates a new [`ConeMesh`] from a given radius, height, + /// and number of vertices used for the base of the cone. + #[inline] + pub const fn new(radius: f32, height: f32, resolution: usize) -> Self { + Self { + cone: Cone { radius, height }, + resolution, + } + } + + /// Sets the number of vertices used for the base of the cone. + #[inline] + pub const fn resolution(mut self, resolution: usize) -> Self { + self.resolution = resolution; + self + } + + /// Builds a [`Mesh`] based on the configuration in `self`. + pub fn build(&self) -> Mesh { + let Cone { radius, height } = self.cone; + let num_vertices = self.resolution * 2 + 1; + let num_indices = self.resolution * 3; + + let mut positions = Vec::with_capacity(num_vertices); + let mut normals = Vec::with_capacity(num_vertices); + let mut uvs = Vec::with_capacity(num_vertices); + let mut indices = Vec::with_capacity(num_indices); + + // Tip + positions.push([0.0, self.cone.height / 2.0, 0.0]); + + // The tip doesn't have a singular normal that works correctly. + // We use an invalid normal here so that it becomes NaN in the fragment shader + // and doesn't affect the overall shading. This might seem hacky, but it's one of + // the only ways to get perfectly smooth cones without creases or other shading artefacts. + // + // Note that this requires that normals are not normalized in the vertex shader, + // as that would make the entire triangle invalid and make the cone appear as black. + normals.push([0.0, 0.0, 0.0]); + + uvs.push([0.5, 0.5]); + + // Lateral surface, i.e. the side of the cone + let step_theta = std::f32::consts::TAU / self.resolution as f32; + for segment in 0..=self.resolution { + let theta = segment as f32 * step_theta; + let (sin, cos) = theta.sin_cos(); + + positions.push([radius * cos, -height / 2.0, radius * sin]); + normals.push([cos, 0., sin]); + uvs.push([0.5 + cos * 0.5, 0.5 + sin * 0.5]); + } + + for j in 0..self.resolution { + indices.extend_from_slice(&[0, j as u32 + 1, j as u32]); + } + + indices.extend(&[0, positions.len() as u32 - 1, positions.len() as u32 - 2]); + + // Base + let base = CircleMesh::new(radius, self.resolution).facing_neg_y(); + base.build_mesh_data( + [0.0, -height / 2.0, 0.0], + &mut indices, + &mut positions, + &mut normals, + &mut uvs, + ); + + Mesh::new(PrimitiveTopology::TriangleList) + .with_indices(Some(Indices::U32(indices))) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + } +} + +impl Meshable for Cone { + type Output = ConeMesh; + + fn mesh(&self) -> Self::Output { + ConeMesh { + cone: *self, + ..Default::default() + } + } +} + +impl From for Mesh { + fn from(cone: Cone) -> Self { + cone.mesh().build() + } +} + +impl From for Mesh { + fn from(cone: ConeMesh) -> Self { + cone.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/conical_frustum.rs b/crates/bevy_render/src/mesh/shape/conical_frustum.rs new file mode 100644 index 0000000000000..b15ae816432c2 --- /dev/null +++ b/crates/bevy_render/src/mesh/shape/conical_frustum.rs @@ -0,0 +1,170 @@ +use super::{CircleMesh, MeshFacingExtension, Meshable}; +use crate::mesh::{Indices, Mesh}; +use bevy_math::{primitives::ConicalFrustum, Vec3}; +use wgpu::PrimitiveTopology; + +/// A builder used for creating a [`Mesh`] with a [`ConicalFrustum`] shape. +#[derive(Clone, Copy, Debug)] +pub struct ConicalFrustumMesh { + /// The [`ConicalFrustum`] shape. + pub frustum: ConicalFrustum, + /// The number of vertices used for the top and bottom of the conical frustum. + /// The default is `32`. + pub resolution: usize, + /// The number of horizontal lines subdividing the lateral surface of the conical frustum. + /// The default is `1`. + pub segments: usize, +} + +impl Default for ConicalFrustumMesh { + fn default() -> Self { + Self { + frustum: ConicalFrustum::default(), + resolution: 32, + segments: 1, + } + } +} + +impl ConicalFrustumMesh { + /// Creates a new [`ConicalFrustumMesh`] from the given top and bottom radii, a height, + /// and a resolution used for the top and bottom. + #[inline] + pub const fn new(radius_top: f32, radius_bottom: f32, height: f32, resolution: usize) -> Self { + Self { + frustum: ConicalFrustum { + radius_top, + radius_bottom, + height, + }, + resolution, + segments: 1, + } + } + + /// Sets the number of vertices used for the top and bottom of the conical frustum. + #[inline] + pub const fn resolution(mut self, resolution: usize) -> Self { + self.resolution = resolution; + self + } + + /// Sets the number of horizontal lines subdividing the lateral surface of the conical frustum. + #[inline] + pub const fn segments(mut self, segments: usize) -> Self { + self.segments = segments; + self + } + + /// Builds a [`Mesh`] based on the configuration in `self`. + pub fn build(&self) -> Mesh { + debug_assert!(self.resolution > 2); + debug_assert!(self.segments > 0); + + let ConicalFrustum { + radius_top, + radius_bottom, + height, + } = self.frustum; + + let num_rings = self.segments + 1; + let num_vertices = self.resolution * 2 + num_rings * (self.resolution + 1); + let num_faces = self.resolution * (num_rings - 2); + let num_indices = (2 * num_faces + 2 * (self.resolution - 1) * 2) * 3; + + let mut positions = Vec::with_capacity(num_vertices); + let mut normals = Vec::with_capacity(num_vertices); + let mut uvs = Vec::with_capacity(num_vertices); + let mut indices = Vec::with_capacity(num_indices); + + let step_theta = std::f32::consts::TAU / self.resolution as f32; + let step_y = height / self.segments as f32; + let step_radius = (radius_top - radius_bottom) / self.segments as f32; + + // Rings + for ring in 0..num_rings { + let y = -height / 2.0 + ring as f32 * step_y; + let radius = radius_bottom + ring as f32 * step_radius; + + for segment in 0..=self.resolution { + let theta = segment as f32 * step_theta; + let (sin, cos) = theta.sin_cos(); + + positions.push([radius * cos, y, radius * sin]); + normals.push( + Vec3::new(cos, (radius_bottom - radius_top) / height, sin) + .normalize() + .to_array(), + ); + uvs.push([ + segment as f32 / self.resolution as f32, + ring as f32 / self.segments as f32, + ]); + } + } + + // Lateral surface + for i in 0..self.segments as u32 { + let ring = i * (self.resolution + 1) as u32; + let next_ring = (i + 1) * (self.resolution + 1) as u32; + + for j in 0..self.resolution as u32 { + indices.extend_from_slice(&[ + ring + j, + next_ring + j, + ring + j + 1, + next_ring + j, + next_ring + j + 1, + ring + j + 1, + ]); + } + } + + let top = CircleMesh::new(self.frustum.radius_top, self.resolution).facing_y(); + top.build_mesh_data( + [0.0, self.frustum.height / 2.0, 0.0], + &mut indices, + &mut positions, + &mut normals, + &mut uvs, + ); + + let bottom = CircleMesh::new(self.frustum.radius_bottom, self.resolution).facing_neg_y(); + bottom.build_mesh_data( + [0.0, -self.frustum.height / 2.0, 0.0], + &mut indices, + &mut positions, + &mut normals, + &mut uvs, + ); + + Mesh::new(PrimitiveTopology::TriangleList) + .with_indices(Some(Indices::U32(indices))) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + } +} + +impl Meshable for ConicalFrustum { + type Output = ConicalFrustumMesh; + + fn mesh(&self) -> Self::Output { + ConicalFrustumMesh { + frustum: *self, + ..Default::default() + } + } +} + +impl From for Mesh { + fn from(frustum: ConicalFrustum) -> Self { + frustum.mesh().build() + } +} + +impl From for Mesh { + fn from(frustum: ConicalFrustumMesh) -> Self { + frustum.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/cuboid.rs b/crates/bevy_render/src/mesh/shape/cuboid.rs new file mode 100644 index 0000000000000..8d0d687d01d67 --- /dev/null +++ b/crates/bevy_render/src/mesh/shape/cuboid.rs @@ -0,0 +1,100 @@ +use super::{Mesh, Meshable}; +use crate::mesh::Indices; +use bevy_math::primitives::Cuboid; +use wgpu::PrimitiveTopology; + +/// A builder used for creating a [`Mesh`] with a [`Cuboid`] shape. +#[derive(Clone, Copy, Debug, Default)] +pub struct CuboidMesh { + /// The [`Cuboid`] shape. + pub cuboid: Cuboid, +} + +impl CuboidMesh { + /// Creates a new [`CuboidMesh`] from a full `X`, `Y`, and `Z` length. + #[inline] + pub fn new(x_length: f32, y_length: f32, z_length: f32) -> Self { + Self { + cuboid: Cuboid::new(x_length, y_length, z_length), + } + } + + /// Builds a [`Mesh`] based on the configuration in `self`. + pub fn build(&self) -> Mesh { + let min = -self.cuboid.half_extents; + let max = self.cuboid.half_extents; + + // Suppose Y-up right hand, and camera look from +Z to -Z + let vertices = &[ + // Front + ([min.x, min.y, max.z], [0.0, 0.0, 1.0], [0.0, 0.0]), + ([max.x, min.y, max.z], [0.0, 0.0, 1.0], [1.0, 0.0]), + ([max.x, max.y, max.z], [0.0, 0.0, 1.0], [1.0, 1.0]), + ([min.x, max.y, max.z], [0.0, 0.0, 1.0], [0.0, 1.0]), + // Back + ([min.x, max.y, min.z], [0.0, 0.0, -1.0], [1.0, 0.0]), + ([max.x, max.y, min.z], [0.0, 0.0, -1.0], [0.0, 0.0]), + ([max.x, min.y, min.z], [0.0, 0.0, -1.0], [0.0, 1.0]), + ([min.x, min.y, min.z], [0.0, 0.0, -1.0], [1.0, 1.0]), + // Right + ([max.x, min.y, min.z], [1.0, 0.0, 0.0], [0.0, 0.0]), + ([max.x, max.y, min.z], [1.0, 0.0, 0.0], [1.0, 0.0]), + ([max.x, max.y, max.z], [1.0, 0.0, 0.0], [1.0, 1.0]), + ([max.x, min.y, max.z], [1.0, 0.0, 0.0], [0.0, 1.0]), + // Left + ([min.x, min.y, max.z], [-1.0, 0.0, 0.0], [1.0, 0.0]), + ([min.x, max.y, max.z], [-1.0, 0.0, 0.0], [0.0, 0.0]), + ([min.x, max.y, min.z], [-1.0, 0.0, 0.0], [0.0, 1.0]), + ([min.x, min.y, min.z], [-1.0, 0.0, 0.0], [1.0, 1.0]), + // Top + ([max.x, max.y, min.z], [0.0, 1.0, 0.0], [1.0, 0.0]), + ([min.x, max.y, min.z], [0.0, 1.0, 0.0], [0.0, 0.0]), + ([min.x, max.y, max.z], [0.0, 1.0, 0.0], [0.0, 1.0]), + ([max.x, max.y, max.z], [0.0, 1.0, 0.0], [1.0, 1.0]), + // Bottom + ([max.x, min.y, max.z], [0.0, -1.0, 0.0], [0.0, 0.0]), + ([min.x, min.y, max.z], [0.0, -1.0, 0.0], [1.0, 0.0]), + ([min.x, min.y, min.z], [0.0, -1.0, 0.0], [1.0, 1.0]), + ([max.x, min.y, min.z], [0.0, -1.0, 0.0], [0.0, 1.0]), + ]; + + let positions: Vec<_> = vertices.iter().map(|(p, _, _)| *p).collect(); + let normals: Vec<_> = vertices.iter().map(|(_, n, _)| *n).collect(); + let uvs: Vec<_> = vertices.iter().map(|(_, _, uv)| *uv).collect(); + + let indices = Indices::U32(vec![ + 0, 1, 2, 2, 3, 0, // front + 4, 5, 6, 6, 7, 4, // back + 8, 9, 10, 10, 11, 8, // right + 12, 13, 14, 14, 15, 12, // left + 16, 17, 18, 18, 19, 16, // top + 20, 21, 22, 22, 23, 20, // bottom + ]); + + Mesh::new(PrimitiveTopology::TriangleList) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + .with_indices(Some(indices)) + } +} + +impl Meshable for Cuboid { + type Output = CuboidMesh; + + fn mesh(&self) -> Self::Output { + CuboidMesh { cuboid: *self } + } +} + +impl From for Mesh { + fn from(cuboid: Cuboid) -> Self { + cuboid.mesh().build() + } +} + +impl From for Mesh { + fn from(cuboid: CuboidMesh) -> Self { + cuboid.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/cylinder.rs b/crates/bevy_render/src/mesh/shape/cylinder.rs index a4d517ac73952..aabad9fe1f117 100644 --- a/crates/bevy_render/src/mesh/shape/cylinder.rs +++ b/crates/bevy_render/src/mesh/shape/cylinder.rs @@ -1,79 +1,108 @@ -use crate::mesh::{Indices, Mesh}; +use super::Meshable; +use crate::mesh::{ + shape::{CircleMesh, MeshFacingExtension}, + Indices, Mesh, +}; +use bevy_math::primitives::Cylinder; use wgpu::PrimitiveTopology; -/// A cylinder which stands on the XZ plane +/// A builder used for creating a [`Mesh`] with a [`Cylinder`] shape. #[derive(Clone, Copy, Debug)] -pub struct Cylinder { - /// Radius in the XZ plane. - pub radius: f32, - /// Height of the cylinder in the Y axis. - pub height: f32, - /// The number of vertices around each horizontal slice of the cylinder. If you are looking at the cylinder from - /// above, this is the number of points you will see on the circle. - /// A higher number will make it appear more circular. +pub struct CylinderMesh { + /// The [`Cylinder`] shape. + pub cylinder: Cylinder, + /// The number of vertices used for the top and bottom of the cylinder. + /// The default is `32`. pub resolution: u32, - /// The number of segments between the two ends. Setting this to 1 will have triangles spanning the full - /// height of the cylinder. Setting it to 2 will have two sets of triangles with a horizontal slice in the middle of - /// cylinder. Greater numbers increase triangles/slices in the same way. + /// The number of segments along the height of the cylinder. + /// Must be greater than `0` for geometry to be generated. + /// The default is `1`. pub segments: u32, } -impl Default for Cylinder { +impl Default for CylinderMesh { fn default() -> Self { Self { - radius: 0.5, - height: 1.0, - resolution: 16, + cylinder: Cylinder::default(), + resolution: 32, segments: 1, } } } -impl From for Mesh { - fn from(c: Cylinder) -> Self { - debug_assert!(c.radius > 0.0); - debug_assert!(c.height > 0.0); - debug_assert!(c.resolution > 2); - debug_assert!(c.segments > 0); +impl CylinderMesh { + /// Creates a new [`CylinderMesh`] from the given radius, a height, + /// and a resolution used for the top and bottom. + #[inline] + pub fn new(radius: f32, height: f32, resolution: u32) -> Self { + Self { + cylinder: Cylinder::new(radius, height), + resolution, + ..Default::default() + } + } + + /// Sets the number of vertices used for the top and bottom of the cylinder. + #[inline] + pub const fn resolution(mut self, resolution: u32) -> Self { + self.resolution = resolution; + self + } + + /// Sets the number of segments along the height of the cylinder. + /// Must be greater than `0` for geometry to be generated. + #[inline] + pub const fn segments(mut self, segments: u32) -> Self { + self.segments = segments; + self + } + + /// Builds a [`Mesh`] based on the configuration in `self`. + pub fn build(&self) -> Mesh { + let resolution = self.resolution; + let segments = self.segments; - let num_rings = c.segments + 1; - let num_vertices = c.resolution * 2 + num_rings * (c.resolution + 1); - let num_faces = c.resolution * (num_rings - 2); - let num_indices = (2 * num_faces + 2 * (c.resolution - 1) * 2) * 3; + debug_assert!(resolution > 2); + debug_assert!(segments > 0); + + let num_rings = segments + 1; + let num_vertices = resolution * 2 + num_rings * (resolution + 1); + let num_faces = resolution * (num_rings - 2); + let num_indices = (2 * num_faces + 2 * (resolution - 1) * 2) * 3; let mut positions = Vec::with_capacity(num_vertices as usize); let mut normals = Vec::with_capacity(num_vertices as usize); let mut uvs = Vec::with_capacity(num_vertices as usize); let mut indices = Vec::with_capacity(num_indices as usize); - let step_theta = std::f32::consts::TAU / c.resolution as f32; - let step_y = c.height / c.segments as f32; + let step_theta = std::f32::consts::TAU / resolution as f32; + let step_y = 2.0 * self.cylinder.half_height / segments as f32; // rings for ring in 0..num_rings { - let y = -c.height / 2.0 + ring as f32 * step_y; + let y = -self.cylinder.half_height + ring as f32 * step_y; - for segment in 0..=c.resolution { + for segment in 0..=resolution { let theta = segment as f32 * step_theta; let (sin, cos) = theta.sin_cos(); - positions.push([c.radius * cos, y, c.radius * sin]); + positions.push([self.cylinder.radius * cos, y, self.cylinder.radius * sin]); normals.push([cos, 0., sin]); uvs.push([ - segment as f32 / c.resolution as f32, - ring as f32 / c.segments as f32, + segment as f32 / resolution as f32, + ring as f32 / segments as f32, ]); } } // barrel skin - for i in 0..c.segments { - let ring = i * (c.resolution + 1); - let next_ring = (i + 1) * (c.resolution + 1); + for i in 0..segments { + let ring = i * (resolution + 1); + let next_ring = (i + 1) * (resolution + 1); - for j in 0..c.resolution { + for j in 0..resolution { indices.extend_from_slice(&[ ring + j, next_ring + j, @@ -85,38 +114,22 @@ impl From for Mesh { } } - // caps - - let mut build_cap = |top: bool| { - let offset = positions.len() as u32; - let (y, normal_y, winding) = if top { - (c.height / 2., 1., (1, 0)) - } else { - (c.height / -2., -1., (0, 1)) - }; - - for i in 0..c.resolution { - let theta = i as f32 * step_theta; - let (sin, cos) = theta.sin_cos(); - - positions.push([cos * c.radius, y, sin * c.radius]); - normals.push([0.0, normal_y, 0.0]); - uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]); - } - - for i in 1..(c.resolution - 1) { - indices.extend_from_slice(&[ - offset, - offset + i + winding.0, - offset + i + winding.1, - ]); - } - }; - - // top - - build_cap(true); - build_cap(false); + // Top and bottom + let base = CircleMesh::new(self.cylinder.radius, self.resolution as usize).facing_y(); + base.build_mesh_data( + [0.0, self.cylinder.half_height, 0.0], + &mut indices, + &mut positions, + &mut normals, + &mut uvs, + ); + base.facing_neg_y().build_mesh_data( + [0.0, -self.cylinder.half_height, 0.0], + &mut indices, + &mut positions, + &mut normals, + &mut uvs, + ); Mesh::new(PrimitiveTopology::TriangleList) .with_indices(Some(Indices::U32(indices))) @@ -125,3 +138,26 @@ impl From for Mesh { .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) } } + +impl Meshable for Cylinder { + type Output = CylinderMesh; + + fn mesh(&self) -> Self::Output { + CylinderMesh { + cylinder: *self, + ..Default::default() + } + } +} + +impl From for Mesh { + fn from(cylinder: Cylinder) -> Self { + cylinder.mesh().build() + } +} + +impl From for Mesh { + fn from(cylinder: CylinderMesh) -> Self { + cylinder.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/ellipse.rs b/crates/bevy_render/src/mesh/shape/ellipse.rs new file mode 100644 index 0000000000000..8d6de46c58072 --- /dev/null +++ b/crates/bevy_render/src/mesh/shape/ellipse.rs @@ -0,0 +1,143 @@ +use crate::mesh::{Indices, Mesh}; + +use super::{Facing, MeshFacingExtension, Meshable}; +use bevy_math::primitives::Ellipse; +use wgpu::PrimitiveTopology; + +/// A builder used for creating a [`Mesh`] with an [`Ellipse`] shape. +#[derive(Clone, Copy, Debug)] +pub struct EllipseMesh { + /// The [`Ellipse`] shape. + pub ellipse: Ellipse, + /// The number of vertices used for the ellipse mesh. + /// The default is `32`. + #[doc(alias = "vertices")] + pub resolution: usize, + /// The XYZ direction that the mesh is facing. + /// The default is [`Facing::Z`]. + pub facing: Facing, +} + +impl Default for EllipseMesh { + fn default() -> Self { + Self { + ellipse: Ellipse::default(), + resolution: 32, + facing: Facing::Z, + } + } +} + +impl MeshFacingExtension for EllipseMesh { + #[inline] + fn facing(mut self, facing: Facing) -> Self { + self.facing = facing; + self + } +} + +impl EllipseMesh { + /// Creates a new [`EllipseMesh`] from a given half width and half height and a vertex count. + #[inline] + pub const fn new(half_width: f32, half_height: f32, resolution: usize) -> Self { + Self { + ellipse: Ellipse { + half_width, + half_height, + }, + resolution, + facing: Facing::Z, + } + } + + /// Sets the number of vertices used for the ellipse mesh. + #[inline] + #[doc(alias = "vertices")] + pub const fn resolution(mut self, resolution: usize) -> Self { + self.resolution = resolution; + self + } + + /// Builds a [`Mesh`] based on the configuration in `self`. + pub fn build(&self) -> Mesh { + let mut indices = Vec::with_capacity((self.resolution - 2) * 3); + let mut positions = Vec::with_capacity(self.resolution); + let mut normals = Vec::with_capacity(self.resolution); + let mut uvs = Vec::with_capacity(self.resolution); + + self.build_mesh_data( + [0.0; 3], + &mut indices, + &mut positions, + &mut normals, + &mut uvs, + ); + + Mesh::new(PrimitiveTopology::TriangleList) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + .with_indices(Some(Indices::U32(indices))) + } + + pub(super) fn build_mesh_data( + &self, + translation: [f32; 3], + indices: &mut Vec, + positions: &mut Vec<[f32; 3]>, + normals: &mut Vec<[f32; 3]>, + uvs: &mut Vec<[f32; 2]>, + ) { + let sides = self.resolution; + let [trans_x, trans_y, trans_z] = translation; + + let index_offset = positions.len() as u32; + let facing_coords = self.facing.to_array(); + let normal_sign = self.facing.signum() as f32; + let step = normal_sign * std::f32::consts::TAU / sides as f32; + + for i in 0..sides { + let theta = std::f32::consts::FRAC_PI_2 + i as f32 * step; + let (sin, cos) = theta.sin_cos(); + let x = cos * self.ellipse.half_width; + let y = sin * self.ellipse.half_height; + + let position = match self.facing { + Facing::X | Facing::NegX => [trans_x, trans_y + y, trans_z - x], + Facing::Y | Facing::NegY => [trans_x + x, trans_y, trans_z - y], + Facing::Z | Facing::NegZ => [trans_x + x, trans_y + y, trans_z], + }; + + positions.push(position); + normals.push(facing_coords); + uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]); + } + + for i in 1..(sides as u32 - 1) { + indices.extend_from_slice(&[index_offset, index_offset + i, index_offset + i + 1]); + } + } +} + +impl Meshable for Ellipse { + type Output = EllipseMesh; + + fn mesh(&self) -> Self::Output { + EllipseMesh { + ellipse: *self, + ..Default::default() + } + } +} + +impl From for Mesh { + fn from(ellipse: Ellipse) -> Self { + ellipse.mesh().build() + } +} + +impl From for Mesh { + fn from(ellipse: EllipseMesh) -> Self { + ellipse.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/icosphere.rs b/crates/bevy_render/src/mesh/shape/icosphere.rs deleted file mode 100644 index 457ea0f82661d..0000000000000 --- a/crates/bevy_render/src/mesh/shape/icosphere.rs +++ /dev/null @@ -1,112 +0,0 @@ -use crate::mesh::{Indices, Mesh}; -use hexasphere::shapes::IcoSphere; -use thiserror::Error; -use wgpu::PrimitiveTopology; - -/// A sphere made from a subdivided Icosahedron. -#[derive(Debug, Clone, Copy)] -pub struct Icosphere { - /// The radius of the sphere. - pub radius: f32, - /// The number of subdivisions applied. - pub subdivisions: usize, -} - -impl Default for Icosphere { - fn default() -> Self { - Self { - radius: 1.0, - subdivisions: 5, - } - } -} - -#[derive(Debug, Clone, Error)] -pub enum FromIcosphereError { - #[error("Cannot create an icosphere of {subdivisions} subdivisions due to there being too many vertices being generated: {number_of_resulting_points}. (Limited to 65535 vertices or 79 subdivisions)")] - TooManyVertices { - subdivisions: usize, - number_of_resulting_points: usize, - }, -} - -impl TryFrom for Mesh { - type Error = FromIcosphereError; - - fn try_from(sphere: Icosphere) -> Result { - if sphere.subdivisions >= 80 { - /* - Number of triangles: - N = 20 - - Number of edges: - E = 30 - - Number of vertices: - V = 12 - - Number of points within a triangle (triangular numbers): - inner(s) = (s^2 + s) / 2 - - Number of points on an edge: - edges(s) = s - - Add up all vertices on the surface: - vertices(s) = edges(s) * E + inner(s - 1) * N + V - - Expand and simplify. Notice that the triangular number formula has roots at -1, and 0, so translating it one to the right fixes it. - subdivisions(s) = 30s + 20((s^2 - 2s + 1 + s - 1) / 2) + 12 - subdivisions(s) = 30s + 10s^2 - 10s + 12 - subdivisions(s) = 10(s^2 + 2s) + 12 - - Factor an (s + 1) term to simplify in terms of calculation - subdivisions(s) = 10(s + 1)^2 + 12 - 10 - resulting_vertices(s) = 10(s + 1)^2 + 2 - */ - let temp = sphere.subdivisions + 1; - let number_of_resulting_points = temp * temp * 10 + 2; - return Err(FromIcosphereError::TooManyVertices { - subdivisions: sphere.subdivisions, - number_of_resulting_points, - }); - } - let generated = IcoSphere::new(sphere.subdivisions, |point| { - let inclination = point.y.acos(); - let azimuth = point.z.atan2(point.x); - - let norm_inclination = inclination / std::f32::consts::PI; - let norm_azimuth = 0.5 - (azimuth / std::f32::consts::TAU); - - [norm_azimuth, norm_inclination] - }); - - let raw_points = generated.raw_points(); - - let points = raw_points - .iter() - .map(|&p| (p * sphere.radius).into()) - .collect::>(); - - let normals = raw_points - .iter() - .copied() - .map(Into::into) - .collect::>(); - - let uvs = generated.raw_data().to_owned(); - - let mut indices = Vec::with_capacity(generated.indices_per_main_triangle() * 20); - - for i in 0..20 { - generated.get_indices(i, &mut indices); - } - - let indices = Indices::U32(indices); - - Ok(Mesh::new(PrimitiveTopology::TriangleList) - .with_indices(Some(indices)) - .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, points) - .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)) - } -} diff --git a/crates/bevy_render/src/mesh/shape/mod.rs b/crates/bevy_render/src/mesh/shape/mod.rs index c9f6b9e1492bf..87589cae04128 100644 --- a/crates/bevy_render/src/mesh/shape/mod.rs +++ b/crates/bevy_render/src/mesh/shape/mod.rs @@ -1,277 +1,135 @@ -use super::{Indices, Mesh}; -use bevy_math::*; +//! Mesh generation for [primitive shapes](bevy_math::primitives). -#[derive(Debug, Copy, Clone)] -pub struct Cube { - pub size: f32, -} - -impl Cube { - pub fn new(size: f32) -> Cube { - Cube { size } - } -} - -impl Default for Cube { - fn default() -> Self { - Cube { size: 1.0 } - } -} - -impl From for Mesh { - fn from(cube: Cube) -> Self { - Box::new(cube.size, cube.size, cube.size).into() - } -} - -/// An axis-aligned box defined by its minimum and maximum point. -#[derive(Debug, Copy, Clone)] -pub struct Box { - pub min_x: f32, - pub max_x: f32, - - pub min_y: f32, - pub max_y: f32, +#![warn(missing_docs)] - pub min_z: f32, - pub max_z: f32, -} - -impl Box { - /// Creates a new box centered at the origin with the supplied side lengths. - pub fn new(x_length: f32, y_length: f32, z_length: f32) -> Box { - Box { - max_x: x_length / 2.0, - min_x: -x_length / 2.0, - max_y: y_length / 2.0, - min_y: -y_length / 2.0, - max_z: z_length / 2.0, - min_z: -z_length / 2.0, +mod capsule; +mod circle; +mod cone; +mod conical_frustum; +mod cuboid; +mod cylinder; +mod ellipse; +mod plane; +mod rectangle; +mod regular_polygon; +mod sphere; +mod torus; +mod triangle; + +pub use capsule::{CapsuleMesh, CapsuleUvProfile}; +pub use circle::CircleMesh; +pub use cone::ConeMesh; +pub use conical_frustum::ConicalFrustumMesh; +pub use cuboid::CuboidMesh; +pub use cylinder::CylinderMesh; +pub use ellipse::EllipseMesh; +pub use plane::PlaneMesh; +pub use rectangle::RectangleMesh; +pub use regular_polygon::RegularPolygonMesh; +pub use sphere::{IcosphereError, SphereKind, SphereMesh}; +pub use torus::TorusMesh; +pub use triangle::Triangle2dMesh; + +use super::Mesh; + +/// A trait for shapes that can be turned into a [`Mesh`]. +pub trait Meshable { + /// The output of [`Self::mesh`]. This can either be a [`Mesh`] + /// or a builder used for creating a [`Mesh`]. + type Output; + + /// Creates a [`Mesh`] for a shape. + fn mesh(&self) -> Self::Output; +} + +/// The cartesian axis that a [`Mesh`] should be facing upon creation. +/// This is either positive or negative `X`, `Y`, or `Z`. +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub enum Facing { + /// Facing the `+X` direction. + X = 1, + /// Facing the `+Y` direction. + Y = 2, + /// Facing the `+Z` direction. + #[default] + Z = 3, + /// Facing the `-X` direction. + NegX = -1, + /// Facing the `-Y` direction. + NegY = -2, + /// Facing the `-Z` direction. + NegZ = -3, +} + +impl Facing { + /// Returns `1` if the facing direction is positive `X`, `Y`, or `Z`, and `-1` otherwise. + #[inline] + pub const fn signum(&self) -> i8 { + match self { + Facing::X | Facing::Y | Facing::Z => 1, + _ => -1, } } - /// Creates a new box given the coordinates of two opposing corners. - pub fn from_corners(a: Vec3, b: Vec3) -> Box { - let max = a.max(b); - let min = a.min(b); - Box { - max_x: max.x, - min_x: min.x, - max_y: max.y, - min_y: min.y, - max_z: max.z, - min_z: min.z, + /// Returns the direction in as an array in the format `[x, y, z]`. + /// + /// # Example + /// + /// ``` + /// # use bevy_render::prelude::Facing; + /// assert_eq!(Facing::X.to_array(), [1.0, 0.0, 0.0]); + /// ``` + #[inline] + pub const fn to_array(&self) -> [f32; 3] { + match self { + Facing::X => [1.0, 0.0, 0.0], + Facing::Y => [0.0, 1.0, 0.0], + Facing::Z => [0.0, 0.0, 1.0], + Facing::NegX => [-1.0, 0.0, 0.0], + Facing::NegY => [0.0, -1.0, 0.0], + Facing::NegZ => [0.0, 0.0, -1.0], } } } -impl Default for Box { - fn default() -> Self { - Box::new(2.0, 1.0, 1.0) - } -} - -impl From for Mesh { - fn from(sp: Box) -> Self { - // suppose Y-up right hand, and camera look from +z to -z - let vertices = &[ - // Front - ([sp.min_x, sp.min_y, sp.max_z], [0., 0., 1.0], [0., 0.]), - ([sp.max_x, sp.min_y, sp.max_z], [0., 0., 1.0], [1.0, 0.]), - ([sp.max_x, sp.max_y, sp.max_z], [0., 0., 1.0], [1.0, 1.0]), - ([sp.min_x, sp.max_y, sp.max_z], [0., 0., 1.0], [0., 1.0]), - // Back - ([sp.min_x, sp.max_y, sp.min_z], [0., 0., -1.0], [1.0, 0.]), - ([sp.max_x, sp.max_y, sp.min_z], [0., 0., -1.0], [0., 0.]), - ([sp.max_x, sp.min_y, sp.min_z], [0., 0., -1.0], [0., 1.0]), - ([sp.min_x, sp.min_y, sp.min_z], [0., 0., -1.0], [1.0, 1.0]), - // Right - ([sp.max_x, sp.min_y, sp.min_z], [1.0, 0., 0.], [0., 0.]), - ([sp.max_x, sp.max_y, sp.min_z], [1.0, 0., 0.], [1.0, 0.]), - ([sp.max_x, sp.max_y, sp.max_z], [1.0, 0., 0.], [1.0, 1.0]), - ([sp.max_x, sp.min_y, sp.max_z], [1.0, 0., 0.], [0., 1.0]), - // Left - ([sp.min_x, sp.min_y, sp.max_z], [-1.0, 0., 0.], [1.0, 0.]), - ([sp.min_x, sp.max_y, sp.max_z], [-1.0, 0., 0.], [0., 0.]), - ([sp.min_x, sp.max_y, sp.min_z], [-1.0, 0., 0.], [0., 1.0]), - ([sp.min_x, sp.min_y, sp.min_z], [-1.0, 0., 0.], [1.0, 1.0]), - // Top - ([sp.max_x, sp.max_y, sp.min_z], [0., 1.0, 0.], [1.0, 0.]), - ([sp.min_x, sp.max_y, sp.min_z], [0., 1.0, 0.], [0., 0.]), - ([sp.min_x, sp.max_y, sp.max_z], [0., 1.0, 0.], [0., 1.0]), - ([sp.max_x, sp.max_y, sp.max_z], [0., 1.0, 0.], [1.0, 1.0]), - // Bottom - ([sp.max_x, sp.min_y, sp.max_z], [0., -1.0, 0.], [0., 0.]), - ([sp.min_x, sp.min_y, sp.max_z], [0., -1.0, 0.], [1.0, 0.]), - ([sp.min_x, sp.min_y, sp.min_z], [0., -1.0, 0.], [1.0, 1.0]), - ([sp.max_x, sp.min_y, sp.min_z], [0., -1.0, 0.], [0., 1.0]), - ]; - - let positions: Vec<_> = vertices.iter().map(|(p, _, _)| *p).collect(); - let normals: Vec<_> = vertices.iter().map(|(_, n, _)| *n).collect(); - let uvs: Vec<_> = vertices.iter().map(|(_, _, uv)| *uv).collect(); +/// An extension trait for methods related to setting a specific [`Facing`] direction. +pub trait MeshFacingExtension: Sized { + /// Set the [`Facing`] direction. + fn facing(self, facing: Facing) -> Self; - let indices = Indices::U32(vec![ - 0, 1, 2, 2, 3, 0, // front - 4, 5, 6, 6, 7, 4, // back - 8, 9, 10, 10, 11, 8, // right - 12, 13, 14, 14, 15, 12, // left - 16, 17, 18, 18, 19, 16, // top - 20, 21, 22, 22, 23, 20, // bottom - ]); - - Mesh::new(PrimitiveTopology::TriangleList) - .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) - .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) - .with_indices(Some(indices)) + /// Set the [`Facing`] direction to `+X`. + #[inline] + fn facing_x(self) -> Self { + self.facing(Facing::X) } -} - -/// A rectangle on the `XY` plane centered at the origin. -#[derive(Debug, Copy, Clone)] -pub struct Quad { - /// Full width and height of the rectangle. - pub size: Vec2, - /// Horizontally-flip the texture coordinates of the resulting mesh. - pub flip: bool, -} -impl Default for Quad { - fn default() -> Self { - Quad::new(Vec2::ONE) + /// Set the [`Facing`] direction to `+Y`. + #[inline] + fn facing_y(self) -> Self { + self.facing(Facing::Y) } -} -impl Quad { - pub fn new(size: Vec2) -> Self { - Self { size, flip: false } + /// Set the [`Facing`] direction to `+Z`. + #[inline] + fn facing_z(self) -> Self { + self.facing(Facing::Z) } - pub fn flipped(size: Vec2) -> Self { - Self { size, flip: true } + /// Set the [`Facing`] direction to `-X`. + #[inline] + fn facing_neg_x(self) -> Self { + self.facing(Facing::NegX) } -} - -impl From for Mesh { - fn from(quad: Quad) -> Self { - let extent_x = quad.size.x / 2.0; - let extent_y = quad.size.y / 2.0; - let (u_left, u_right) = if quad.flip { (1.0, 0.0) } else { (0.0, 1.0) }; - let vertices = [ - ([-extent_x, -extent_y, 0.0], [0.0, 0.0, 1.0], [u_left, 1.0]), - ([-extent_x, extent_y, 0.0], [0.0, 0.0, 1.0], [u_left, 0.0]), - ([extent_x, extent_y, 0.0], [0.0, 0.0, 1.0], [u_right, 0.0]), - ([extent_x, -extent_y, 0.0], [0.0, 0.0, 1.0], [u_right, 1.0]), - ]; - - let indices = Indices::U32(vec![0, 2, 1, 0, 3, 2]); - - let positions: Vec<_> = vertices.iter().map(|(p, _, _)| *p).collect(); - let normals: Vec<_> = vertices.iter().map(|(_, n, _)| *n).collect(); - let uvs: Vec<_> = vertices.iter().map(|(_, _, uv)| *uv).collect(); - - Mesh::new(PrimitiveTopology::TriangleList) - .with_indices(Some(indices)) - .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) - .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) - } -} - -/// A square on the `XZ` plane centered at the origin. -#[derive(Debug, Copy, Clone)] -pub struct Plane { - /// The total side length of the square. - pub size: f32, - /// The number of subdivisions in the mesh. - /// - /// 0 - is the original plane geometry, the 4 points in the XZ plane. - /// - /// 1 - is split by 1 line in the middle of the plane on both the X axis and the Z axis, resulting in a plane with 4 quads / 8 triangles. - /// - /// 2 - is a plane split by 2 lines on both the X and Z axes, subdividing the plane into 3 equal sections along each axis, resulting in a plane with 9 quads / 18 triangles. - /// - /// and so on... - pub subdivisions: u32, -} - -impl Default for Plane { - fn default() -> Self { - Plane { - size: 1.0, - subdivisions: 0, - } - } -} - -impl Plane { - /// Creates a new plane centered at the origin with the supplied side length and zero subdivisions. - pub fn from_size(size: f32) -> Self { - Self { - size, - subdivisions: 0, - } + /// Set the [`Facing`] direction to `-Y`. + #[inline] + fn facing_neg_y(self) -> Self { + self.facing(Facing::NegY) } -} - -impl From for Mesh { - fn from(plane: Plane) -> Self { - // here this is split in the z and x directions if one ever needs asymmetrical subdivision - // two Plane struct fields would need to be added instead of the single subdivisions field - let z_vertex_count = plane.subdivisions + 2; - let x_vertex_count = plane.subdivisions + 2; - let num_vertices = (z_vertex_count * x_vertex_count) as usize; - let num_indices = ((z_vertex_count - 1) * (x_vertex_count - 1) * 6) as usize; - let up = Vec3::Y.to_array(); - let mut positions: Vec<[f32; 3]> = Vec::with_capacity(num_vertices); - let mut normals: Vec<[f32; 3]> = Vec::with_capacity(num_vertices); - let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(num_vertices); - let mut indices: Vec = Vec::with_capacity(num_indices); - - for z in 0..z_vertex_count { - for x in 0..x_vertex_count { - let tx = x as f32 / (x_vertex_count - 1) as f32; - let tz = z as f32 / (z_vertex_count - 1) as f32; - positions.push([(-0.5 + tx) * plane.size, 0.0, (-0.5 + tz) * plane.size]); - normals.push(up); - uvs.push([tx, tz]); - } - } - - for y in 0..z_vertex_count - 1 { - for x in 0..x_vertex_count - 1 { - let quad = y * x_vertex_count + x; - indices.push(quad + x_vertex_count + 1); - indices.push(quad + 1); - indices.push(quad + x_vertex_count); - indices.push(quad); - indices.push(quad + x_vertex_count); - indices.push(quad + 1); - } - } - - Mesh::new(PrimitiveTopology::TriangleList) - .with_indices(Some(Indices::U32(indices))) - .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) - .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + /// Set the [`Facing`] direction to `-Z`. + #[inline] + fn facing_neg_z(self) -> Self { + self.facing(Facing::NegZ) } } - -mod capsule; -mod cylinder; -mod icosphere; -mod regular_polygon; -mod torus; -mod uvsphere; - -pub use capsule::{Capsule, CapsuleUvProfile}; -pub use cylinder::Cylinder; -pub use icosphere::Icosphere; -pub use regular_polygon::{Circle, RegularPolygon}; -pub use torus::Torus; -pub use uvsphere::UVSphere; -use wgpu::PrimitiveTopology; diff --git a/crates/bevy_render/src/mesh/shape/plane.rs b/crates/bevy_render/src/mesh/shape/plane.rs new file mode 100644 index 0000000000000..3fadd47e164ea --- /dev/null +++ b/crates/bevy_render/src/mesh/shape/plane.rs @@ -0,0 +1,109 @@ +use super::{Mesh, Meshable}; +use crate::mesh::Indices; +use bevy_math::{primitives::Plane3d, Quat, Vec2, Vec3}; +use wgpu::PrimitiveTopology; + +/// A builder used for creating a [`Mesh`] with a [`Plane3d`] shape. +#[derive(Clone, Copy, Debug)] +pub struct PlaneMesh { + /// The [`Plane3d`] shape. + pub plane: Plane3d, + /// Half the size of the plane mesh. + pub half_size: Vec2, +} + +impl Default for PlaneMesh { + fn default() -> Self { + Self { + plane: Plane3d::default(), + half_size: Vec2::ONE, + } + } +} + +impl PlaneMesh { + /// Creates a new [`PlaneMesh`] from a given normal and size. + /// + /// # Panics + /// + /// Panics if the given `normal` is zero (or very close to zero), or non-finite. + #[inline] + pub fn new(normal: Vec3, size: Vec2) -> Self { + Self { + plane: Plane3d::new(normal), + half_size: 2.0 * size, + } + } + + /// Creates a new [`PlaneMesh`] from the given size, with the normal pointing upwards. + #[inline] + pub fn from_size(size: Vec2) -> Self { + Self { + half_size: 2.0 * size, + ..Default::default() + } + } + + /// Sets the normal of the plane, aka the direction the plane is facing. + /// + /// # Panics + /// + /// Panics if the given `normal` is zero (or very close to zero), or non-finite. + #[inline] + #[doc(alias = "facing")] + pub fn normal(mut self, normal: Vec3) -> Self { + self.plane = Plane3d::new(normal); + self + } + + /// Sets the size of the plane mesh. + #[inline] + pub fn size(mut self, size: Vec2) -> Self { + self.half_size = size / 2.0; + self + } + + /// Builds a [`Mesh`] based on the configuration in `self`. + pub fn build(&self) -> Mesh { + let rotation = Quat::from_rotation_arc(Vec3::Y, *self.plane.normal); + let positions = vec![ + rotation * Vec3::new(self.half_size.x, 0.0, -self.half_size.y), + rotation * Vec3::new(-self.half_size.x, 0.0, -self.half_size.y), + rotation * Vec3::new(-self.half_size.x, 0.0, self.half_size.y), + rotation * Vec3::new(self.half_size.x, 0.0, self.half_size.y), + ]; + + let normals = vec![self.plane.normal.to_array(); 4]; + let uvs = vec![[1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]]; + let indices = Indices::U32(vec![0, 1, 2, 0, 2, 3]); + + Mesh::new(PrimitiveTopology::TriangleList) + .with_indices(Some(indices)) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + } +} + +impl Meshable for Plane3d { + type Output = PlaneMesh; + + fn mesh(&self) -> Self::Output { + PlaneMesh { + plane: *self, + ..Default::default() + } + } +} + +impl From for Mesh { + fn from(plane: Plane3d) -> Self { + plane.mesh().build() + } +} + +impl From for Mesh { + fn from(plane: PlaneMesh) -> Self { + plane.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/rectangle.rs b/crates/bevy_render/src/mesh/shape/rectangle.rs new file mode 100644 index 0000000000000..874a0d85c6112 --- /dev/null +++ b/crates/bevy_render/src/mesh/shape/rectangle.rs @@ -0,0 +1,97 @@ +use super::{Facing, Mesh, MeshFacingExtension, Meshable}; +use crate::mesh::Indices; +use bevy_math::primitives::Rectangle; +use wgpu::PrimitiveTopology; + +/// A builder used for creating a [`Mesh`] with a [`Rectangle`] shape. +#[derive(Clone, Copy, Debug, Default)] +pub struct RectangleMesh { + /// The [`Rectangle`] shape. + pub rectangle: Rectangle, + /// The XYZ direction that the mesh is facing. + /// The default is [`Facing::Z`]. + pub facing: Facing, +} + +impl MeshFacingExtension for RectangleMesh { + #[inline] + fn facing(mut self, facing: Facing) -> Self { + self.facing = facing; + self + } +} + +impl RectangleMesh { + /// Creates a new [`RectangleMesh`] from a given radius and vertex count. + #[inline] + pub fn new(width: f32, height: f32) -> Self { + Self { + rectangle: Rectangle::new(width, height), + facing: Facing::Z, + } + } + + /// Builds a [`Mesh`] based on the configuration in `self`. + pub fn build(&self) -> Mesh { + let [hw, hh] = [self.rectangle.half_width, self.rectangle.half_height]; + let positions = match self.facing { + Facing::Z | Facing::NegZ => vec![ + [hw, hh, 0.0], + [-hw, hh, 0.0], + [-hw, -hh, 0.0], + [hw, -hh, 0.0], + ], + Facing::Y | Facing::NegY => vec![ + [hw, 0.0, -hh], + [-hw, 0.0, -hh], + [-hw, 0.0, hh], + [hw, 0.0, hh], + ], + Facing::X | Facing::NegX => vec![ + [0.0, hh, -hw], + [0.0, hh, hw], + [0.0, -hh, hw], + [0.0, -hh, -hw], + ], + }; + + let normals = vec![self.facing.to_array(); 4]; + let uvs = vec![[1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]]; + + // Flip indices if facing -X, -Y, or -Z + let indices = if self.facing.signum() > 0 { + Indices::U32(vec![0, 1, 2, 0, 2, 3]) + } else { + Indices::U32(vec![0, 2, 1, 0, 3, 2]) + }; + + Mesh::new(PrimitiveTopology::TriangleList) + .with_indices(Some(indices)) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + } +} + +impl Meshable for Rectangle { + type Output = RectangleMesh; + + fn mesh(&self) -> Self::Output { + RectangleMesh { + rectangle: *self, + ..Default::default() + } + } +} + +impl From for Mesh { + fn from(rectangle: Rectangle) -> Self { + rectangle.mesh().build() + } +} + +impl From for Mesh { + fn from(rectangle: RectangleMesh) -> Self { + rectangle.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/regular_polygon.rs b/crates/bevy_render/src/mesh/shape/regular_polygon.rs index 879c59fabd11e..8e3446a5aedf6 100644 --- a/crates/bevy_render/src/mesh/shape/regular_polygon.rs +++ b/crates/bevy_render/src/mesh/shape/regular_polygon.rs @@ -1,107 +1,90 @@ -use crate::mesh::{Indices, Mesh}; -use wgpu::PrimitiveTopology; +use super::{Facing, Mesh, MeshFacingExtension, Meshable}; +use bevy_math::primitives::{Ellipse, RegularPolygon}; -/// A regular polygon in the `XY` plane -#[derive(Debug, Copy, Clone)] -pub struct RegularPolygon { - /// Circumscribed radius in the `XY` plane. - /// - /// In other words, the vertices of this polygon will all touch a circle of this radius. - pub radius: f32, - /// Number of sides. - pub sides: usize, +/// A builder used for creating a [`Mesh`] with a [`RegularPolygon`] shape. +#[derive(Clone, Copy, Debug, Default)] +pub struct RegularPolygonMesh { + /// The [`RegularPolygon`] shape. + pub polygon: RegularPolygon, + /// The XYZ direction that the mesh is facing. + /// The default is [`Facing::Z`]. + pub facing: Facing, } -impl Default for RegularPolygon { - fn default() -> Self { - Self { - radius: 0.5, - sides: 6, - } +impl MeshFacingExtension for RegularPolygonMesh { + #[inline] + fn facing(mut self, facing: Facing) -> Self { + self.facing = facing; + self } } -impl RegularPolygon { - /// Creates a regular polygon in the `XY` plane - pub fn new(radius: f32, sides: usize) -> Self { - Self { radius, sides } - } -} - -impl From for Mesh { - fn from(polygon: RegularPolygon) -> Self { - let RegularPolygon { radius, sides } = polygon; - - debug_assert!(sides > 2, "RegularPolygon requires at least 3 sides."); - - let mut positions = Vec::with_capacity(sides); - let mut normals = Vec::with_capacity(sides); - let mut uvs = Vec::with_capacity(sides); - - let step = std::f32::consts::TAU / sides as f32; - for i in 0..sides { - let theta = std::f32::consts::FRAC_PI_2 - i as f32 * step; - let (sin, cos) = theta.sin_cos(); - - positions.push([cos * radius, sin * radius, 0.0]); - normals.push([0.0, 0.0, 1.0]); - uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]); +impl RegularPolygonMesh { + /// Creates a new [`RegularPolygonMesh`] from the radius + /// of the circumcircle and a number of sides. + /// + /// # Panics + /// + /// Panics if `circumradius` is non-positive. + #[inline] + pub fn new(circumradius: f32, sides: usize) -> Self { + Self { + polygon: RegularPolygon::new(circumradius, sides), + ..Default::default() } + } - let mut indices = Vec::with_capacity((sides - 2) * 3); - for i in 1..(sides as u32 - 1) { - // Vertices are generated in CW order above, hence the reversed indices here - // to emit triangle vertices in CCW order. - indices.extend_from_slice(&[0, i + 1, i]); + /// Builds a [`Mesh`] based on the configuration in `self`. + pub fn build(&self) -> Mesh { + // The ellipse mesh is just a regular polygon with two radii + Ellipse { + half_width: self.polygon.circumcircle.radius, + half_height: self.polygon.circumcircle.radius, } - - Mesh::new(PrimitiveTopology::TriangleList) - .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) - .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) - .with_indices(Some(Indices::U32(indices))) + .mesh() + .resolution(self.polygon.sides) + .facing(self.facing) + .build() } -} - -/// A circle in the `XY` plane -#[derive(Debug, Copy, Clone)] -pub struct Circle { - /// Inscribed radius in the `XY` plane. - pub radius: f32, - /// The number of vertices used. - pub vertices: usize, -} -impl Default for Circle { - fn default() -> Self { - Self { - radius: 0.5, - vertices: 64, + pub(super) fn build_mesh_data( + &self, + translation: [f32; 3], + indices: &mut Vec, + positions: &mut Vec<[f32; 3]>, + normals: &mut Vec<[f32; 3]>, + uvs: &mut Vec<[f32; 2]>, + ) { + Ellipse { + half_width: self.polygon.circumcircle.radius, + half_height: self.polygon.circumcircle.radius, } + .mesh() + .resolution(self.polygon.sides) + .facing(self.facing) + .build_mesh_data(translation, indices, positions, normals, uvs); } } -impl Circle { - /// Creates a circle in the `XY` plane - pub fn new(radius: f32) -> Self { - Self { - radius, +impl Meshable for RegularPolygon { + type Output = RegularPolygonMesh; + + fn mesh(&self) -> Self::Output { + RegularPolygonMesh { + polygon: *self, ..Default::default() } } } -impl From for RegularPolygon { - fn from(circle: Circle) -> Self { - Self { - radius: circle.radius, - sides: circle.vertices, - } +impl From for Mesh { + fn from(polygon: RegularPolygon) -> Self { + polygon.mesh().build() } } -impl From for Mesh { - fn from(circle: Circle) -> Self { - Mesh::from(RegularPolygon::from(circle)) +impl From for Mesh { + fn from(polygon: RegularPolygonMesh) -> Self { + polygon.build() } } diff --git a/crates/bevy_render/src/mesh/shape/sphere.rs b/crates/bevy_render/src/mesh/shape/sphere.rs new file mode 100644 index 0000000000000..336edcad509eb --- /dev/null +++ b/crates/bevy_render/src/mesh/shape/sphere.rs @@ -0,0 +1,260 @@ +use std::f32::consts::PI; + +use super::{Mesh, Meshable}; +use crate::mesh::Indices; +use bevy_math::primitives::Sphere; +use hexasphere::shapes::IcoSphere; +use thiserror::Error; +use wgpu::PrimitiveTopology; + +/// An error when creating an icosphere [`Mesh`] from a [`SphereMesh`]. +#[derive(Clone, Copy, Debug, Error)] +pub enum IcosphereError { + /// The icosphere has too many vertices. + #[error("Cannot create an icosphere of {subdivisions} subdivisions due to there being too many vertices being generated: {number_of_resulting_points}. (Limited to 65535 vertices or 79 subdivisions)")] + TooManyVertices { + /// The number of subdivisions used. 79 is the largest allowed value for a mesh to be generated. + subdivisions: usize, + /// The number of vertices generated. 65535 is the largest allowed value for a mesh to be generated. + number_of_resulting_points: usize, + }, +} + +/// A type of sphere mesh. +#[derive(Clone, Copy, Debug)] +pub enum SphereKind { + /// An icosphere, a spherical mesh that consists of equally sized triangles. + Ico { + /// The number of subdivisions applied. + /// The number of faces quadruples with each subdivision. + subdivisions: usize, + }, + /// A UV sphere, a spherical mesh that consists of quadrilaterals + /// apart from triangles at the top and bottom. + Uv { + /// The number of longitudinal sectors, aka the horizontal resolution. + #[doc(alias = "horizontal_resolution")] + sectors: usize, + /// The number of latitudinal stacks, aka the vertical resolution. + #[doc(alias = "vertical_resolution")] + stacks: usize, + }, +} + +impl Default for SphereKind { + fn default() -> Self { + Self::Ico { subdivisions: 5 } + } +} + +/// A builder used for creating a [`Mesh`] with an [`Sphere`] shape. +#[derive(Clone, Copy, Debug, Default)] +pub struct SphereMesh { + /// The [`Sphere`] shape. + pub sphere: Sphere, + /// The type of sphere mesh that will be built. + pub kind: SphereKind, +} + +impl SphereMesh { + /// Creates a new [`SphereMesh`] from a radius and [`SphereKind`]. + #[inline] + pub const fn new(radius: f32, kind: SphereKind) -> Self { + Self { + sphere: Sphere { radius }, + kind, + } + } + + /// Sets the [`SphereKind`] that will be used for building the mesh. + #[inline] + pub const fn set_kind(mut self, kind: SphereKind) -> Self { + self.kind = kind; + 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. + /// If there are `80` or more subdivisions, the vertex count will be too large, + /// and an [`IcosphereError`] is returned. + /// + /// A good default is `5` subdivisions. + pub fn ico(&self, subdivisions: usize) -> Result { + if subdivisions >= 80 { + /* + Number of triangles: + N = 20 + + Number of edges: + E = 30 + + Number of vertices: + V = 12 + + Number of points within a triangle (triangular numbers): + inner(s) = (s^2 + s) / 2 + + Number of points on an edge: + edges(s) = s + + Add up all vertices on the surface: + vertices(s) = edges(s) * E + inner(s - 1) * N + V + + Expand and simplify. Notice that the triangular number formula has roots at -1, and 0, so translating it one to the right fixes it. + subdivisions(s) = 30s + 20((s^2 - 2s + 1 + s - 1) / 2) + 12 + subdivisions(s) = 30s + 10s^2 - 10s + 12 + subdivisions(s) = 10(s^2 + 2s) + 12 + + Factor an (s + 1) term to simplify in terms of calculation + subdivisions(s) = 10(s + 1)^2 + 12 - 10 + resulting_vertices(s) = 10(s + 1)^2 + 2 + */ + let temp = subdivisions + 1; + let number_of_resulting_points = temp * temp * 10 + 2; + return Err(IcosphereError::TooManyVertices { + subdivisions, + number_of_resulting_points, + }); + } + let generated = IcoSphere::new(subdivisions, |point| { + let inclination = point.y.acos(); + let azimuth = point.z.atan2(point.x); + + let norm_inclination = inclination / std::f32::consts::PI; + let norm_azimuth = 0.5 - (azimuth / std::f32::consts::TAU); + + [norm_azimuth, norm_inclination] + }); + + let raw_points = generated.raw_points(); + + let points = raw_points + .iter() + .map(|&p| (p * self.sphere.radius).into()) + .collect::>(); + + let normals = raw_points + .iter() + .copied() + .map(Into::into) + .collect::>(); + + let uvs = generated.raw_data().to_owned(); + + let mut indices = Vec::with_capacity(generated.indices_per_main_triangle() * 20); + + for i in 0..20 { + generated.get_indices(i, &mut indices); + } + + let indices = Indices::U32(indices); + + Ok(Mesh::new(PrimitiveTopology::TriangleList) + .with_indices(Some(indices)) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, points) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)) + } + + /// Creates a UV sphere [`Mesh`] with the given number of + /// longitudinal sectors and latitudinal stacks, aka horizontal and vertical resolution. + /// + /// A good default is `32` sectors and `18` stacks. + pub fn uv(&self, sectors: usize, stacks: usize) -> Mesh { + // Largely inspired from http://www.songho.ca/opengl/gl_sphere.html + + let sectors_f32 = sectors as f32; + let stacks_f32 = stacks as f32; + let length_inv = 1. / self.sphere.radius; + let sector_step = 2. * PI / sectors_f32; + let stack_step = PI / stacks_f32; + + let mut vertices: Vec<[f32; 3]> = Vec::with_capacity(stacks * sectors); + let mut normals: Vec<[f32; 3]> = Vec::with_capacity(stacks * sectors); + let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(stacks * sectors); + let mut indices: Vec = Vec::with_capacity(stacks * sectors * 2 * 3); + + for i in 0..stacks + 1 { + let stack_angle = PI / 2. - (i as f32) * stack_step; + let xy = self.sphere.radius * stack_angle.cos(); + let z = self.sphere.radius * stack_angle.sin(); + + for j in 0..sectors + 1 { + let sector_angle = (j as f32) * sector_step; + let x = xy * sector_angle.cos(); + let y = xy * sector_angle.sin(); + + vertices.push([x, y, z]); + normals.push([x * length_inv, y * length_inv, z * length_inv]); + uvs.push([(j as f32) / sectors_f32, (i as f32) / stacks_f32]); + } + } + + // indices + // k1--k1+1 + // | / | + // | / | + // k2--k2+1 + for i in 0..stacks { + let mut k1 = i * (sectors + 1); + let mut k2 = k1 + sectors + 1; + for _j in 0..sectors { + if i != 0 { + indices.push(k1 as u32); + indices.push(k2 as u32); + indices.push((k1 + 1) as u32); + } + if i != stacks - 1 { + indices.push((k1 + 1) as u32); + indices.push(k2 as u32); + indices.push((k2 + 1) as u32); + } + k1 += 1; + k2 += 1; + } + } + + Mesh::new(PrimitiveTopology::TriangleList) + .with_indices(Some(Indices::U32(indices))) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + } +} + +impl Meshable for Sphere { + type Output = SphereMesh; + + fn mesh(&self) -> Self::Output { + SphereMesh { + sphere: *self, + ..Default::default() + } + } +} + +impl From for Mesh { + fn from(sphere: Sphere) -> Self { + sphere.mesh().build() + } +} + +impl From for Mesh { + fn from(sphere: SphereMesh) -> Self { + sphere.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/torus.rs b/crates/bevy_render/src/mesh/shape/torus.rs index 5254fcceebc01..561f78170e324 100644 --- a/crates/bevy_render/src/mesh/shape/torus.rs +++ b/crates/bevy_render/src/mesh/shape/torus.rs @@ -1,73 +1,114 @@ +use super::Meshable; use crate::mesh::{Indices, Mesh}; -use bevy_math::Vec3; +use bevy_math::{primitives::Torus, Vec3}; use wgpu::PrimitiveTopology; -/// A torus (donut) shape. -#[derive(Debug, Clone, Copy)] -pub struct Torus { - pub radius: f32, - pub ring_radius: f32, - pub subdivisions_segments: usize, - pub subdivisions_sides: usize, +/// A builder used for creating a [`Mesh`] with a [`Torus`] shape. +#[derive(Clone, Copy, Debug)] +pub struct TorusMesh { + /// The [`Torus`] shape. + pub torus: Torus, + /// The number of segments used for the main ring of the torus. + /// + /// A resolution of `4` would make the torus appear rectangular, + /// while a resolution of `32` resembles a circular ring. + pub major_resolution: usize, + /// The number of vertices used for each circular segment + /// in the ring or tube of the torus. + pub minor_resolution: usize, } -impl Default for Torus { +impl Default for TorusMesh { fn default() -> Self { - Torus { - radius: 1.0, - ring_radius: 0.5, - subdivisions_segments: 32, - subdivisions_sides: 24, + Self { + torus: Torus::default(), + major_resolution: 32, + minor_resolution: 24, } } } -impl From for Mesh { - fn from(torus: Torus) -> Self { +impl TorusMesh { + /// Creates a new [`TorusMesh`] from an inner and outer radius. + /// + /// The inner radius is the radius of the hole, and the outer radius + /// is the radius of the entire object. + #[inline] + pub fn new(inner_radius: f32, outer_radius: f32) -> Self { + Self { + torus: Torus::new(inner_radius, outer_radius), + ..Default::default() + } + } + + /// Sets the number of segments used for the main ring of the torus. + /// + /// A resolution of `4` would make the torus appear rectangular, + /// while a resolution of `32` resembles a circular ring. + #[inline] + pub const fn major_resolution(mut self, resolution: usize) -> Self { + self.major_resolution = resolution; + self + } + + /// Sets the number of vertices used for each circular segment + /// in the ring or tube of the torus. + #[inline] + pub const fn minor_resolution(mut self, resolution: usize) -> Self { + self.minor_resolution = resolution; + self + } + + /// Builds a [`Mesh`] according to the configuration in `self`. + pub fn build(&self) -> Mesh { // code adapted from http://apparat-engine.blogspot.com/2013/04/procedural-meshes-torus.html // (source code at https://github.com/SEilers/Apparat) - let n_vertices = (torus.subdivisions_segments + 1) * (torus.subdivisions_sides + 1); + let n_vertices = (self.major_resolution + 1) * (self.minor_resolution + 1); let mut positions: Vec<[f32; 3]> = Vec::with_capacity(n_vertices); let mut normals: Vec<[f32; 3]> = Vec::with_capacity(n_vertices); let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(n_vertices); - let segment_stride = 2.0 * std::f32::consts::PI / torus.subdivisions_segments as f32; - let side_stride = 2.0 * std::f32::consts::PI / torus.subdivisions_sides as f32; + let segment_stride = 2.0 * std::f32::consts::PI / self.major_resolution as f32; + let side_stride = 2.0 * std::f32::consts::PI / self.minor_resolution as f32; - for segment in 0..=torus.subdivisions_segments { + for segment in 0..=self.major_resolution { let theta = segment_stride * segment as f32; - for side in 0..=torus.subdivisions_sides { + for side in 0..=self.minor_resolution { let phi = side_stride * side as f32; let position = Vec3::new( - theta.cos() * (torus.radius + torus.ring_radius * phi.cos()), - torus.ring_radius * phi.sin(), - theta.sin() * (torus.radius + torus.ring_radius * phi.cos()), + theta.cos() * (self.torus.major_radius + self.torus.minor_radius * phi.cos()), + self.torus.minor_radius * phi.sin(), + theta.sin() * (self.torus.major_radius + self.torus.minor_radius * phi.cos()), ); - let center = Vec3::new(torus.radius * theta.cos(), 0., torus.radius * theta.sin()); + let center = Vec3::new( + self.torus.major_radius * theta.cos(), + 0., + self.torus.major_radius * theta.sin(), + ); let normal = (position - center).normalize(); positions.push(position.into()); normals.push(normal.into()); uvs.push([ - segment as f32 / torus.subdivisions_segments as f32, - side as f32 / torus.subdivisions_sides as f32, + segment as f32 / self.major_resolution as f32, + side as f32 / self.minor_resolution as f32, ]); } } - let n_faces = (torus.subdivisions_segments) * (torus.subdivisions_sides); + let n_faces = (self.major_resolution) * (self.minor_resolution); let n_triangles = n_faces * 2; let n_indices = n_triangles * 3; let mut indices: Vec = Vec::with_capacity(n_indices); - let n_vertices_per_row = torus.subdivisions_sides + 1; - for segment in 0..torus.subdivisions_segments { - for side in 0..torus.subdivisions_sides { + let n_vertices_per_row = self.minor_resolution + 1; + for segment in 0..self.major_resolution { + for side in 0..self.minor_resolution { let lt = side + segment * n_vertices_per_row; let rt = (side + 1) + segment * n_vertices_per_row; @@ -91,3 +132,26 @@ impl From for Mesh { .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) } } + +impl Meshable for Torus { + type Output = TorusMesh; + + fn mesh(&self) -> Self::Output { + TorusMesh { + torus: *self, + ..Default::default() + } + } +} + +impl From for Mesh { + fn from(torus: Torus) -> Self { + torus.mesh().build() + } +} + +impl From for Mesh { + fn from(torus: TorusMesh) -> Self { + torus.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/triangle.rs b/crates/bevy_render/src/mesh/shape/triangle.rs new file mode 100644 index 0000000000000..3e498016c6561 --- /dev/null +++ b/crates/bevy_render/src/mesh/shape/triangle.rs @@ -0,0 +1,96 @@ +use super::{Facing, Mesh, MeshFacingExtension, Meshable}; +use crate::mesh::Indices; +use bevy_math::{ + primitives::{Triangle2d, WindingOrder}, + Vec2, +}; +use wgpu::PrimitiveTopology; + +/// A builder used for creating a [`Mesh`] with a [`Triangle2d`] shape. +#[derive(Clone, Copy, Debug, Default)] +pub struct Triangle2dMesh { + /// The [`Triangle2d`] shape. + pub triangle: Triangle2d, + /// The XYZ direction that the mesh is facing. + /// The default is [`Facing::Z`]. + pub facing: Facing, +} + +impl MeshFacingExtension for Triangle2dMesh { + #[inline] + fn facing(mut self, facing: Facing) -> Self { + self.facing = facing; + self + } +} + +impl Triangle2dMesh { + /// Creates a new [`Triangle2dMesh`] from points `a`, `b`, and `c`. + #[inline] + pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self { + Self { + triangle: Triangle2d::new(a, b, c), + facing: Facing::Z, + } + } + + /// Builds a [`Mesh`] based on the configuration in `self`. + pub fn build(&self) -> Mesh { + let [a, b, c] = self.triangle.vertices; + + let positions = match self.facing { + Facing::X | Facing::NegX => [[0.0, a.y, -a.x], [0.0, b.y, -b.x], [0.0, c.y, -c.x]], + Facing::Y | Facing::NegY => [[a.x, 0.0, -a.y], [b.x, 0.0, -b.y], [c.x, 0.0, -c.y]], + Facing::Z | Facing::NegZ => [[a.x, a.y, 0.0], [b.x, b.y, 0.0], [c.x, c.y, 0.0]], + }; + + let normals = vec![self.facing.to_array(); 3]; + + // The extents of the bounding box of the triangle, + // used to compute the UV coordinates of the points. + let extents = a.min(b).min(c).abs().max(a.max(b).max(c)) * Vec2::new(1.0, -1.0); + let uvs = vec![ + a / extents / 2.0 + 0.5, + b / extents / 2.0 + 0.5, + c / extents / 2.0 + 0.5, + ]; + + let flipped = self.facing.signum() < 0; + let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise; + let is_cw = self.triangle.winding_order() == WindingOrder::Clockwise; + let indices = if (is_ccw && !flipped) || (is_cw && flipped) { + Indices::U32(vec![0, 1, 2]) + } else { + Indices::U32(vec![0, 2, 1]) + }; + + Mesh::new(PrimitiveTopology::TriangleList) + .with_indices(Some(indices)) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, Vec::from(positions)) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + } +} + +impl Meshable for Triangle2d { + type Output = Triangle2dMesh; + + fn mesh(&self) -> Triangle2dMesh { + Triangle2dMesh { + triangle: *self, + ..Default::default() + } + } +} + +impl From for Mesh { + fn from(triangle: Triangle2d) -> Self { + triangle.mesh().build() + } +} + +impl From for Mesh { + fn from(triangle: Triangle2dMesh) -> Self { + triangle.build() + } +} diff --git a/crates/bevy_render/src/mesh/shape/uvsphere.rs b/crates/bevy_render/src/mesh/shape/uvsphere.rs deleted file mode 100644 index b6b89ebc40157..0000000000000 --- a/crates/bevy_render/src/mesh/shape/uvsphere.rs +++ /dev/null @@ -1,89 +0,0 @@ -use wgpu::PrimitiveTopology; - -use crate::mesh::{Indices, Mesh}; -use std::f32::consts::PI; - -/// A sphere made of sectors and stacks. -#[allow(clippy::upper_case_acronyms)] -#[derive(Debug, Clone, Copy)] -pub struct UVSphere { - /// The radius of the sphere. - pub radius: f32, - /// Longitudinal sectors - pub sectors: usize, - /// Latitudinal stacks - pub stacks: usize, -} - -impl Default for UVSphere { - fn default() -> Self { - Self { - radius: 1.0, - sectors: 36, - stacks: 18, - } - } -} - -impl From for Mesh { - fn from(sphere: UVSphere) -> Self { - // Largely inspired from http://www.songho.ca/opengl/gl_sphere.html - - let sectors = sphere.sectors as f32; - let stacks = sphere.stacks as f32; - let length_inv = 1. / sphere.radius; - let sector_step = 2. * PI / sectors; - let stack_step = PI / stacks; - - let mut vertices: Vec<[f32; 3]> = Vec::with_capacity(sphere.stacks * sphere.sectors); - let mut normals: Vec<[f32; 3]> = Vec::with_capacity(sphere.stacks * sphere.sectors); - let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(sphere.stacks * sphere.sectors); - let mut indices: Vec = Vec::with_capacity(sphere.stacks * sphere.sectors * 2 * 3); - - for i in 0..sphere.stacks + 1 { - let stack_angle = PI / 2. - (i as f32) * stack_step; - let xy = sphere.radius * stack_angle.cos(); - let z = sphere.radius * stack_angle.sin(); - - for j in 0..sphere.sectors + 1 { - let sector_angle = (j as f32) * sector_step; - let x = xy * sector_angle.cos(); - let y = xy * sector_angle.sin(); - - vertices.push([x, y, z]); - normals.push([x * length_inv, y * length_inv, z * length_inv]); - uvs.push([(j as f32) / sectors, (i as f32) / stacks]); - } - } - - // indices - // k1--k1+1 - // | / | - // | / | - // k2--k2+1 - for i in 0..sphere.stacks { - let mut k1 = i * (sphere.sectors + 1); - let mut k2 = k1 + sphere.sectors + 1; - for _j in 0..sphere.sectors { - if i != 0 { - indices.push(k1 as u32); - indices.push(k2 as u32); - indices.push((k1 + 1) as u32); - } - if i != sphere.stacks - 1 { - indices.push((k1 + 1) as u32); - indices.push(k2 as u32); - indices.push((k2 + 1) as u32); - } - k1 += 1; - k2 += 1; - } - } - - Mesh::new(PrimitiveTopology::TriangleList) - .with_indices(Some(Indices::U32(indices))) - .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices) - .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) - } -} diff --git a/errors/B0004.md b/errors/B0004.md index 55a2d3f8dab2d..cf2615cff7911 100644 --- a/errors/B0004.md +++ b/errors/B0004.md @@ -33,7 +33,7 @@ fn setup_cube( .with_children(|parent| { // cube parent.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() @@ -80,7 +80,7 @@ fn setup_cube( .with_children(|parent| { // cube parent.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() diff --git a/examples/2d/2d_shapes.rs b/examples/2d/2d_shapes.rs index 7fa168fe697c1..3e6146ad6ad02 100644 --- a/examples/2d/2d_shapes.rs +++ b/examples/2d/2d_shapes.rs @@ -18,38 +18,41 @@ fn setup( // Circle commands.spawn(MaterialMesh2dBundle { - mesh: meshes.add(shape::Circle::new(50.).into()).into(), + mesh: meshes + .add(primitives::Circle { radius: 50.0 }.into()) + .into(), material: materials.add(ColorMaterial::from(Color::PURPLE)), - transform: Transform::from_translation(Vec3::new(-150., 0., 0.)), + transform: Transform::from_translation(Vec3::new(-150.0, 0.0, 0.0)), ..default() }); - // Rectangle - commands.spawn(SpriteBundle { - sprite: Sprite { - color: Color::rgb(0.25, 0.25, 0.75), - custom_size: Some(Vec2::new(50.0, 100.0)), - ..default() - }, - transform: Transform::from_translation(Vec3::new(-50., 0., 0.)), + // Ellipse + commands.spawn(MaterialMesh2dBundle { + mesh: meshes + .add(primitives::Ellipse::new(50.0, 100.0).into()) + .into(), + material: materials.add(ColorMaterial::from(Color::rgb(0.25, 0.25, 0.75))), + transform: Transform::from_translation(Vec3::new(-50.0, 0.0, 0.0)), ..default() }); - // Quad + // Rectangle commands.spawn(MaterialMesh2dBundle { mesh: meshes - .add(shape::Quad::new(Vec2::new(50., 100.)).into()) + .add(primitives::Rectangle::new(50.0, 100.0).into()) .into(), material: materials.add(ColorMaterial::from(Color::LIME_GREEN)), - transform: Transform::from_translation(Vec3::new(50., 0., 0.)), + transform: Transform::from_translation(Vec3::new(50.0, 0.0, 0.0)), ..default() }); // Hexagon commands.spawn(MaterialMesh2dBundle { - mesh: meshes.add(shape::RegularPolygon::new(50., 6).into()).into(), + mesh: meshes + .add(primitives::RegularPolygon::new(50.0, 6).into()) + .into(), material: materials.add(ColorMaterial::from(Color::TURQUOISE)), - transform: Transform::from_translation(Vec3::new(150., 0., 0.)), + transform: Transform::from_translation(Vec3::new(150.0, 0.0, 0.0)), ..default() }); } diff --git a/examples/2d/bloom_2d.rs b/examples/2d/bloom_2d.rs index 760804bcfbd60..296539b8c2a17 100644 --- a/examples/2d/bloom_2d.rs +++ b/examples/2d/bloom_2d.rs @@ -48,7 +48,9 @@ fn setup( // Circle mesh commands.spawn(MaterialMesh2dBundle { - mesh: meshes.add(shape::Circle::new(100.).into()).into(), + mesh: meshes + .add(primitives::Circle { radius: 100. }.into()) + .into(), // 4. Put something bright in a dark environment to see the effect material: materials.add(ColorMaterial::from(Color::rgb(7.5, 0.0, 7.5))), transform: Transform::from_translation(Vec3::new(-200., 0., 0.)), @@ -58,7 +60,7 @@ fn setup( // Hexagon mesh commands.spawn(MaterialMesh2dBundle { mesh: meshes - .add(shape::RegularPolygon::new(100., 6).into()) + .add(primitives::RegularPolygon::new(100., 6).into()) .into(), // 4. Put something bright in a dark environment to see the effect material: materials.add(ColorMaterial::from(Color::rgb(6.25, 9.4, 9.1))), diff --git a/examples/2d/mesh2d.rs b/examples/2d/mesh2d.rs index bc419e532180b..5ac3f682dc7b6 100644 --- a/examples/2d/mesh2d.rs +++ b/examples/2d/mesh2d.rs @@ -1,6 +1,6 @@ -//! Shows how to render a polygonal [`Mesh`], generated from a [`Quad`] primitive, in a 2D scene. +//! Shows how to render a polygonal [`Mesh`], generated from a [`Rectangle`] primitive, in a 2D scene. //! -//! [`Quad`]: shape::Quad +//! [`Rectangle`]: primitives::Rectangle use bevy::{prelude::*, sprite::MaterialMesh2dBundle}; @@ -18,7 +18,9 @@ fn setup( ) { commands.spawn(Camera2dBundle::default()); commands.spawn(MaterialMesh2dBundle { - mesh: meshes.add(Mesh::from(shape::Quad::default())).into(), + mesh: meshes + .add(Mesh::from(primitives::Rectangle::default())) + .into(), transform: Transform::default().with_scale(Vec3::splat(128.)), material: materials.add(ColorMaterial::from(Color::PURPLE)), ..default() diff --git a/examples/2d/mesh2d_vertex_color_texture.rs b/examples/2d/mesh2d_vertex_color_texture.rs index 1e439e28c9d16..8bd3dfae64cca 100644 --- a/examples/2d/mesh2d_vertex_color_texture.rs +++ b/examples/2d/mesh2d_vertex_color_texture.rs @@ -1,7 +1,7 @@ -//! Shows how to render a polygonal [`Mesh`], generated from a [`Quad`] primitive, in a 2D scene. +//! Shows how to render a polygonal [`Mesh`], generated from a [`Rectangle`] primitive, in a 2D scene. //! Adds a texture and colored vertices, giving per-vertex tinting. //! -//! [`Quad`]: shape::Quad +//! [`Rectangle`]: primitives::Rectangle use bevy::{ prelude::*, @@ -23,9 +23,9 @@ fn setup( ) { // Load the Bevy logo as a texture let texture_handle = asset_server.load("branding/banner.png"); - // Build a default quad mesh - let mut mesh = Mesh::from(shape::Quad::default()); - // Build vertex colors for the quad. One entry per vertex (the corners of the quad) + // Build a default rectangle mesh + let mut mesh = Mesh::from(primitives::Rectangle::default()); + // Build vertex colors for the rectangle. One entry per vertex (the corners of the rectangle) let vertex_colors: Vec<[f32; 4]> = vec![ Color::RED.as_rgba_f32(), Color::GREEN.as_rgba_f32(), @@ -40,7 +40,7 @@ fn setup( // Spawn camera commands.spawn(Camera2dBundle::default()); - // Spawn the quad with vertex colors + // Spawn the rectangle with vertex colors commands.spawn(MaterialMesh2dBundle { mesh: mesh_handle.clone(), transform: Transform::from_translation(Vec3::new(-96., 0., 0.)) @@ -49,7 +49,7 @@ fn setup( ..default() }); - // Spawning the quad with vertex colors and a texture results in tinting + // Spawning the rectangle with vertex colors and a texture results in tinting commands.spawn(MaterialMesh2dBundle { mesh: mesh_handle, transform: Transform::from_translation(Vec3::new(96., 0., 0.)) diff --git a/examples/3d/3d_gizmos.rs b/examples/3d/3d_gizmos.rs index ecee465e07b10..6e05998cabcd7 100644 --- a/examples/3d/3d_gizmos.rs +++ b/examples/3d/3d_gizmos.rs @@ -23,13 +23,13 @@ fn setup( }); // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane::from_size(5.0))), + mesh: meshes.add(shape::PlaneMesh::default().size(Vec2::splat(5.0)).into()), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); // cube commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(shape::CuboidMesh::new(1.0, 1.0, 1.0).into()), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs index ca142616fdb53..486d93991d9c0 100644 --- a/examples/3d/3d_scene.rs +++ b/examples/3d/3d_scene.rs @@ -17,14 +17,14 @@ fn setup( ) { // circular base commands.spawn(PbrBundle { - mesh: meshes.add(shape::Circle::new(4.0).into()), + mesh: meshes.add(primitives::Circle { radius: 4.0 }.into()), material: materials.add(Color::WHITE.into()), transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), ..default() }); // cube commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(primitives::Cuboid::new(1.0, 1.0, 1.0).into()), material: materials.add(Color::rgb_u8(124, 144, 255).into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() diff --git a/examples/3d/3d_shapes.rs b/examples/3d/3d_shapes.rs index 999d11a74c878..4388be54629a3 100644 --- a/examples/3d/3d_shapes.rs +++ b/examples/3d/3d_shapes.rs @@ -34,13 +34,14 @@ fn setup( }); let shapes = [ - meshes.add(shape::Cube::default().into()), - meshes.add(shape::Box::default().into()), - meshes.add(shape::Capsule::default().into()), - meshes.add(shape::Torus::default().into()), - meshes.add(shape::Cylinder::default().into()), - meshes.add(shape::Icosphere::default().try_into().unwrap()), - meshes.add(shape::UVSphere::default().into()), + meshes.add(primitives::Cuboid::default().into()), + meshes.add(primitives::Capsule::default().into()), + meshes.add(primitives::Torus::default().into()), + meshes.add(primitives::Cylinder::default().into()), + meshes.add(primitives::Cone::default().into()), + meshes.add(primitives::ConicalFrustum::default().into()), + meshes.add(primitives::Sphere { radius: 0.5 }.mesh().ico(6).unwrap()), + meshes.add(primitives::Sphere { radius: 0.5 }.mesh().uv(6, 6)), ]; let num_shapes = shapes.len(); @@ -75,13 +76,18 @@ fn setup( // ground plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(50.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(50.0)) + .into(), + ), material: materials.add(Color::SILVER.into()), ..default() }); commands.spawn(Camera3dBundle { - transform: Transform::from_xyz(0.0, 6., 12.0).looking_at(Vec3::new(0., 1., 0.), Vec3::Y), + transform: Transform::from_xyz(0.0, 5.0, 12.0).looking_at(Vec3::new(0., 1., 0.), Vec3::Y), ..default() }); } diff --git a/examples/3d/3d_viewport_to_world.rs b/examples/3d/3d_viewport_to_world.rs index 308e379a4e113..b6d941fa669d6 100644 --- a/examples/3d/3d_viewport_to_world.rs +++ b/examples/3d/3d_viewport_to_world.rs @@ -51,7 +51,12 @@ fn setup( // plane commands.spawn(( PbrBundle { - mesh: meshes.add(shape::Plane::from_size(20.).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(20.)) + .into(), + ), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }, diff --git a/examples/3d/anti_aliasing.rs b/examples/3d/anti_aliasing.rs index 849bdbbc93a83..d4dda98033902 100644 --- a/examples/3d/anti_aliasing.rs +++ b/examples/3d/anti_aliasing.rs @@ -259,7 +259,12 @@ fn setup( ) { // Plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(50.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(50.0)) + .into(), + ), material: materials.add(Color::rgb(0.1, 0.2, 0.1).into()), ..default() }); @@ -272,7 +277,7 @@ fn setup( // Cubes for i in 0..5 { commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 0.25 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(0.25)))), material: cube_material.clone(), transform: Transform::from_xyz(i as f32 * 0.25 - 1.0, 0.125, -i as f32 * 0.5), ..default() diff --git a/examples/3d/atmospheric_fog.rs b/examples/3d/atmospheric_fog.rs index 60071e2dd9554..22cd01a4b963c 100644 --- a/examples/3d/atmospheric_fog.rs +++ b/examples/3d/atmospheric_fog.rs @@ -79,7 +79,7 @@ fn setup_terrain_scene( // Sky commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Box::default())), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(StandardMaterial { base_color: Color::hex("888888").unwrap(), unlit: true, diff --git a/examples/3d/blend_modes.rs b/examples/3d/blend_modes.rs index d378a713ec82e..42870fce5204a 100644 --- a/examples/3d/blend_modes.rs +++ b/examples/3d/blend_modes.rs @@ -37,13 +37,7 @@ fn setup( asset_server: Res, ) { let base_color = Color::rgba(0.9, 0.2, 0.3, 1.0); - let icosphere_mesh = meshes.add( - Mesh::try_from(shape::Icosphere { - radius: 0.9, - subdivisions: 7, - }) - .unwrap(), - ); + let icosphere_mesh = meshes.add(primitives::Sphere { radius: 0.9 }.mesh().ico(7).unwrap()); // Opaque let opaque = commands @@ -149,7 +143,12 @@ fn setup( let black_material = materials.add(Color::BLACK.into()); let white_material = materials.add(Color::WHITE.into()); - let plane_mesh = meshes.add(shape::Plane::from_size(2.0).into()); + let plane_mesh = meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(2.0)) + .into(), + ); for x in -3..4 { for z in -3..4 { diff --git a/examples/3d/bloom_3d.rs b/examples/3d/bloom_3d.rs index b67f1d3909a93..8c72c4522edc8 100644 --- a/examples/3d/bloom_3d.rs +++ b/examples/3d/bloom_3d.rs @@ -55,14 +55,7 @@ fn setup_scene( ..default() }); - let mesh = meshes.add( - shape::Icosphere { - radius: 0.5, - subdivisions: 5, - } - .try_into() - .unwrap(), - ); + let mesh = meshes.add(primitives::Sphere { radius: 0.5 }.mesh().ico(5).unwrap()); for x in -5..5 { for z in -5..5 { diff --git a/examples/3d/deferred_rendering.rs b/examples/3d/deferred_rendering.rs index 6b39e3fd4f160..8cad535d9178e 100644 --- a/examples/3d/deferred_rendering.rs +++ b/examples/3d/deferred_rendering.rs @@ -103,16 +103,18 @@ fn setup( // Plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(50.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(50.0)) + .into(), + ), material: forward_mat_h.clone(), ..default() }); - let cube_h = meshes.add(Mesh::from(shape::Cube { size: 0.1 })); - let sphere_h = meshes.add(Mesh::from(shape::UVSphere { - radius: 0.125, - ..default() - })); + let cube_h = meshes.add(primitives::Cuboid::from_size(Vec3::splat(0.1)).into()); + let sphere_h = meshes.add(primitives::Sphere { radius: 0.125 }.mesh().uv(36, 18)); // Cubes commands.spawn(PbrBundle { @@ -196,7 +198,7 @@ fn setup( // sky commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Box::default())), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(StandardMaterial { base_color: Color::hex("888888").unwrap(), unlit: true, @@ -257,7 +259,7 @@ fn setup_parallax( let normal_handle = asset_server.load("textures/parallax_example/cube_normal.png"); normal.0 = Some(normal_handle); - let mut cube: Mesh = shape::Cube { size: 0.15 }.into(); + let mut cube: Mesh = primitives::Cuboid::from_size(Vec3::splat(0.15)).into(); // NOTE: for normal maps and depth maps to work, the mesh // needs tangents generated. diff --git a/examples/3d/fog.rs b/examples/3d/fog.rs index 253c2c8d567b5..36078ca6d6dab 100644 --- a/examples/3d/fog.rs +++ b/examples/3d/fog.rs @@ -58,16 +58,9 @@ fn setup_pyramid_scene( // pillars for (x, z) in &[(-1.5, -1.5), (1.5, -1.5), (1.5, 1.5), (-1.5, 1.5)] { commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Box { - min_x: -0.5, - max_x: 0.5, - min_z: -0.5, - max_z: 0.5, - min_y: 0.0, - max_y: 3.0, - })), + mesh: meshes.add(primitives::Cuboid::new(1.0, 3.0, 1.0).into()), material: stone.clone(), - transform: Transform::from_xyz(*x, 0.0, *z), + transform: Transform::from_xyz(*x, 1.5, *z), ..default() }); } @@ -75,7 +68,7 @@ fn setup_pyramid_scene( // orb commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::try_from(shape::Icosphere::default()).unwrap()), + mesh: meshes.add(primitives::Sphere::default().into()), material: materials.add(StandardMaterial { base_color: Color::hex("126212CC").unwrap(), reflectance: 1.0, @@ -94,17 +87,14 @@ fn setup_pyramid_scene( // steps for i in 0..50 { - let size = i as f32 / 2.0 + 3.0; - let y = -i as f32 / 2.0; + let size = i as f32 + 6.0; + let y = -i as f32 / 2.0 + 0.25; commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Box { - min_x: -size, - max_x: size, - min_z: -size, - max_z: size, - min_y: 0.0, - max_y: 0.5, - })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::new( + 2.0 * size, + 0.5, + 2.0 * size, + ))), material: stone.clone(), transform: Transform::from_xyz(0.0, y, 0.0), ..default() @@ -113,7 +103,7 @@ fn setup_pyramid_scene( // sky commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Box::default())), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(StandardMaterial { base_color: Color::hex("888888").unwrap(), unlit: true, diff --git a/examples/3d/lighting.rs b/examples/3d/lighting.rs index 614bf575ab5df..e19b419231b27 100644 --- a/examples/3d/lighting.rs +++ b/examples/3d/lighting.rs @@ -25,7 +25,12 @@ fn setup( ) { // ground plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(10.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(10.0)) + .into(), + ), material: materials.add(StandardMaterial { base_color: Color::WHITE, perceptual_roughness: 1.0, @@ -38,7 +43,7 @@ fn setup( let mut transform = Transform::from_xyz(2.5, 2.5, 0.0); transform.rotate_z(PI / 2.); commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Box::new(5.0, 0.15, 5.0))), + mesh: meshes.add(Mesh::from(primitives::Cuboid::new(5.0, 0.15, 5.0))), transform, material: materials.add(StandardMaterial { base_color: Color::INDIGO, @@ -51,7 +56,7 @@ fn setup( let mut transform = Transform::from_xyz(0.0, 2.5, -2.5); transform.rotate_x(PI / 2.); commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Box::new(5.0, 0.15, 5.0))), + mesh: meshes.add(Mesh::from(primitives::Cuboid::new(5.0, 0.15, 5.0))), transform, material: materials.add(StandardMaterial { base_color: Color::INDIGO, @@ -66,7 +71,7 @@ fn setup( transform.rotate_y(PI / 8.); commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Quad::new(Vec2::new(2.0, 0.5)))), + mesh: meshes.add(Mesh::from(primitives::Rectangle::new(2.0, 0.5))), transform, material: materials.add(StandardMaterial { base_color_texture: Some(asset_server.load("branding/bevy_logo_light.png")), @@ -83,7 +88,7 @@ fn setup( // cube commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::new(1.0, 1.0, 1.0))), material: materials.add(StandardMaterial { base_color: Color::PINK, ..default() @@ -96,10 +101,7 @@ fn setup( // sphere commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::UVSphere { - radius: 0.5, - ..default() - })), + mesh: meshes.add(primitives::Sphere { radius: 0.5 }.mesh().uv(32, 18)), material: materials.add(StandardMaterial { base_color: Color::LIME_GREEN, ..default() @@ -131,10 +133,7 @@ fn setup( }) .with_children(|builder| { builder.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::UVSphere { - radius: 0.1, - ..default() - })), + mesh: meshes.add(primitives::Sphere { radius: 0.1 }.mesh().uv(32, 18)), material: materials.add(StandardMaterial { base_color: Color::RED, emissive: Color::rgba_linear(7.13, 0.0, 0.0, 0.0), @@ -162,11 +161,7 @@ fn setup( .with_children(|builder| { builder.spawn(PbrBundle { transform: Transform::from_rotation(Quat::from_rotation_x(PI / 2.0)), - mesh: meshes.add(Mesh::from(shape::Capsule { - depth: 0.125, - radius: 0.1, - ..default() - })), + mesh: meshes.add(Mesh::from(primitives::Capsule::new(0.1, 0.125).mesh())), material: materials.add(StandardMaterial { base_color: Color::GREEN, emissive: Color::rgba_linear(0.0, 7.13, 0.0, 0.0), @@ -191,10 +186,7 @@ fn setup( }) .with_children(|builder| { builder.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::UVSphere { - radius: 0.1, - ..default() - })), + mesh: meshes.add(primitives::Sphere { radius: 0.1 }.mesh().uv(32, 18)), material: materials.add(StandardMaterial { base_color: Color::BLUE, emissive: Color::rgba_linear(0.0, 0.0, 7.13, 0.0), diff --git a/examples/3d/orthographic.rs b/examples/3d/orthographic.rs index c34e06a788513..b72dceda276eb 100644 --- a/examples/3d/orthographic.rs +++ b/examples/3d/orthographic.rs @@ -29,31 +29,33 @@ fn setup( // plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(5.0).into()), + mesh: meshes.add(Mesh::from( + primitives::Plane3d::default().mesh().size(Vec2::splat(5.0)), + )), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); // cubes commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::ONE))), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(1.5, 0.5, 1.5), ..default() }); commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::ONE))), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(1.5, 0.5, -1.5), ..default() }); commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::ONE))), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(-1.5, 0.5, 1.5), ..default() }); commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::ONE))), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(-1.5, 0.5, -1.5), ..default() diff --git a/examples/3d/parallax_mapping.rs b/examples/3d/parallax_mapping.rs index 37098494ee2eb..55580fabe10de 100644 --- a/examples/3d/parallax_mapping.rs +++ b/examples/3d/parallax_mapping.rs @@ -233,25 +233,17 @@ fn setup( }) .with_children(|commands| { // represent the light source as a sphere - let mesh = meshes.add( - shape::Icosphere { - radius: 0.05, - subdivisions: 3, - } - .try_into() - .unwrap(), - ); + let mesh = meshes.add(primitives::Sphere { radius: 0.05 }.mesh().ico(3).unwrap()); commands.spawn(PbrBundle { mesh, ..default() }); }); // Plane commands.spawn(PbrBundle { mesh: meshes.add( - shape::Plane { - size: 10.0, - subdivisions: 0, - } - .into(), + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(10.0)) + .into(), ), material: materials.add(StandardMaterial { // standard material derived from dark green, but @@ -284,7 +276,7 @@ fn setup( mesh: meshes.add( // NOTE: for normal maps and depth maps to work, the mesh // needs tangents generated. - Mesh::from(shape::Cube { size: 1.0 }) + Mesh::from(primitives::Cuboid::from_size(Vec3::splat(1.0))) .with_generated_tangents() .unwrap(), ), @@ -295,7 +287,7 @@ fn setup( )); let background_cube = meshes.add( - Mesh::from(shape::Cube { size: 40.0 }) + Mesh::from(primitives::Cuboid::from_size(Vec3::splat(40.0))) .with_generated_tangents() .unwrap(), ); diff --git a/examples/3d/parenting.rs b/examples/3d/parenting.rs index 827d8ffbec782..aff4b29726758 100644 --- a/examples/3d/parenting.rs +++ b/examples/3d/parenting.rs @@ -28,7 +28,7 @@ fn setup( mut meshes: ResMut>, mut materials: ResMut>, ) { - let cube_handle = meshes.add(Mesh::from(shape::Cube { size: 2.0 })); + let cube_handle = meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(2.0)))); let cube_material_handle = materials.add(StandardMaterial { base_color: Color::rgb(0.8, 0.7, 0.6), ..default() diff --git a/examples/3d/pbr.rs b/examples/3d/pbr.rs index 348fc7d9d5750..88bafb0bb96d1 100644 --- a/examples/3d/pbr.rs +++ b/examples/3d/pbr.rs @@ -17,13 +17,7 @@ fn setup( mut materials: ResMut>, asset_server: Res, ) { - let sphere_mesh = meshes.add( - Mesh::try_from(shape::Icosphere { - radius: 0.45, - ..default() - }) - .unwrap(), - ); + let sphere_mesh = meshes.add(primitives::Sphere { radius: 0.45 }.mesh().ico(5).unwrap()); // add entities to the world for y in -2..=2 { for x in -5..=5 { diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index a319e7d6e4e03..35494e1d3b2ef 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -63,7 +63,7 @@ fn setup( let image_handle = images.add(image); - let cube_handle = meshes.add(Mesh::from(shape::Cube { size: 4.0 })); + let cube_handle = meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(4.0)))); let cube_material_handle = materials.add(StandardMaterial { base_color: Color::rgb(0.8, 0.7, 0.6), reflectance: 0.02, @@ -117,8 +117,8 @@ fn setup( first_pass_layer, )); - let cube_size = 4.0; - let cube_handle = meshes.add(Mesh::from(shape::Box::new(cube_size, cube_size, cube_size))); + let cube_size = Vec3::splat(4.0); + let cube_handle = meshes.add(Mesh::from(primitives::Cuboid::from_size(cube_size))); // This material has the texture that has been rendered. let material_handle = materials.add(StandardMaterial { diff --git a/examples/3d/shadow_biases.rs b/examples/3d/shadow_biases.rs index 8900b75517f55..9ea1560cb7f85 100644 --- a/examples/3d/shadow_biases.rs +++ b/examples/3d/shadow_biases.rs @@ -41,10 +41,11 @@ fn setup( ..default() }); let sphere_handle = meshes.add( - Mesh::try_from(shape::Icosphere { + primitives::Sphere { radius: sphere_radius, - ..default() - }) + } + .mesh() + .ico(5) .unwrap(), ); @@ -112,7 +113,12 @@ fn setup( // ground plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(2.0 * spawn_plane_depth).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(2.0 * spawn_plane_depth)) + .into(), + ), material: white_handle, ..default() }); diff --git a/examples/3d/shadow_caster_receiver.rs b/examples/3d/shadow_caster_receiver.rs index b94144d8dfab4..a15f9d50aa63d 100644 --- a/examples/3d/shadow_caster_receiver.rs +++ b/examples/3d/shadow_caster_receiver.rs @@ -29,20 +29,13 @@ fn setup( ) { let spawn_plane_depth = 500.0f32; let spawn_height = 2.0; - let sphere_radius = 0.25; let white_handle = materials.add(StandardMaterial { base_color: Color::WHITE, perceptual_roughness: 1.0, ..default() }); - let sphere_handle = meshes.add( - Mesh::try_from(shape::Icosphere { - radius: sphere_radius, - ..default() - }) - .unwrap(), - ); + let sphere_handle = meshes.add(primitives::Sphere { radius: 0.25 }.mesh().ico(5).unwrap()); // sphere - initially a caster commands.spawn(PbrBundle { @@ -66,7 +59,12 @@ fn setup( // floating plane - initially not a shadow receiver and not a caster commands.spawn(( PbrBundle { - mesh: meshes.add(shape::Plane::from_size(20.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(20.0)) + .into(), + ), material: materials.add(Color::GREEN.into()), transform: Transform::from_xyz(0.0, 1.0, -10.0), ..default() @@ -77,7 +75,12 @@ fn setup( // lower ground plane - initially a shadow receiver commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(20.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(20.0)) + .into(), + ), material: white_handle, ..default() }); diff --git a/examples/3d/spherical_area_lights.rs b/examples/3d/spherical_area_lights.rs index 99dfc14d09eeb..9b631e15f4498 100644 --- a/examples/3d/spherical_area_lights.rs +++ b/examples/3d/spherical_area_lights.rs @@ -22,7 +22,12 @@ fn setup( // plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(100.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(100.0)) + .into(), + ), material: materials.add(StandardMaterial { base_color: Color::rgb(0.2, 0.2, 0.2), perceptual_roughness: 0.08, @@ -36,11 +41,7 @@ fn setup( let radius_range = 0.0..0.8; let pos_len = position_range.end - position_range.start; let radius_len = radius_range.end - radius_range.start; - let mesh = meshes.add(Mesh::from(shape::UVSphere { - sectors: 128, - stacks: 64, - ..default() - })); + let mesh = meshes.add(primitives::Sphere::default().mesh().uv(128, 64)); for i in 0..COUNT { let percent = i as f32 / COUNT as f32; diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index 1a0f215d6464f..48b70367f6f98 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -24,7 +24,12 @@ fn setup( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(100.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(100.0)) + .into(), + ), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/3d/spotlight.rs b/examples/3d/spotlight.rs index db1ba40172817..b196495af48de 100644 --- a/examples/3d/spotlight.rs +++ b/examples/3d/spotlight.rs @@ -35,14 +35,19 @@ fn setup( ) { // ground plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(100.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(100.0)) + .into(), + ), material: materials.add(Color::WHITE.into()), ..default() }); // cubes let mut rng = StdRng::seed_from_u64(19878367467713); - let cube_mesh = meshes.add(Mesh::from(shape::Cube { size: 0.5 })); + let cube_mesh = meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(0.5)))); let blue = materials.add(Color::rgb_u8(124, 144, 255).into()); for _ in 0..40 { let x = rng.gen_range(-5.0..5.0); @@ -59,14 +64,8 @@ fn setup( )); } - let sphere_mesh = meshes.add(Mesh::from(shape::UVSphere { - radius: 0.05, - ..default() - })); - let sphere_mesh_direction = meshes.add(Mesh::from(shape::UVSphere { - radius: 0.1, - ..default() - })); + let sphere_mesh = meshes.add(primitives::Sphere { radius: 0.05 }.mesh().uv(32, 18)); + let sphere_mesh_direction = meshes.add(primitives::Sphere { radius: 0.1 }.mesh().uv(32, 18)); let red_emissive = materials.add(StandardMaterial { base_color: Color::RED, emissive: Color::rgba_linear(1.0, 0.0, 0.0, 0.0), diff --git a/examples/3d/ssao.rs b/examples/3d/ssao.rs index 61e1ef31953d7..f86a69659db95 100644 --- a/examples/3d/ssao.rs +++ b/examples/3d/ssao.rs @@ -48,30 +48,26 @@ fn setup( ..default() }); commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::ONE))), material: material.clone(), transform: Transform::from_xyz(0.0, 0.0, 1.0), ..default() }); commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::ONE))), material: material.clone(), transform: Transform::from_xyz(0.0, -1.0, 0.0), ..default() }); commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::ONE))), material, transform: Transform::from_xyz(1.0, 0.0, 0.0), ..default() }); commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::UVSphere { - radius: 0.4, - sectors: 72, - stacks: 36, - })), + mesh: meshes.add(primitives::Sphere { radius: 0.4 }.mesh().uv(72, 36)), material: materials.add(StandardMaterial { base_color: Color::rgb(0.4, 0.4, 0.4), perceptual_roughness: 1.0, diff --git a/examples/3d/texture.rs b/examples/3d/texture.rs index b0036abb4872f..2cf0c4f501837 100644 --- a/examples/3d/texture.rs +++ b/examples/3d/texture.rs @@ -24,10 +24,10 @@ fn setup( // create a new quad mesh. this is what we will apply the texture to let quad_width = 8.0; - let quad_handle = meshes.add(Mesh::from(shape::Quad::new(Vec2::new( + let quad_handle = meshes.add(Mesh::from(primitives::Rectangle::new( quad_width, quad_width * aspect, - )))); + ))); // this material renders the texture normally let material_handle = materials.add(StandardMaterial { diff --git a/examples/3d/tonemapping.rs b/examples/3d/tonemapping.rs index 62601e8cb2434..0912d04d67724 100644 --- a/examples/3d/tonemapping.rs +++ b/examples/3d/tonemapping.rs @@ -2,7 +2,6 @@ use bevy::{ core_pipeline::tonemapping::Tonemapping, - math::vec2, pbr::CascadeShadowConfigBuilder, prelude::*, reflect::TypePath, @@ -106,7 +105,12 @@ fn setup_basic_scene( // plane commands.spawn(( PbrBundle { - mesh: meshes.add(shape::Plane::from_size(50.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(50.0)) + .into(), + ), material: materials.add(Color::rgb(0.1, 0.2, 0.1).into()), ..default() }, @@ -119,7 +123,7 @@ fn setup_basic_scene( ..default() }); - let cube_mesh = meshes.add(Mesh::from(shape::Cube { size: 0.25 })); + let cube_mesh = meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(0.25)))); for i in 0..5 { commands.spawn(( PbrBundle { @@ -133,10 +137,7 @@ fn setup_basic_scene( } // spheres - let sphere_mesh = meshes.add(Mesh::from(shape::UVSphere { - radius: 0.125, - ..default() - })); + let sphere_mesh = meshes.add(primitives::Sphere { radius: 0.125 }.mesh().uv(32, 18)); for i in 0..6 { let j = i % 3; let s_val = if i < 3 { 0.0 } else { 0.2 }; @@ -225,10 +226,9 @@ fn setup_color_gradient_scene( commands.spawn(( MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Quad { - size: vec2(1.0, 1.0) * 0.7, - flip: false, - })), + mesh: meshes.add(Mesh::from(primitives::Rectangle::from_size(Vec2::splat( + 0.7, + )))), material: materials.add(ColorGradientMaterial {}), transform, visibility: Visibility::Hidden, @@ -250,10 +250,7 @@ fn setup_image_viewer_scene( // exr/hdr viewer (exr requires enabling bevy feature) commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Quad { - size: vec2(1.0, 1.0), - flip: false, - })), + mesh: meshes.add(Mesh::from(primitives::Rectangle::default())), material: materials.add(StandardMaterial { base_color_texture: None, unlit: true, @@ -336,7 +333,7 @@ fn update_image_viewer( if let Some(image_changed) = images.get(image_changed_id) { let size = image_changed.size_f32().normalize_or_zero() * 1.4; // Resize Mesh - let quad = Mesh::from(shape::Quad::new(size)); + let quad = Mesh::from(primitives::Rectangle::from_size(size)); meshes.insert(mesh_h, quad); } } diff --git a/examples/3d/transmission.rs b/examples/3d/transmission.rs index 591e4e7bc4311..83af347c16b2f 100644 --- a/examples/3d/transmission.rs +++ b/examples/3d/transmission.rs @@ -68,27 +68,18 @@ fn setup( mut materials: ResMut>, asset_server: Res, ) { - let icosphere_mesh = meshes.add( - Mesh::try_from(shape::Icosphere { - radius: 0.9, - subdivisions: 7, - }) - .unwrap(), + let icosphere_mesh = meshes.add(primitives::Sphere { radius: 0.9 }.mesh().ico(7).unwrap()); + let cube_mesh = meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(0.7)))); + let plane_mesh = meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(2.0)) + .into(), ); - let cube_mesh = meshes.add(Mesh::from(shape::Cube { size: 0.7 })); - - let plane_mesh = meshes.add(shape::Plane::from_size(2.0).into()); - - let cylinder_mesh = meshes.add( - Mesh::try_from(shape::Cylinder { - radius: 0.5, - height: 2.0, - resolution: 50, - segments: 1, - }) - .unwrap(), - ); + let cylinder_mesh = meshes.add(Mesh::from( + primitives::Cylinder::new(0.5, 2.0).mesh().resolution(50), + )); // Cube #1 commands.spawn(( diff --git a/examples/3d/transparency_3d.rs b/examples/3d/transparency_3d.rs index f048f959423d2..8ff824cae3e9f 100644 --- a/examples/3d/transparency_3d.rs +++ b/examples/3d/transparency_3d.rs @@ -20,19 +20,18 @@ fn setup( ) { // opaque plane, uses `alpha_mode: Opaque` by default commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(6.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(6.0)) + .into(), + ), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); // transparent sphere, uses `alpha_mode: Mask(f32)` commands.spawn(PbrBundle { - mesh: meshes.add( - Mesh::try_from(shape::Icosphere { - radius: 0.5, - subdivisions: 3, - }) - .unwrap(), - ), + mesh: meshes.add(primitives::Sphere { radius: 0.5 }.mesh().ico(3).unwrap()), material: materials.add(StandardMaterial { // Alpha channel of the color controls transparency. // We set it to 0.0 here, because it will be changed over time in the @@ -49,13 +48,7 @@ fn setup( }); // transparent unlit sphere, uses `alpha_mode: Mask(f32)` commands.spawn(PbrBundle { - mesh: meshes.add( - Mesh::try_from(shape::Icosphere { - radius: 0.5, - subdivisions: 3, - }) - .unwrap(), - ), + mesh: meshes.add(primitives::Sphere { radius: 0.5 }.mesh().ico(3).unwrap()), material: materials.add(StandardMaterial { base_color: Color::rgba(0.2, 0.7, 0.1, 0.0), alpha_mode: AlphaMode::Mask(0.5), @@ -67,7 +60,7 @@ fn setup( }); // transparent cube, uses `alpha_mode: Blend` commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), // Notice how there is no need to set the `alpha_mode` explicitly here. // When converting a color to a material using `into()`, the alpha mode is // automatically set to `Blend` if the alpha channel is anything lower than 1.0. @@ -77,13 +70,7 @@ fn setup( }); // opaque sphere commands.spawn(PbrBundle { - mesh: meshes.add( - Mesh::try_from(shape::Icosphere { - radius: 0.5, - subdivisions: 3, - }) - .unwrap(), - ), + mesh: meshes.add(primitives::Sphere::default().mesh().ico(3).unwrap()), material: materials.add(Color::rgb(0.7, 0.2, 0.1).into()), transform: Transform::from_xyz(0.0, 0.5, -1.5), ..default() diff --git a/examples/3d/two_passes.rs b/examples/3d/two_passes.rs index eded9674964da..83f00953820a9 100644 --- a/examples/3d/two_passes.rs +++ b/examples/3d/two_passes.rs @@ -17,13 +17,18 @@ fn setup( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(5.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(5.0)) + .into(), + ), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); // cube commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() diff --git a/examples/3d/vertex_colors.rs b/examples/3d/vertex_colors.rs index 93d05882b76d5..9f85f3d5292ba 100644 --- a/examples/3d/vertex_colors.rs +++ b/examples/3d/vertex_colors.rs @@ -17,13 +17,18 @@ fn setup( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(5.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(5.0)) + .into(), + ), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); // cube // Assign vertex colors based on vertex positions - let mut colorful_cube = Mesh::from(shape::Cube { size: 1.0 }); + let mut colorful_cube = Mesh::from(primitives::Cuboid::default()); if let Some(VertexAttributeValues::Float32x3(positions)) = colorful_cube.attribute(Mesh::ATTRIBUTE_POSITION) { diff --git a/examples/3d/wireframe.rs b/examples/3d/wireframe.rs index 058ba18f6c108..0e696b2b8d3c2 100644 --- a/examples/3d/wireframe.rs +++ b/examples/3d/wireframe.rs @@ -54,7 +54,9 @@ fn setup( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane::from_size(5.0))), + mesh: meshes.add(Mesh::from( + primitives::Plane3d::default().mesh().size(Vec2::splat(5.0)), + )), material: materials.add(Color::BLUE.into()), ..default() }); @@ -62,7 +64,7 @@ fn setup( // Red cube: Never renders a wireframe commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::RED.into()), transform: Transform::from_xyz(-1.0, 0.5, -1.0), ..default() @@ -71,7 +73,7 @@ fn setup( )); // Orange cube: Follows global wireframe setting commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::ORANGE.into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() @@ -79,7 +81,7 @@ fn setup( // Green cube: Always renders a wireframe commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::GREEN.into()), transform: Transform::from_xyz(1.0, 0.5, 1.0), ..default() diff --git a/examples/animation/animated_fox.rs b/examples/animation/animated_fox.rs index cf695d81aa1f6..75b834866be2f 100644 --- a/examples/animation/animated_fox.rs +++ b/examples/animation/animated_fox.rs @@ -45,7 +45,12 @@ fn setup( // Plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(500000.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(00000.0)) + .into(), + ), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/animation/animated_transform.rs b/examples/animation/animated_transform.rs index 0a3aa023986c6..04d7e6a2f3162 100644 --- a/examples/animation/animated_transform.rs +++ b/examples/animation/animated_transform.rs @@ -118,7 +118,7 @@ fn setup( commands .spawn(( PbrBundle { - mesh: meshes.add(Mesh::try_from(shape::Icosphere::default()).unwrap()), + mesh: meshes.add(Mesh::try_from(primitives::Sphere::default()).unwrap()), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), ..default() }, @@ -138,7 +138,8 @@ fn setup( p.spawn(( PbrBundle { transform: Transform::from_xyz(1.5, 0.0, 0.0), - mesh: meshes.add(Mesh::from(shape::Cube { size: 0.5 })), + mesh: meshes + .add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(0.5)))), material: materials.add(Color::rgb(0.3, 0.9, 0.3).into()), ..default() }, diff --git a/examples/animation/cubic_curve.rs b/examples/animation/cubic_curve.rs index 92a003edaa723..eb87b2c54e5bb 100644 --- a/examples/animation/cubic_curve.rs +++ b/examples/animation/cubic_curve.rs @@ -38,7 +38,7 @@ fn setup( // Spawning a cube to experiment on commands.spawn(( PbrBundle { - mesh: meshes.add(shape::Cube::default().into()), + mesh: meshes.add(primitives::Cuboid::default().into()), material: materials.add(Color::ORANGE.into()), transform: Transform::from_translation(points[0][0]), ..default() @@ -60,7 +60,12 @@ fn setup( // ground plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(50.).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(50.)) + .into(), + ), material: materials.add(Color::SILVER.into()), ..default() }); diff --git a/examples/async_tasks/async_compute.rs b/examples/async_tasks/async_compute.rs index ebf4eaa986b55..e0cd8fc103ad5 100644 --- a/examples/async_tasks/async_compute.rs +++ b/examples/async_tasks/async_compute.rs @@ -35,7 +35,7 @@ fn add_assets( mut meshes: ResMut>, mut materials: ResMut>, ) { - let box_mesh_handle = meshes.add(Mesh::from(shape::Cube { size: 0.25 })); + let box_mesh_handle = meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(0.25)))); commands.insert_resource(BoxMeshHandle(box_mesh_handle)); let box_material_handle = materials.add(Color::rgb(1.0, 0.2, 0.3).into()); diff --git a/examples/audio/spatial_audio_2d.rs b/examples/audio/spatial_audio_2d.rs index 8387c688d38ac..829052eabacc6 100644 --- a/examples/audio/spatial_audio_2d.rs +++ b/examples/audio/spatial_audio_2d.rs @@ -34,7 +34,9 @@ fn setup( // sound emitter commands.spawn(( MaterialMesh2dBundle { - mesh: meshes.add(shape::Circle::new(15.0).into()).into(), + mesh: meshes + .add(primitives::Circle { radius: 15.0 }.into()) + .into(), material: materials.add(ColorMaterial::from(Color::BLUE)), transform: Transform::from_translation(Vec3::new(0.0, 50.0, 0.0)), ..default() diff --git a/examples/audio/spatial_audio_3d.rs b/examples/audio/spatial_audio_3d.rs index 22a2537468685..5f7861f041fb1 100644 --- a/examples/audio/spatial_audio_3d.rs +++ b/examples/audio/spatial_audio_3d.rs @@ -22,10 +22,7 @@ fn setup( // sound emitter commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::UVSphere { - radius: 0.2, - ..default() - })), + mesh: meshes.add(primitives::Sphere { radius: 0.2 }.mesh().uv(32, 18)), material: materials.add(Color::BLUE.into()), transform: Transform::from_xyz(0.0, 0.0, 0.0), ..default() @@ -43,7 +40,7 @@ fn setup( .with_children(|parent| { // left ear indicator parent.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 0.2 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(0.2)))), material: materials.add(Color::RED.into()), transform: Transform::from_translation(listener.left_ear_offset), ..default() @@ -51,7 +48,7 @@ fn setup( // right ear indicator parent.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 0.2 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(0.2)))), material: materials.add(Color::GREEN.into()), transform: Transform::from_translation(listener.right_ear_offset), ..default() diff --git a/examples/ecs/iter_combinations.rs b/examples/ecs/iter_combinations.rs index 363dfc726c768..e755d29f0ac2a 100644 --- a/examples/ecs/iter_combinations.rs +++ b/examples/ecs/iter_combinations.rs @@ -43,14 +43,7 @@ fn generate_bodies( mut meshes: ResMut>, mut materials: ResMut>, ) { - let mesh = meshes.add( - Mesh::try_from(shape::Icosphere { - radius: 1.0, - subdivisions: 3, - }) - .unwrap(), - ); - + let mesh = meshes.add(primitives::Sphere { radius: 1.0 }.mesh().ico(3).unwrap()); let color_range = 0.5..1.0; let vel_range = -0.5..0.5; @@ -106,13 +99,7 @@ fn generate_bodies( BodyBundle { pbr: PbrBundle { transform: Transform::from_scale(Vec3::splat(star_radius)), - mesh: meshes.add( - Mesh::try_from(shape::Icosphere { - radius: 1.0, - subdivisions: 5, - }) - .unwrap(), - ), + mesh: meshes.add(primitives::Sphere { radius: 1.0 }.mesh().ico(5).unwrap()), material: materials.add(StandardMaterial { base_color: Color::ORANGE_RED, emissive: (Color::ORANGE_RED * 2.), diff --git a/examples/games/breakout.rs b/examples/games/breakout.rs index 9e5392df850c0..566ecceacf062 100644 --- a/examples/games/breakout.rs +++ b/examples/games/breakout.rs @@ -207,7 +207,7 @@ fn setup( // Ball commands.spawn(( MaterialMesh2dBundle { - mesh: meshes.add(shape::Circle::default().into()).into(), + mesh: meshes.add(primitives::Circle::default().into()).into(), material: materials.add(ColorMaterial::from(BALL_COLOR)), transform: Transform::from_translation(BALL_STARTING_POSITION).with_scale(BALL_SIZE), ..default() diff --git a/examples/mobile/src/lib.rs b/examples/mobile/src/lib.rs index d0ce7c2f9bcd2..942665f565428 100644 --- a/examples/mobile/src/lib.rs +++ b/examples/mobile/src/lib.rs @@ -62,26 +62,25 @@ fn setup_scene( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(5.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(5.0)) + .into(), + ), material: materials.add(Color::rgb(0.1, 0.2, 0.1).into()), ..default() }); // cube commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::rgb(0.5, 0.4, 0.3).into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() }); // sphere commands.spawn(PbrBundle { - mesh: meshes.add( - Mesh::try_from(shape::Icosphere { - subdivisions: 4, - radius: 0.5, - }) - .unwrap(), - ), + mesh: meshes.add(primitives::Sphere { radius: 0.5 }.mesh().ico(4).unwrap()), material: materials.add(Color::rgb(0.1, 0.4, 0.8).into()), transform: Transform::from_xyz(1.5, 1.5, 1.5), ..default() diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index a828420387ed6..26455b02f13c0 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -21,7 +21,7 @@ fn setup( ) { // cube commands.spawn(MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), transform: Transform::from_xyz(0.0, 0.5, 0.0), material: materials.add(CustomMaterial {}), ..default() diff --git a/examples/shader/array_texture.rs b/examples/shader/array_texture.rs index 478119cfa9c57..a0a98078edea9 100644 --- a/examples/shader/array_texture.rs +++ b/examples/shader/array_texture.rs @@ -77,7 +77,7 @@ fn create_array_texture( image.reinterpret_stacked_2d_as_array(array_layers); // Spawn some cubes using the array texture - let mesh_handle = meshes.add(Mesh::from(shape::Cube { size: 1.0 })); + let mesh_handle = meshes.add(Mesh::from(primitives::Cuboid::default())); let material_handle = materials.add(ArrayTextureMaterial { array_texture: loading_texture.handle.clone(), }); diff --git a/examples/shader/custom_vertex_attribute.rs b/examples/shader/custom_vertex_attribute.rs index 0411af15a7634..920ea70fdcc02 100644 --- a/examples/shader/custom_vertex_attribute.rs +++ b/examples/shader/custom_vertex_attribute.rs @@ -31,7 +31,7 @@ fn setup( mut meshes: ResMut>, mut materials: ResMut>, ) { - let mesh = Mesh::from(shape::Cube { size: 1.0 }) + let mesh = Mesh::from(primitives::Cuboid::default()) // Sets the custom attribute .with_inserted_attribute( ATTRIBUTE_BLEND_COLOR, diff --git a/examples/shader/extended_material.rs b/examples/shader/extended_material.rs index 7f055a9bef860..d973270250724 100644 --- a/examples/shader/extended_material.rs +++ b/examples/shader/extended_material.rs @@ -24,13 +24,7 @@ fn setup( ) { // sphere commands.spawn(MaterialMeshBundle { - mesh: meshes.add( - Mesh::try_from(shape::Icosphere { - radius: 1.0, - subdivisions: 5, - }) - .unwrap(), - ), + mesh: meshes.add(primitives::Sphere { radius: 1.0 }.mesh().ico(5).unwrap()), transform: Transform::from_xyz(0.0, 0.5, 0.0), material: materials.add(ExtendedMaterial { base: StandardMaterial { diff --git a/examples/shader/fallback_image.rs b/examples/shader/fallback_image.rs index b799b56d27e02..75521b6de74cb 100644 --- a/examples/shader/fallback_image.rs +++ b/examples/shader/fallback_image.rs @@ -27,7 +27,7 @@ fn setup( mut materials: ResMut>, ) { commands.spawn(MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(FallbackTestMaterial { image_1d: None, image_2d: None, diff --git a/examples/shader/post_processing.rs b/examples/shader/post_processing.rs index ad684675d806b..f4df0ba0bf145 100644 --- a/examples/shader/post_processing.rs +++ b/examples/shader/post_processing.rs @@ -322,7 +322,7 @@ fn setup( // cube commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() diff --git a/examples/shader/shader_defs.rs b/examples/shader/shader_defs.rs index a638c3569a054..3c4ac6efc57d6 100644 --- a/examples/shader/shader_defs.rs +++ b/examples/shader/shader_defs.rs @@ -27,7 +27,7 @@ fn setup( ) { // blue cube commands.spawn(MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), transform: Transform::from_xyz(-1.0, 0.5, 0.0), material: materials.add(CustomMaterial { color: Color::BLUE, @@ -38,7 +38,7 @@ fn setup( // red cube (with green color overridden by the IS_RED "shader def") commands.spawn(MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), transform: Transform::from_xyz(1.0, 0.5, 0.0), material: materials.add(CustomMaterial { color: Color::GREEN, diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index a0b4c7db79c82..3c5d43904ffcd 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -35,7 +35,7 @@ fn main() { fn setup(mut commands: Commands, mut meshes: ResMut>) { commands.spawn(( - meshes.add(Mesh::from(shape::Cube { size: 0.5 })), + meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(0.5)))), SpatialBundle::INHERITED_IDENTITY, InstanceMaterialData( (1..=10) diff --git a/examples/shader/shader_material.rs b/examples/shader/shader_material.rs index eb20a77566fb1..73e9f9520c11e 100644 --- a/examples/shader/shader_material.rs +++ b/examples/shader/shader_material.rs @@ -22,7 +22,7 @@ fn setup( ) { // cube commands.spawn(MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), transform: Transform::from_xyz(0.0, 0.5, 0.0), material: materials.add(CustomMaterial { color: Color::BLUE, diff --git a/examples/shader/shader_material_2d.rs b/examples/shader/shader_material_2d.rs index 351b5c5c8bf72..7a8d86b9c95d3 100644 --- a/examples/shader/shader_material_2d.rs +++ b/examples/shader/shader_material_2d.rs @@ -29,7 +29,9 @@ fn setup( // quad commands.spawn(MaterialMesh2dBundle { - mesh: meshes.add(Mesh::from(shape::Quad::default())).into(), + mesh: meshes + .add(Mesh::from(primitives::Rectangle::default())) + .into(), transform: Transform::default().with_scale(Vec3::splat(128.)), material: materials.add(CustomMaterial { color: Color::BLUE, diff --git a/examples/shader/shader_material_glsl.rs b/examples/shader/shader_material_glsl.rs index f24cfa5070334..f394ad1864053 100644 --- a/examples/shader/shader_material_glsl.rs +++ b/examples/shader/shader_material_glsl.rs @@ -28,7 +28,7 @@ fn setup( ) { // cube commands.spawn(MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), transform: Transform::from_xyz(0.0, 0.5, 0.0), material: materials.add(CustomMaterial { color: Color::BLUE, diff --git a/examples/shader/shader_material_screenspace_texture.rs b/examples/shader/shader_material_screenspace_texture.rs index ddee9a68097e0..088db00781c43 100644 --- a/examples/shader/shader_material_screenspace_texture.rs +++ b/examples/shader/shader_material_screenspace_texture.rs @@ -25,7 +25,12 @@ fn setup( mut standard_materials: ResMut>, ) { commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(5.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(5.0)) + .into(), + ), material: standard_materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); @@ -35,7 +40,7 @@ fn setup( }); commands.spawn(MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), transform: Transform::from_xyz(0.0, 0.5, 0.0), material: custom_materials.add(CustomMaterial { texture: asset_server.load( diff --git a/examples/shader/shader_prepass.rs b/examples/shader/shader_prepass.rs index de17d4341fcfd..1d455a22d1131 100644 --- a/examples/shader/shader_prepass.rs +++ b/examples/shader/shader_prepass.rs @@ -61,7 +61,12 @@ fn setup( // plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(5.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(5.0)) + .into(), + ), material: std_materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); @@ -71,7 +76,7 @@ fn setup( // For a real application, this isn't ideal. commands.spawn(( MaterialMeshBundle { - mesh: meshes.add(shape::Quad::new(Vec2::new(20.0, 20.0)).into()), + mesh: meshes.add(primitives::Rectangle::new(20.0, 20.0).into()), material: depth_materials.add(PrepassOutputMaterial { settings: ShowPrepassSettings::default(), }), @@ -85,7 +90,7 @@ fn setup( // Opaque cube commands.spawn(( MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(CustomMaterial { color: Color::WHITE, color_texture: Some(asset_server.load("branding/icon.png")), @@ -99,7 +104,7 @@ fn setup( // Cube with alpha mask commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: std_materials.add(StandardMaterial { alpha_mode: AlphaMode::Mask(1.0), base_color_texture: Some(asset_server.load("branding/icon.png")), @@ -112,7 +117,7 @@ fn setup( // Cube with alpha blending. // Transparent materials are ignored by the prepass commands.spawn(MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(CustomMaterial { color: Color::WHITE, color_texture: Some(asset_server.load("branding/icon.png")), diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index 91c23479c6322..5345961cd4d2c 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -74,7 +74,7 @@ fn setup( // a cube with multiple textures commands.spawn(MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(BindlessMaterial { textures }), ..Default::default() }); diff --git a/examples/stress_tests/bevymark.rs b/examples/stress_tests/bevymark.rs index 1c727b28d5927..ae0433ba7936c 100644 --- a/examples/stress_tests/bevymark.rs +++ b/examples/stress_tests/bevymark.rs @@ -206,7 +206,7 @@ fn setup( textures, materials, quad: meshes - .add(Mesh::from(shape::Quad::new(Vec2::splat( + .add(Mesh::from(primitives::Rectangle::from_size(Vec2::splat( BIRD_TEXTURE_SIZE as f32, )))) .into(), diff --git a/examples/stress_tests/many_cubes.rs b/examples/stress_tests/many_cubes.rs index 6a1542a025fab..e62c0ab67b2b4 100644 --- a/examples/stress_tests/many_cubes.rs +++ b/examples/stress_tests/many_cubes.rs @@ -101,7 +101,7 @@ fn setup( let images = images.into_inner(); let material_assets = material_assets.into_inner(); - let mesh = meshes.add(Mesh::from(shape::Cube { size: 1.0 })); + let mesh = meshes.add(Mesh::from(primitives::Cuboid::default())); let material_textures = init_textures(args, images); let materials = init_materials(args, &material_textures, material_assets); diff --git a/examples/stress_tests/many_foxes.rs b/examples/stress_tests/many_foxes.rs index 896b601e4cd5b..c83fbe1b326fe 100644 --- a/examples/stress_tests/many_foxes.rs +++ b/examples/stress_tests/many_foxes.rs @@ -185,7 +185,12 @@ fn setup( // Plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(5000.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(5000.0)) + .into(), + ), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); diff --git a/examples/stress_tests/many_lights.rs b/examples/stress_tests/many_lights.rs index c36a04920c792..c55516bb54353 100644 --- a/examples/stress_tests/many_lights.rs +++ b/examples/stress_tests/many_lights.rs @@ -48,19 +48,13 @@ fn setup( const N_LIGHTS: usize = 100_000; commands.spawn(PbrBundle { - mesh: meshes.add( - Mesh::try_from(shape::Icosphere { - radius: RADIUS, - subdivisions: 9, - }) - .unwrap(), - ), + mesh: meshes.add(primitives::Sphere { radius: RADIUS }.mesh().ico(9).unwrap()), material: materials.add(StandardMaterial::from(Color::WHITE)), transform: Transform::from_scale(Vec3::NEG_ONE), ..default() }); - let mesh = meshes.add(Mesh::from(shape::Cube { size: 1.0 })); + let mesh = meshes.add(Mesh::from(primitives::Cuboid::default())); let material = materials.add(StandardMaterial { base_color: Color::PINK, ..default() diff --git a/examples/tools/gamepad_viewer.rs b/examples/tools/gamepad_viewer.rs index 29ef2c5af2386..3434402139b08 100644 --- a/examples/tools/gamepad_viewer.rs +++ b/examples/tools/gamepad_viewer.rs @@ -72,12 +72,23 @@ impl FromWorld for ButtonMeshes { fn from_world(world: &mut World) -> Self { let mut meshes = world.resource_mut::>(); Self { - circle: meshes.add(shape::Circle::new(BUTTON_RADIUS).into()).into(), + circle: meshes + .add( + primitives::Circle { + radius: BUTTON_RADIUS, + } + .into(), + ) + .into(), triangle: meshes - .add(shape::RegularPolygon::new(BUTTON_RADIUS, 3).into()) + .add(primitives::RegularPolygon::new(BUTTON_RADIUS, 3).into()) + .into(), + start_pause: meshes + .add(primitives::Rectangle::from_size(START_SIZE).into()) + .into(), + trigger: meshes + .add(primitives::Rectangle::from_size(TRIGGER_SIZE).into()) .into(), - start_pause: meshes.add(shape::Quad::new(START_SIZE).into()).into(), - trigger: meshes.add(shape::Quad::new(TRIGGER_SIZE).into()).into(), } } } diff --git a/examples/transforms/3d_rotation.rs b/examples/transforms/3d_rotation.rs index ddedbbae859e9..74aa9191b1956 100644 --- a/examples/transforms/3d_rotation.rs +++ b/examples/transforms/3d_rotation.rs @@ -26,7 +26,7 @@ fn setup( // Spawn a cube to rotate. commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::WHITE.into()), transform: Transform::from_translation(Vec3::ZERO), ..default() diff --git a/examples/transforms/scale.rs b/examples/transforms/scale.rs index bbae8b71b9be2..2ea79fdd1871e 100644 --- a/examples/transforms/scale.rs +++ b/examples/transforms/scale.rs @@ -42,7 +42,7 @@ fn setup( // Spawn a cube to scale. commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::WHITE.into()), transform: Transform::from_rotation(Quat::from_rotation_y(PI / 4.0)), ..default() diff --git a/examples/transforms/transform.rs b/examples/transforms/transform.rs index 38bad31c4fa30..3455ef5d68b31 100644 --- a/examples/transforms/transform.rs +++ b/examples/transforms/transform.rs @@ -46,13 +46,7 @@ fn setup( // Add an object (sphere) for visualizing scaling. commands.spawn(( PbrBundle { - mesh: meshes.add( - Mesh::try_from(shape::Icosphere { - radius: 3.0, - subdivisions: 32, - }) - .unwrap(), - ), + mesh: meshes.add(primitives::Sphere { radius: 3.0 }.mesh().ico(32).unwrap()), material: materials.add(Color::YELLOW.into()), transform: Transform::from_translation(Vec3::ZERO), ..default() @@ -73,7 +67,7 @@ fn setup( Transform::from_translation(Vec3::Z * -10.0).with_rotation(Quat::from_rotation_y(PI / 2.)); commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::WHITE.into()), transform: cube_spawn, ..default() diff --git a/examples/transforms/translation.rs b/examples/transforms/translation.rs index e8aaef4edfdd3..f5c9e5ceb2126 100644 --- a/examples/transforms/translation.rs +++ b/examples/transforms/translation.rs @@ -40,7 +40,7 @@ fn setup( let entity_spawn = Vec3::ZERO; commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::WHITE.into()), transform: Transform::from_translation(entity_spawn), ..default() diff --git a/examples/window/low_power.rs b/examples/window/low_power.rs index 8a1af9af3b2a4..68ccf8ff54436 100644 --- a/examples/window/low_power.rs +++ b/examples/window/low_power.rs @@ -151,7 +151,7 @@ pub(crate) mod test_setup { ) { commands.spawn(( PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 0.5 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::from_size(Vec3::splat(0.5)))), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), ..default() }, diff --git a/examples/window/screenshot.rs b/examples/window/screenshot.rs index 8ebfdaba5efec..c42a281928a02 100644 --- a/examples/window/screenshot.rs +++ b/examples/window/screenshot.rs @@ -35,13 +35,18 @@ fn setup( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(5.0).into()), + mesh: meshes.add( + primitives::Plane3d::default() + .mesh() + .size(Vec2::splat(5.0)) + .into(), + ), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); // cube commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() diff --git a/tests/window/minimising.rs b/tests/window/minimising.rs index 40314a2153a9f..41784e11d17b9 100644 --- a/tests/window/minimising.rs +++ b/tests/window/minimising.rs @@ -35,16 +35,15 @@ fn setup_3d( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { - size: 5.0, - subdivisions: 0, - })), + mesh: meshes.add(Mesh::from( + primitives::Plane3d::default().mesh().size(Vec2::splat(5.0)), + )), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); // cube commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() diff --git a/tests/window/resizing.rs b/tests/window/resizing.rs index 0d9135989c164..6af9b0533d9c0 100644 --- a/tests/window/resizing.rs +++ b/tests/window/resizing.rs @@ -118,16 +118,15 @@ fn setup_3d( ) { // plane commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { - size: 5.0, - subdivisions: 0, - })), + mesh: meshes.add(Mesh::from( + primitives::Plane3d::default().mesh().size(Vec2::splat(5.0)), + )), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); // cube commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + mesh: meshes.add(Mesh::from(primitives::Cuboid::default())), material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default()