Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add consuming builder methods for more ergonomic Mesh creation #10056

Merged
merged 9 commits into from
Oct 9, 2023
4 changes: 3 additions & 1 deletion crates/bevy_pbr/src/pbr_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
178 changes: 142 additions & 36 deletions crates/bevy_render/src/mesh/mesh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// ])))
/// }
/// ```
///
Expand Down Expand Up @@ -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.
Expand All @@ -147,27 +149,31 @@ 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.
pub const ATTRIBUTE_UV_1: MeshVertexAttribute =
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);

Expand Down Expand Up @@ -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<VertexAttributeValues>,
) -> Self {
self.insert_attribute(attribute, values);
self
}

/// Removes the data for a vertex attribute
pub fn remove_attribute(
&mut self,
Expand All @@ -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<MeshVertexAttributeId>) -> Self {
self.remove_attribute(attribute);
self
}

#[inline]
pub fn contains_attribute(&self, id: impl Into<MeshVertexAttributeId>) -> bool {
self.attributes.contains_key(&id.into())
Expand Down Expand Up @@ -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<Indices>) -> Self {
self.set_indices(indices);
self
}

/// Retrieves the vertex `indices` of the mesh.
#[inline]
pub fn indices(&self) -> Option<&Indices> {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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<Mesh, GenerateTangentsError> {
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
self.generate_tangents()?;
Ok(self)
}

/// Compute the Axis-Aligned Bounding Box of the mesh vertices in model space
pub fn compute_aabb(&self) -> Option<Aabb> {
let Some(VertexAttributeValues::Float32x3(values)) =
Expand All @@ -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<Image>) -> 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<String>) {
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<String>) -> 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()
Expand Down Expand Up @@ -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]]);
}
}
11 changes: 5 additions & 6 deletions crates/bevy_render/src/mesh/shape/capsule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,11 +364,10 @@ impl From<Capsule> 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)))
}
}
11 changes: 5 additions & 6 deletions crates/bevy_render/src/mesh/shape/cylinder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,10 @@ impl From<Cylinder> 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)
}
}
11 changes: 5 additions & 6 deletions crates/bevy_render/src/mesh/shape/icosphere.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,10 @@ impl TryFrom<Icosphere> 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))
}
}
Loading