From 8e1d247601a258231c79582240c244e4f4d69cda Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Mon, 9 Oct 2023 16:47:41 -0300 Subject: [PATCH] Add consuming builder methods for more ergonomic `Mesh` creation (#10056) # Objective - This PR aims to make creating meshes a little bit more ergonomic, specifically by removing the need for intermediate mutable variables. ## Solution - We add methods that consume the `Mesh` and return a mesh with the specified changes, so that meshes can be entirely constructed via builder-style calls, without intermediate variables; - Methods are flagged with `#[must_use]` to ensure proper use; - Examples are updated to use the new methods where applicable. Some examples are kept with the mutating methods so that users can still easily discover them, and also where the new methods wouldn't really be an improvement. ## Examples Before: ```rust let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vs); mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vns); mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vts); mesh.set_indices(Some(Indices::U32(tris))); mesh ``` After: ```rust Mesh::new(PrimitiveTopology::TriangleList) .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vs) .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vns) .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vts) .with_indices(Some(Indices::U32(tris))) ``` Before: ```rust let mut cube = Mesh::from(shape::Cube { size: 1.0 }); cube.generate_tangents().unwrap(); PbrBundle { mesh: meshes.add(cube), ..default() } ``` After: ```rust PbrBundle { mesh: meshes.add( Mesh::from(shape::Cube { size: 1.0 }) .with_generated_tangents() .unwrap(), ), ..default() } ``` --- ## Changelog - Added consuming builder methods for more ergonomic `Mesh` creation: `with_inserted_attribute()`, `with_removed_attribute()`, `with_indices()`, `with_duplicated_vertices()`, `with_computed_flat_normals()`, `with_generated_tangents()`, `with_morph_targets()`, `with_morph_target_names()`. --- crates/bevy_pbr/src/pbr_material.rs | 4 +- crates/bevy_render/src/mesh/mesh/mod.rs | 178 ++++++++++++++---- crates/bevy_render/src/mesh/shape/capsule.rs | 11 +- crates/bevy_render/src/mesh/shape/cylinder.rs | 11 +- .../bevy_render/src/mesh/shape/icosphere.rs | 11 +- crates/bevy_render/src/mesh/shape/mod.rs | 33 ++-- .../src/mesh/shape/regular_polygon.rs | 11 +- crates/bevy_render/src/mesh/shape/torus.rs | 11 +- crates/bevy_render/src/mesh/shape/uvsphere.rs | 11 +- examples/3d/generate_custom_mesh.rs | 33 ++-- examples/3d/lines.rs | 17 +- examples/3d/parallax_mapping.rs | 22 ++- examples/animation/custom_skinned_mesh.rs | 124 ++++++------ examples/shader/custom_vertex_attribute.rs | 13 +- 14 files changed, 291 insertions(+), 199 deletions(-) diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index 47c861c1557898..f66fc80e3f249a 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -152,11 +152,13 @@ pub struct StandardMaterial { /// - Vertex normals /// /// Tangents do not have to be stored in your model, - /// they can be generated using the [`Mesh::generate_tangents`] method. + /// they can be generated using the [`Mesh::generate_tangents`] or + /// [`Mesh::with_generated_tangents`] methods. /// If your material has a normal map, but still renders as a flat surface, /// make sure your meshes have their tangents set. /// /// [`Mesh::generate_tangents`]: bevy_render::mesh::Mesh::generate_tangents + /// [`Mesh::with_generated_tangents`]: bevy_render::mesh::Mesh::with_generated_tangents #[texture(9)] #[sampler(10)] #[dependency] diff --git a/crates/bevy_render/src/mesh/mesh/mod.rs b/crates/bevy_render/src/mesh/mesh/mod.rs index f40318e15d27b7..7f92c3bcd19f8c 100644 --- a/crates/bevy_render/src/mesh/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mesh/mod.rs @@ -49,32 +49,32 @@ pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; /// # use bevy_render::mesh::{Mesh, Indices}; /// # use bevy_render::render_resource::PrimitiveTopology; /// fn create_simple_parallelogram() -> Mesh { -/// // Create a new mesh, add 4 vertices, each with its own position attribute (coordinate in -/// // 3D space), for each of the corners of the parallelogram. -/// let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); -/// mesh.insert_attribute( -/// Mesh::ATTRIBUTE_POSITION, -/// vec![[0.0, 0.0, 0.0], [1.0, 2.0, 0.0], [2.0, 2.0, 0.0], [1.0, 0.0, 0.0]] -/// ); -/// // Assign a UV coordinate to each vertex. -/// mesh.insert_attribute( -/// Mesh::ATTRIBUTE_UV_0, -/// vec![[0.0, 1.0], [0.5, 0.0], [1.0, 0.0], [0.5, 1.0]] -/// ); -/// // Assign normals (everything points outwards) -/// mesh.insert_attribute( -/// Mesh::ATTRIBUTE_NORMAL, -/// vec![[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]] -/// ); -/// // After defining all the vertices and their attributes, build each triangle using the -/// // indices of the vertices that make it up in a counter-clockwise order. -/// mesh.set_indices(Some(Indices::U32(vec![ -/// // First triangle -/// 0, 3, 1, -/// // Second triangle -/// 1, 3, 2 -/// ]))); -/// mesh +/// // Create a new mesh using a triangle list topology, where each set of 3 vertices composes a triangle. +/// Mesh::new(PrimitiveTopology::TriangleList) +/// // Add 4 vertices, each with its own position attribute (coordinate in +/// // 3D space), for each of the corners of the parallelogram. +/// .with_inserted_attribute( +/// Mesh::ATTRIBUTE_POSITION, +/// vec![[0.0, 0.0, 0.0], [1.0, 2.0, 0.0], [2.0, 2.0, 0.0], [1.0, 0.0, 0.0]] +/// ) +/// // Assign a UV coordinate to each vertex. +/// .with_inserted_attribute( +/// Mesh::ATTRIBUTE_UV_0, +/// vec![[0.0, 1.0], [0.5, 0.0], [1.0, 0.0], [0.5, 1.0]] +/// ) +/// // Assign normals (everything points outwards) +/// .with_inserted_attribute( +/// Mesh::ATTRIBUTE_NORMAL, +/// vec![[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]] +/// ) +/// // After defining all the vertices and their attributes, build each triangle using the +/// // indices of the vertices that make it up in a counter-clockwise order. +/// .with_indices(Some(Indices::U32(vec![ +/// // First triangle +/// 0, 3, 1, +/// // Second triangle +/// 1, 3, 2 +/// ]))) /// } /// ``` /// @@ -126,16 +126,18 @@ pub struct Mesh { } impl Mesh { - /// Where the vertex is located in space. Use in conjunction with [`Mesh::insert_attribute`]. + /// Where the vertex is located in space. Use in conjunction with [`Mesh::insert_attribute`] + /// or [`Mesh::with_inserted_attribute`]. pub const ATTRIBUTE_POSITION: MeshVertexAttribute = MeshVertexAttribute::new("Vertex_Position", 0, VertexFormat::Float32x3); /// The direction the vertex normal is facing in. - /// Use in conjunction with [`Mesh::insert_attribute`]. + /// Use in conjunction with [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`]. pub const ATTRIBUTE_NORMAL: MeshVertexAttribute = MeshVertexAttribute::new("Vertex_Normal", 1, VertexFormat::Float32x3); - /// Texture coordinates for the vertex. Use in conjunction with [`Mesh::insert_attribute`]. + /// Texture coordinates for the vertex. Use in conjunction with [`Mesh::insert_attribute`] + /// or [`Mesh::with_inserted_attribute`]. /// /// Values are generally between 0. and 1., with `StandardMaterial` and `ColorMaterial` /// `[0.,0.]` is the top left of the texture, and [1.,1.] the bottom-right. @@ -147,7 +149,7 @@ impl Mesh { MeshVertexAttribute::new("Vertex_Uv", 2, VertexFormat::Float32x2); /// Alternate texture coordinates for the vertex. Use in conjunction with - /// [`Mesh::insert_attribute`]. + /// [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`]. /// /// Typically, these are used for lightmaps, textures that provide /// precomputed illumination. @@ -155,19 +157,23 @@ impl Mesh { MeshVertexAttribute::new("Vertex_Uv_1", 3, VertexFormat::Float32x2); /// The direction of the vertex tangent. Used for normal mapping. - /// Usually generated with [`generate_tangents`](Mesh::generate_tangents). + /// Usually generated with [`generate_tangents`](Mesh::generate_tangents) or + /// [`with_generated_tangents`](Mesh::with_generated_tangents). pub const ATTRIBUTE_TANGENT: MeshVertexAttribute = MeshVertexAttribute::new("Vertex_Tangent", 4, VertexFormat::Float32x4); - /// Per vertex coloring. Use in conjunction with [`Mesh::insert_attribute`]. + /// Per vertex coloring. Use in conjunction with [`Mesh::insert_attribute`] + /// or [`Mesh::with_inserted_attribute`]. pub const ATTRIBUTE_COLOR: MeshVertexAttribute = MeshVertexAttribute::new("Vertex_Color", 5, VertexFormat::Float32x4); - /// Per vertex joint transform matrix weight. Use in conjunction with [`Mesh::insert_attribute`]. + /// Per vertex joint transform matrix weight. Use in conjunction with [`Mesh::insert_attribute`] + /// or [`Mesh::with_inserted_attribute`]. pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute = MeshVertexAttribute::new("Vertex_JointWeight", 6, VertexFormat::Float32x4); - /// Per vertex joint transform matrix index. Use in conjunction with [`Mesh::insert_attribute`]. + /// Per vertex joint transform matrix index. Use in conjunction with [`Mesh::insert_attribute`] + /// or [`Mesh::with_inserted_attribute`]. pub const ATTRIBUTE_JOINT_INDEX: MeshVertexAttribute = MeshVertexAttribute::new("Vertex_JointIndex", 7, VertexFormat::Uint16x4); @@ -224,6 +230,24 @@ impl Mesh { .insert(attribute.id, MeshAttributeData { attribute, values }); } + /// Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal etc.). + /// The name will often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`]. + /// + /// (Alternatively, you can use [`Mesh::insert_attribute`] to mutate an existing mesh in-place) + /// + /// # Panics + /// Panics when the format of the values does not match the attribute's format. + #[must_use] + #[inline] + pub fn with_inserted_attribute( + mut self, + attribute: MeshVertexAttribute, + values: impl Into, + ) -> Self { + self.insert_attribute(attribute, values); + self + } + /// Removes the data for a vertex attribute pub fn remove_attribute( &mut self, @@ -234,6 +258,15 @@ impl Mesh { .map(|data| data.values) } + /// Consumes the mesh and returns a mesh without the data for a vertex attribute + /// + /// (Alternatively, you can use [`Mesh::remove_attribute`] to mutate an existing mesh in-place) + #[must_use] + pub fn with_removed_attribute(mut self, attribute: impl Into) -> Self { + self.remove_attribute(attribute); + self + } + #[inline] pub fn contains_attribute(&self, id: impl Into) -> bool { self.attributes.contains_key(&id.into()) @@ -283,6 +316,18 @@ impl Mesh { self.indices = indices; } + /// Consumes the mesh and returns a mesh with the given vertex indices. They describe how triangles + /// are constructed out of the vertex attributes and are therefore only useful for the + /// [`PrimitiveTopology`] variants that use triangles. + /// + /// (Alternatively, you can use [`Mesh::set_indices`] to mutate an existing mesh in-place) + #[must_use] + #[inline] + pub fn with_indices(mut self, indices: Option) -> Self { + self.set_indices(indices); + self + } + /// Retrieves the vertex `indices` of the mesh. #[inline] pub fn indices(&self) -> Option<&Indices> { @@ -436,6 +481,18 @@ impl Mesh { } } + /// Consumes the mesh and returns a mesh with no shared vertices. + /// + /// This can dramatically increase the vertex count, so make sure this is what you want. + /// Does nothing if no [Indices] are set. + /// + /// (Alternatively, you can use [`Mesh::duplicate_vertices`] to mutate an existing mesh in-place) + #[must_use] + pub fn with_duplicated_vertices(mut self) -> Self { + self.duplicate_vertices(); + self + } + /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh. /// /// # Panics @@ -465,6 +522,20 @@ impl Mesh { self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); } + /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`]. + /// + /// (Alternatively, you can use [`Mesh::compute_flat_normals`] to mutate an existing mesh in-place) + /// + /// # Panics + /// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3` or + /// if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. + /// Consider calling [`Mesh::with_duplicated_vertices`] or export your mesh with normal attributes. + #[must_use] + pub fn with_computed_flat_normals(mut self) -> Self { + self.compute_flat_normals(); + self + } + /// Generate tangents for the mesh using the `mikktspace` algorithm. /// /// Sets the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful. @@ -475,6 +546,18 @@ impl Mesh { Ok(()) } + /// Consumes the mesh and returns a mesh with tangents generated using the `mikktspace` algorithm. + /// + /// The resulting mesh will have the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful. + /// + /// (Alternatively, you can use [`Mesh::generate_tangents`] to mutate an existing mesh in-place) + /// + /// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set. + pub fn with_generated_tangents(mut self) -> Result { + self.generate_tangents()?; + Ok(self) + } + /// Compute the Axis-Aligned Bounding Box of the mesh vertices in model space pub fn compute_aabb(&self) -> Option { let Some(VertexAttributeValues::Float32x3(values)) = @@ -498,11 +581,34 @@ impl Mesh { self.morph_targets = Some(morph_targets); } + /// Consumes the mesh and returns a mesh with the given [morph targets]. + /// + /// This requires a "morph target image". See [`MorphTargetImage`](crate::mesh::morph::MorphTargetImage) for info. + /// + /// (Alternatively, you can use [`Mesh::set_morph_targets`] to mutate an existing mesh in-place) + /// + /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation + #[must_use] + pub fn with_morph_targets(mut self, morph_targets: Handle) -> Self { + self.set_morph_targets(morph_targets); + self + } + /// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`. pub fn set_morph_target_names(&mut self, names: Vec) { self.morph_target_names = Some(names); } + /// Consumes the mesh and returns a mesh with morph target names. + /// Names should correspond to the order of the morph targets in `set_morph_targets`. + /// + /// (Alternatively, you can use [`Mesh::set_morph_target_names`] to mutate an existing mesh in-place) + #[must_use] + pub fn with_morph_target_names(mut self, names: Vec) -> Self { + self.set_morph_target_names(names); + self + } + /// Gets a list of all morph target names, if they exist. pub fn morph_target_names(&self) -> Option<&[String]> { self.morph_target_names.as_deref() @@ -1123,7 +1229,7 @@ mod tests { #[test] #[should_panic] fn panic_invalid_format() { - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0, 0.0]]); + let _mesh = Mesh::new(PrimitiveTopology::TriangleList) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0, 0.0]]); } } diff --git a/crates/bevy_render/src/mesh/shape/capsule.rs b/crates/bevy_render/src/mesh/shape/capsule.rs index 327471bb5a5102..d438ed28bc3eeb 100644 --- a/crates/bevy_render/src/mesh/shape/capsule.rs +++ b/crates/bevy_render/src/mesh/shape/capsule.rs @@ -364,11 +364,10 @@ impl From for Mesh { assert_eq!(vs.len(), vert_len); assert_eq!(tris.len(), fs_len); - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vs); - mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vns); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vts); - mesh.set_indices(Some(Indices::U32(tris))); - mesh + Mesh::new(PrimitiveTopology::TriangleList) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vs) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vns) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vts) + .with_indices(Some(Indices::U32(tris))) } } diff --git a/crates/bevy_render/src/mesh/shape/cylinder.rs b/crates/bevy_render/src/mesh/shape/cylinder.rs index cac101caa19943..a4d517ac739523 100644 --- a/crates/bevy_render/src/mesh/shape/cylinder.rs +++ b/crates/bevy_render/src/mesh/shape/cylinder.rs @@ -118,11 +118,10 @@ impl From for Mesh { build_cap(true); build_cap(false); - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(Indices::U32(indices))); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh + 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) } } diff --git a/crates/bevy_render/src/mesh/shape/icosphere.rs b/crates/bevy_render/src/mesh/shape/icosphere.rs index 852ae9eb126322..457ea0f82661dc 100644 --- a/crates/bevy_render/src/mesh/shape/icosphere.rs +++ b/crates/bevy_render/src/mesh/shape/icosphere.rs @@ -103,11 +103,10 @@ impl TryFrom for Mesh { let indices = Indices::U32(indices); - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(indices)); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, points); - mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - Ok(mesh) + 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 85a093d2774338..c9f6b9e1492bf9 100644 --- a/crates/bevy_render/src/mesh/shape/mod.rs +++ b/crates/bevy_render/src/mesh/shape/mod.rs @@ -120,12 +120,11 @@ impl From for Mesh { 20, 21, 22, 22, 23, 20, // bottom ]); - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh.set_indices(Some(indices)); - mesh + 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)) } } @@ -173,12 +172,11 @@ impl From for Mesh { let normals: Vec<_> = vertices.iter().map(|(_, n, _)| *n).collect(); let uvs: Vec<_> = vertices.iter().map(|(_, _, uv)| *uv).collect(); - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(indices)); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh + 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) } } @@ -255,12 +253,11 @@ impl From for Mesh { } } - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(Indices::U32(indices))); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh + 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) } } diff --git a/crates/bevy_render/src/mesh/shape/regular_polygon.rs b/crates/bevy_render/src/mesh/shape/regular_polygon.rs index b37d8ad633d3cc..c2e86368b15f3c 100644 --- a/crates/bevy_render/src/mesh/shape/regular_polygon.rs +++ b/crates/bevy_render/src/mesh/shape/regular_polygon.rs @@ -53,12 +53,11 @@ impl From for Mesh { indices.extend_from_slice(&[0, i + 1, i]); } - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh.set_indices(Some(Indices::U32(indices))); - mesh + 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))) } } diff --git a/crates/bevy_render/src/mesh/shape/torus.rs b/crates/bevy_render/src/mesh/shape/torus.rs index f22b3c7571114c..5254fcceebc013 100644 --- a/crates/bevy_render/src/mesh/shape/torus.rs +++ b/crates/bevy_render/src/mesh/shape/torus.rs @@ -84,11 +84,10 @@ impl From for Mesh { } } - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(Indices::U32(indices))); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh + 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) } } diff --git a/crates/bevy_render/src/mesh/shape/uvsphere.rs b/crates/bevy_render/src/mesh/shape/uvsphere.rs index ccce754e564f2c..b6b89ebc401576 100644 --- a/crates/bevy_render/src/mesh/shape/uvsphere.rs +++ b/crates/bevy_render/src/mesh/shape/uvsphere.rs @@ -80,11 +80,10 @@ impl From for Mesh { } } - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(Indices::U32(indices))); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertices); - mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh + 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/examples/3d/generate_custom_mesh.rs b/examples/3d/generate_custom_mesh.rs index a4aa0250280776..a5be376f0719e9 100644 --- a/examples/3d/generate_custom_mesh.rs +++ b/examples/3d/generate_custom_mesh.rs @@ -118,11 +118,10 @@ fn input_handler( } } +#[rustfmt::skip] fn create_cube_mesh() -> Mesh { - let mut cube_mesh = Mesh::new(PrimitiveTopology::TriangleList); - - #[rustfmt::skip] - cube_mesh.insert_attribute( + Mesh::new(PrimitiveTopology::TriangleList) + .with_inserted_attribute( Mesh::ATTRIBUTE_POSITION, // Each array is an [x, y, z] coordinate in local space. // Meshes always rotate around their local [0, 0, 0] when a rotation is applied to their Transform. @@ -159,14 +158,12 @@ fn create_cube_mesh() -> Mesh { [0.5, 0.5, -0.5], [0.5, -0.5, -0.5], ], - ); - + ) // Set-up UV coordinated to point to the upper (V < 0.5), "dirt+grass" part of the texture. // Take a look at the custom image (assets/textures/array_texture.png) // so the UV coords will make more sense // Note: (0.0, 0.0) = Top-Left in UV mapping, (1.0, 1.0) = Bottom-Right in UV mapping - #[rustfmt::skip] - cube_mesh.insert_attribute( + .with_inserted_attribute( Mesh::ATTRIBUTE_UV_0, vec![ // Assigning the UV coords for the top side. @@ -175,21 +172,19 @@ fn create_cube_mesh() -> Mesh { [0.0, 0.45], [0.0, 0.25], [1.0, 0.25], [1.0, 0.45], // Assigning the UV coords for the right side. [1.0, 0.45], [0.0, 0.45], [0.0, 0.2], [1.0, 0.2], - // Assigning the UV coords for the left side. + // Assigning the UV coords for the left side. [1.0, 0.45], [0.0, 0.45], [0.0, 0.2], [1.0, 0.2], // Assigning the UV coords for the back side. [0.0, 0.45], [0.0, 0.2], [1.0, 0.2], [1.0, 0.45], // Assigning the UV coords for the forward side. [0.0, 0.45], [0.0, 0.2], [1.0, 0.2], [1.0, 0.45], ], - ); - + ) // For meshes with flat shading, normals are orthogonal (pointing out) from the direction of // the surface. // Normals are required for correct lighting calculations. // Each array represents a normalized vector, which length should be equal to 1.0. - #[rustfmt::skip] - cube_mesh.insert_attribute( + .with_inserted_attribute( Mesh::ATTRIBUTE_NORMAL, vec![ // Normals for the top side (towards +y) @@ -223,8 +218,7 @@ fn create_cube_mesh() -> Mesh { [0.0, 0.0, -1.0], [0.0, 0.0, -1.0], ], - ); - + ) // Create the triangles out of the 24 vertices we created. // To construct a square, we need 2 triangles, therefore 12 triangles in total. // To construct a triangle, we need the indices of its 3 defined vertices, adding them one @@ -232,17 +226,14 @@ fn create_cube_mesh() -> Mesh { // should appear counter-clockwise from the front of the triangle, in this case from outside the cube). // Read more about how to correctly build a mesh manually in the Bevy documentation of a Mesh, // further examples and the implementation of the built-in shapes. - #[rustfmt::skip] - cube_mesh.set_indices(Some(Indices::U32(vec![ + .with_indices(Some(Indices::U32(vec![ 0,3,1 , 1,3,2, // triangles making up the top (+y) facing side. - 4,5,7 , 5,6,7, // bottom (-y) + 4,5,7 , 5,6,7, // bottom (-y) 8,11,9 , 9,11,10, // right (+x) 12,13,15 , 13,14,15, // left (-x) 16,19,17 , 17,19,18, // back (+z) 20,21,23 , 21,22,23, // forward (-z) - ]))); - - cube_mesh + ]))) } // Function that changes the UV mapping of the mesh, to apply the other texture. diff --git a/examples/3d/lines.rs b/examples/3d/lines.rs index 9dc3caba87b828..4a71bd78ae932f 100644 --- a/examples/3d/lines.rs +++ b/examples/3d/lines.rs @@ -92,13 +92,13 @@ pub struct LineList { impl From for Mesh { fn from(line: LineList) -> Self { + let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect(); + // This tells wgpu that the positions are list of lines // where every pair is a start and end point - let mut mesh = Mesh::new(PrimitiveTopology::LineList); - - let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect(); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertices); - mesh + Mesh::new(PrimitiveTopology::LineList) + // Add the vertices positions as an attribute + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices) } } @@ -112,9 +112,8 @@ impl From for Mesh { fn from(line: LineStrip) -> Self { // This tells wgpu that the positions are a list of points // where a line will be drawn between each consecutive point - let mut mesh = Mesh::new(PrimitiveTopology::LineStrip); - - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, line.points); - mesh + Mesh::new(PrimitiveTopology::LineStrip) + // Add the point positions as an attribute + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, line.points) } } diff --git a/examples/3d/parallax_mapping.rs b/examples/3d/parallax_mapping.rs index f30ae3c42e4113..2061d5b65041b0 100644 --- a/examples/3d/parallax_mapping.rs +++ b/examples/3d/parallax_mapping.rs @@ -264,12 +264,6 @@ fn setup( ..default() }); - let mut cube: Mesh = shape::Cube { size: 1.0 }.into(); - - // NOTE: for normal maps and depth maps to work, the mesh - // needs tangents generated. - cube.generate_tangents().unwrap(); - let parallax_depth_scale = TargetDepth::default().0; let max_parallax_layer_count = TargetLayers::default().0.exp2(); let parallax_mapping_method = CurrentMethod::default(); @@ -287,16 +281,24 @@ fn setup( }); commands.spawn(( PbrBundle { - mesh: meshes.add(cube), + mesh: meshes.add( + // NOTE: for normal maps and depth maps to work, the mesh + // needs tangents generated. + Mesh::from(shape::Cube { size: 1.0 }) + .with_generated_tangents() + .unwrap(), + ), material: parallax_material.clone_weak(), ..default() }, Spin { speed: 0.3 }, )); - let mut background_cube: Mesh = shape::Cube { size: 40.0 }.into(); - background_cube.generate_tangents().unwrap(); - let background_cube = meshes.add(background_cube); + let background_cube = meshes.add( + Mesh::from(shape::Cube { size: 40.0 }) + .with_generated_tangents() + .unwrap(), + ); let background_cube_bundle = |translation| { ( diff --git a/examples/animation/custom_skinned_mesh.rs b/examples/animation/custom_skinned_mesh.rs index 62918473dfb9ef..7f349e28461000 100644 --- a/examples/animation/custom_skinned_mesh.rs +++ b/examples/animation/custom_skinned_mesh.rs @@ -52,68 +52,68 @@ fn setup( ])); // Create a mesh - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - // Set mesh vertex positions - mesh.insert_attribute( - Mesh::ATTRIBUTE_POSITION, - vec![ - [0.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - [0.0, 0.5, 0.0], - [1.0, 0.5, 0.0], - [0.0, 1.0, 0.0], - [1.0, 1.0, 0.0], - [0.0, 1.5, 0.0], - [1.0, 1.5, 0.0], - [0.0, 2.0, 0.0], - [1.0, 2.0, 0.0], - ], - ); - // Set mesh vertex normals - mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10]); - // Set mesh vertex joint indices for mesh skinning. - // Each vertex gets 4 indices used to address the `JointTransforms` array in the vertex shader - // as well as `SkinnedMeshJoint` array in the `SkinnedMesh` component. - // This means that a maximum of 4 joints can affect a single vertex. - mesh.insert_attribute( - Mesh::ATTRIBUTE_JOINT_INDEX, - // Need to be explicit here as [u16; 4] could be either Uint16x4 or Unorm16x4. - VertexAttributeValues::Uint16x4(vec![ - [0, 0, 0, 0], - [0, 0, 0, 0], - [0, 1, 0, 0], - [0, 1, 0, 0], - [0, 1, 0, 0], - [0, 1, 0, 0], - [0, 1, 0, 0], - [0, 1, 0, 0], - [0, 1, 0, 0], - [0, 1, 0, 0], - ]), - ); - // Set mesh vertex joint weights for mesh skinning. - // Each vertex gets 4 joint weights corresponding to the 4 joint indices assigned to it. - // The sum of these weights should equal to 1. - mesh.insert_attribute( - Mesh::ATTRIBUTE_JOINT_WEIGHT, - vec![ - [1.00, 0.00, 0.0, 0.0], - [1.00, 0.00, 0.0, 0.0], - [0.75, 0.25, 0.0, 0.0], - [0.75, 0.25, 0.0, 0.0], - [0.50, 0.50, 0.0, 0.0], - [0.50, 0.50, 0.0, 0.0], - [0.25, 0.75, 0.0, 0.0], - [0.25, 0.75, 0.0, 0.0], - [0.00, 1.00, 0.0, 0.0], - [0.00, 1.00, 0.0, 0.0], - ], - ); - // Tell bevy to construct triangles from a list of vertex indices, - // where each 3 vertex indices form an triangle. - mesh.set_indices(Some(Indices::U16(vec![ - 0, 1, 3, 0, 3, 2, 2, 3, 5, 2, 5, 4, 4, 5, 7, 4, 7, 6, 6, 7, 9, 6, 9, 8, - ]))); + let mesh = Mesh::new(PrimitiveTopology::TriangleList) + // Set mesh vertex positions + .with_inserted_attribute( + Mesh::ATTRIBUTE_POSITION, + vec![ + [0.0, 0.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 0.5, 0.0], + [1.0, 0.5, 0.0], + [0.0, 1.0, 0.0], + [1.0, 1.0, 0.0], + [0.0, 1.5, 0.0], + [1.0, 1.5, 0.0], + [0.0, 2.0, 0.0], + [1.0, 2.0, 0.0], + ], + ) + // Set mesh vertex normals + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10]) + // Set mesh vertex joint indices for mesh skinning. + // Each vertex gets 4 indices used to address the `JointTransforms` array in the vertex shader + // as well as `SkinnedMeshJoint` array in the `SkinnedMesh` component. + // This means that a maximum of 4 joints can affect a single vertex. + .with_inserted_attribute( + Mesh::ATTRIBUTE_JOINT_INDEX, + // Need to be explicit here as [u16; 4] could be either Uint16x4 or Unorm16x4. + VertexAttributeValues::Uint16x4(vec![ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 1, 0, 0], + [0, 1, 0, 0], + [0, 1, 0, 0], + [0, 1, 0, 0], + [0, 1, 0, 0], + [0, 1, 0, 0], + [0, 1, 0, 0], + [0, 1, 0, 0], + ]), + ) + // Set mesh vertex joint weights for mesh skinning. + // Each vertex gets 4 joint weights corresponding to the 4 joint indices assigned to it. + // The sum of these weights should equal to 1. + .with_inserted_attribute( + Mesh::ATTRIBUTE_JOINT_WEIGHT, + vec![ + [1.00, 0.00, 0.0, 0.0], + [1.00, 0.00, 0.0, 0.0], + [0.75, 0.25, 0.0, 0.0], + [0.75, 0.25, 0.0, 0.0], + [0.50, 0.50, 0.0, 0.0], + [0.50, 0.50, 0.0, 0.0], + [0.25, 0.75, 0.0, 0.0], + [0.25, 0.75, 0.0, 0.0], + [0.00, 1.00, 0.0, 0.0], + [0.00, 1.00, 0.0, 0.0], + ], + ) + // Tell bevy to construct triangles from a list of vertex indices, + // where each 3 vertex indices form an triangle. + .with_indices(Some(Indices::U16(vec![ + 0, 1, 3, 0, 3, 2, 2, 3, 5, 2, 5, 4, 4, 5, 7, 4, 7, 6, 6, 7, 9, 6, 9, 8, + ]))); let mesh = meshes.add(mesh); diff --git a/examples/shader/custom_vertex_attribute.rs b/examples/shader/custom_vertex_attribute.rs index c61c81b3b8ad05..0411af15a76348 100644 --- a/examples/shader/custom_vertex_attribute.rs +++ b/examples/shader/custom_vertex_attribute.rs @@ -31,12 +31,13 @@ fn setup( mut meshes: ResMut>, mut materials: ResMut>, ) { - let mut mesh = Mesh::from(shape::Cube { size: 1.0 }); - mesh.insert_attribute( - ATTRIBUTE_BLEND_COLOR, - // The cube mesh has 24 vertices (6 faces, 4 vertices per face), so we insert one BlendColor for each - vec![[1.0, 0.0, 0.0, 1.0]; 24], - ); + let mesh = Mesh::from(shape::Cube { size: 1.0 }) + // Sets the custom attribute + .with_inserted_attribute( + ATTRIBUTE_BLEND_COLOR, + // The cube mesh has 24 vertices (6 faces, 4 vertices per face), so we insert one BlendColor for each + vec![[1.0, 0.0, 0.0, 1.0]; 24], + ); // cube commands.spawn(MaterialMeshBundle {