From 0558f321ec4d7172d98cb934eb69e1a1e5b939b0 Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Sun, 30 Nov 2025 16:20:16 +0000 Subject: [PATCH 1/5] InfallibleMesh --- .../benches/bevy_render/compute_normals.rs | 4 +- crates/bevy_camera/src/primitives.rs | 3 +- crates/bevy_gltf/src/loader/mod.rs | 7 +- crates/bevy_mesh/src/mesh.rs | 1493 ++++++++--------- crates/bevy_mesh/src/mikktspace.rs | 8 +- crates/bevy_mesh/src/primitives/dim2.rs | 69 +- .../bevy_mesh/src/primitives/dim3/capsule.rs | 6 +- crates/bevy_mesh/src/primitives/dim3/cone.rs | 8 +- .../src/primitives/dim3/conical_frustum.rs | 6 +- .../bevy_mesh/src/primitives/dim3/cuboid.rs | 6 +- .../bevy_mesh/src/primitives/dim3/cylinder.rs | 6 +- crates/bevy_mesh/src/primitives/dim3/plane.rs | 6 +- .../src/primitives/dim3/polyline3d.rs | 6 +- .../src/primitives/dim3/segment3d.rs | 6 +- .../bevy_mesh/src/primitives/dim3/sphere.rs | 12 +- .../src/primitives/dim3/tetrahedron.rs | 6 +- crates/bevy_mesh/src/primitives/dim3/torus.rs | 6 +- .../src/primitives/dim3/triangle3d.rs | 6 +- crates/bevy_mesh/src/primitives/extrusion.rs | 14 +- crates/bevy_mesh/src/primitives/mod.rs | 15 +- crates/bevy_pbr/src/decal/forward.rs | 2 +- crates/bevy_pbr/src/meshlet/from_mesh.rs | 10 +- .../mesh_picking/ray_cast/intersections.rs | 15 +- crates/bevy_render/src/mesh/allocator.rs | 5 +- crates/bevy_render/src/mesh/mod.rs | 21 +- crates/bevy_solari/src/scene/blas.rs | 7 +- examples/2d/mesh2d_manual.rs | 4 +- examples/2d/mesh2d_vertex_color_texture.rs | 4 +- examples/3d/generate_custom_mesh.rs | 6 +- examples/3d/lines.rs | 8 +- examples/3d/motion_blur.rs | 3 +- examples/3d/solari.rs | 14 +- examples/3d/vertex_colors.rs | 7 +- examples/animation/custom_skinned_mesh.rs | 4 +- examples/animation/morph_targets.rs | 2 +- examples/asset/alter_mesh.rs | 2 +- examples/gltf/query_gltf_primitives.rs | 2 +- examples/math/custom_primitives.rs | 6 +- examples/math/sampling_primitives.rs | 3 +- .../custom_vertex_attribute.rs | 4 +- .../specialized_mesh_pipeline.rs | 4 +- .../tools/scene_viewer/morph_viewer_plugin.rs | 2 +- tests/3d/test_invalid_skinned_mesh.rs | 4 +- 43 files changed, 929 insertions(+), 903 deletions(-) diff --git a/benches/benches/bevy_render/compute_normals.rs b/benches/benches/bevy_render/compute_normals.rs index 74fcbfe7b11bd..0306038196e21 100644 --- a/benches/benches/bevy_render/compute_normals.rs +++ b/benches/benches/bevy_render/compute_normals.rs @@ -5,7 +5,7 @@ use rand::random; use std::time::{Duration, Instant}; use bevy_asset::RenderAssetUsages; -use bevy_mesh::{Indices, Mesh, PrimitiveTopology}; +use bevy_mesh::{Indices, InfallibleMesh, Mesh, PrimitiveTopology}; const GRID_SIZE: usize = 256; @@ -28,7 +28,7 @@ fn compute_normals(c: &mut Criterion) { .flat_map(|i| std::iter::repeat(i).zip(0..GRID_SIZE)) .map(|(i, j)| [i as f32, j as f32, random::()]) .collect::>(); - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::MAIN_WORLD, ) diff --git a/crates/bevy_camera/src/primitives.rs b/crates/bevy_camera/src/primitives.rs index 9d2a5203c6669..c3edf3f5f1a77 100644 --- a/crates/bevy_camera/src/primitives.rs +++ b/crates/bevy_camera/src/primitives.rs @@ -23,8 +23,7 @@ impl MeshAabb for Mesh { return Some(aabb.into()); } - let Ok(VertexAttributeValues::Float32x3(values)) = - self.try_attribute(Mesh::ATTRIBUTE_POSITION) + let Ok(VertexAttributeValues::Float32x3(values)) = self.attribute(Mesh::ATTRIBUTE_POSITION) else { return None; }; diff --git a/crates/bevy_gltf/src/loader/mod.rs b/crates/bevy_gltf/src/loader/mod.rs index d18324a3ba9b0..2667b7ca3b86c 100644 --- a/crates/bevy_gltf/src/loader/mod.rs +++ b/crates/bevy_gltf/src/loader/mod.rs @@ -31,7 +31,7 @@ use bevy_math::{Mat4, Vec3}; use bevy_mesh::{ morph::{MeshMorphWeights, MorphAttributes, MorphTargetImage, MorphWeights}, skinning::{SkinnedMesh, SkinnedMeshInverseBindposes}, - Indices, Mesh, Mesh3d, MeshVertexAttribute, PrimitiveTopology, + Indices, InfallibleMesh, Mesh, Mesh3d, MeshVertexAttribute, PrimitiveTopology, }; #[cfg(feature = "pbr_transmission_textures")] use bevy_pbr::UvChannel; @@ -727,7 +727,7 @@ impl GltfLoader { }; let primitive_topology = primitive_topology(primitive.mode())?; - let mut mesh = Mesh::new(primitive_topology, settings.load_meshes); + let mut mesh = InfallibleMesh::new(primitive_topology, settings.load_meshes); // Read vertex attributes for (semantic, accessor) in primitive.attributes() { @@ -837,7 +837,8 @@ impl GltfLoader { }); } - let mesh_handle = load_context.add_labeled_asset(primitive_label.to_string(), mesh); + let mesh_handle = + load_context.add_labeled_asset(primitive_label.to_string(), mesh.into()); primitives.push(super::GltfPrimitive::new( &gltf_mesh, &primitive, diff --git a/crates/bevy_mesh/src/mesh.rs b/crates/bevy_mesh/src/mesh.rs index ca224d6e286eb..a1ce73ddeb943 100644 --- a/crates/bevy_mesh/src/mesh.rs +++ b/crates/bevy_mesh/src/mesh.rs @@ -133,6 +133,604 @@ impl From> for MeshExtractableData { } } +/// A struct which wraps a `Mesh` which is guaranteed to not be extracted to the `RenderWorld`, +/// that can be converted into a normal `Mesh` using `InfallibleMesh::into_mesh`. +/// Designed for use during cpu-side mesh construction. Methods on this struct whose `Mesh` +/// counterparts may return `MeshAccessError::ExtractedToRenderWorld` instead return plain values. +#[derive(Asset, Debug, Clone, Reflect, PartialEq)] +#[reflect(Clone)] +pub struct InfallibleMesh(Mesh); + +impl InfallibleMesh { + /// Construct a new mesh. You need to provide a [`PrimitiveTopology`] so that the + /// renderer knows how to treat the vertex data. Most of the time this will be + /// [`PrimitiveTopology::TriangleList`]. + pub fn new(primitive_topology: PrimitiveTopology, asset_usage: RenderAssetUsages) -> Self { + Self(Mesh::new(primitive_topology, asset_usage)) + } + + /// Returns the topology of the mesh. + pub fn primitive_topology(&self) -> PrimitiveTopology { + self.0.primitive_topology + } + + /// Sets the data for a vertex attribute (position, normal, etc.). The name will + /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`]. + /// + /// `Aabb` of entities with modified mesh are not updated automatically. + /// + /// Returns an error if the mesh data has been extracted to `RenderWorld`. + /// + /// # Panics + /// Panics when the format of the values does not match the attribute's format. + pub fn insert_attribute( + &mut self, + attribute: MeshVertexAttribute, + values: impl Into, + ) { + self.0.insert_attribute(attribute, values).unwrap(); + } + + /// 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 [`InfallibleMesh::insert_attribute`] to mutate an existing mesh in-place) + /// + /// `Aabb` of entities with modified mesh are not updated automatically. + /// + /// # 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, + attribute: impl Into, + ) -> Option { + match self.0.remove_attribute(attribute) { + Ok(attr) => Some(attr), + Err(MeshAccessError::NotFound) => None, + _ => unreachable!(), + } + } + + /// Consumes the mesh and returns a mesh without the data for a vertex attribute + /// + /// (Alternatively, you can use [`InfallibleMesh::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 + } + + /// Returns a bool indicating if the attribute is present in this mesh's vertex data. + #[inline] + pub fn contains_attribute(&self, id: impl Into) -> bool { + self.0 + .attributes + .as_ref() + .expect(MESH_EXTRACTED_ERROR) + .contains_key(&id.into()) + } + + /// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`]. + #[inline] + pub fn attribute( + &self, + id: impl Into, + ) -> Option<&VertexAttributeValues> { + self.0.attribute_option(id).expect(MESH_EXTRACTED_ERROR) + } + + /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably. + #[inline] + pub fn attribute_mut( + &mut self, + id: impl Into, + ) -> Option<&mut VertexAttributeValues> { + self.0.attribute_mut_option(id).expect(MESH_EXTRACTED_ERROR) + } + + /// Returns an iterator that yields references to the data of each vertex attribute. + pub fn attributes( + &self, + ) -> impl Iterator { + self.0.attributes().expect(MESH_EXTRACTED_ERROR) + } + + /// Returns an iterator that yields mutable references to the data of each vertex attribute. + pub fn attributes_mut( + &mut self, + ) -> impl Iterator { + self.0.attributes_mut().expect(MESH_EXTRACTED_ERROR) + } + + /// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the + /// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants + /// that use triangles. + #[inline] + pub fn insert_indices(&mut self, indices: Indices) { + self.0.insert_indices(indices).expect(MESH_EXTRACTED_ERROR); + } + + /// 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 [`InfallibleMesh::insert_indices`] to mutate an existing mesh in-place) + #[must_use] + #[inline] + pub fn with_inserted_indices(mut self, indices: Indices) -> Self { + self.insert_indices(indices); + self + } + + /// Retrieves the vertex `indices` of the mesh, returns None if not found. + #[inline] + pub fn indices(&self) -> Option<&Indices> { + self.0.indices.as_ref_option().expect(MESH_EXTRACTED_ERROR) + } + + /// Retrieves the vertex `indices` of the mesh mutably. + #[inline] + pub fn indices_mut(&mut self) -> Option<&mut Indices> { + self.0.indices_mut_option().expect(MESH_EXTRACTED_ERROR) + } + + /// Removes the vertex `indices` from the mesh and returns them. + #[inline] + pub fn remove_indices(&mut self) -> Option { + self.0.remove_indices().expect(MESH_EXTRACTED_ERROR) + } + + /// Consumes the mesh and returns a mesh without the vertex `indices` of the mesh. + /// + /// (Alternatively, you can use [`InfallibleMesh::remove_indices`] to mutate an existing mesh in-place) + #[must_use] + pub fn with_removed_indices(mut self) -> Self { + self.remove_indices(); + self + } + + pub fn duplicate_vertices(&mut self) { + self.0.duplicate_vertices().expect(MESH_EXTRACTED_ERROR); + } + + /// 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 [`InfallibleMesh::duplicate_vertices`] to mutate an existing mesh in-place) + #[must_use] + pub fn with_duplicated_vertices(self) -> Self { + Self( + self.0 + .with_duplicated_vertices() + .expect(MESH_EXTRACTED_ERROR), + ) + } + + /// Inverts the winding of the indices such that all counter-clockwise triangles are now + /// clockwise and vice versa. + /// For lines, their start and end indices are flipped. + /// + /// Does nothing if no [`Indices`] are set. + /// If this operation succeeded, an [`Ok`] result is returned. + pub fn invert_winding(&mut self) -> Result<(), MeshWindingInvertError> { + self.0.invert_winding() + } + + /// Consumes the mesh and returns a mesh with inverted winding of the indices such + /// that all counter-clockwise triangles are now clockwise and vice versa. + /// + /// Does nothing if no [`Indices`] are set. + pub fn with_inverted_winding(mut self) -> Result { + self.0.invert_winding().map(|_| self) + } + + /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh. + /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat + /// normals. + /// + /// # Panics + /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. + /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].= + pub fn compute_normals(&mut self) { + self.0.compute_normals().expect(MESH_EXTRACTED_ERROR); + } + + /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh. + /// + /// # Panics + /// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. + /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. + /// Consider calling [`InfallibleMesh::duplicate_vertices`] or exporting your mesh with normal + /// attributes. + /// FIXME: This should handle more cases since this is called as a part of gltf + /// mesh loading where we can't really blame users for loading meshes that might + /// not conform to the limitations here! + pub fn compute_flat_normals(&mut self) { + self.0.compute_flat_normals().expect(MESH_EXTRACTED_ERROR); + } + + /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared + /// vertices. + /// + /// This method weights normals by the angles of the corners of connected triangles, thus + /// eliminating triangle area and count as factors in the final normal. This does make it + /// somewhat slower than [`InfallibleMesh::compute_area_weighted_normals`] which does not need to + /// greedily normalize each triangle's normal or calculate corner angles. + /// + /// If you would rather have the computed normals be weighted by triangle area, see + /// [`InfallibleMesh::compute_area_weighted_normals`] instead. If you need to weight them in some other + /// way, see [`InfallibleMesh::compute_custom_smooth_normals`]. + /// + /// # Panics + /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. + /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. + /// Panics if the mesh does not have indices defined. + /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle + /// this as an error use [`InfallibleMesh::compute_smooth_normals`] + pub fn compute_smooth_normals(&mut self) { + self.0.compute_smooth_normals().expect(MESH_EXTRACTED_ERROR); + } + + /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared + /// vertices. + /// + /// This method weights normals by the area of each triangle containing the vertex. Thus, + /// larger triangles will skew the normals of their vertices towards their own normal more + /// than smaller triangles will. + /// + /// This method is actually somewhat faster than [`InfallibleMesh::compute_smooth_normals`] because an + /// intermediate result of triangle normal calculation is already scaled by the triangle's area. + /// + /// If you would rather have the computed normals be influenced only by the angles of connected + /// edges, see [`InfallibleMesh::compute_smooth_normals`] instead. If you need to weight them in some + /// other way, see [`InfallibleMesh::compute_custom_smooth_normals`]. + /// + /// # Panics + /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. + /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. + /// Panics if the mesh does not have indices defined. + pub fn compute_area_weighted_normals(&mut self) { + self.0 + .compute_area_weighted_normals() + .expect(MESH_EXTRACTED_ERROR); + } + + /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared + /// vertices. + /// + /// This method allows you to customize how normals are weighted via the `per_triangle` parameter, + /// which must be a function or closure that accepts 3 parameters: + /// - The indices of the three vertices of the triangle as a `[usize; 3]`. + /// - A reference to the values of the [`Mesh::ATTRIBUTE_POSITION`] of the mesh (`&[[f32; 3]]`). + /// - A mutable reference to the sums of all normals so far. + /// + /// See also the standard methods included in Bevy for calculating smooth normals: + /// - [`InfallibleMesh::compute_smooth_normals`] + /// - [`InfallibleMesh::compute_area_weighted_normals`] + /// + /// An example that would weight each connected triangle's normal equally, thus skewing normals + /// towards the planes divided into the most triangles: + /// ``` + /// # use bevy_asset::RenderAssetUsages; + /// # use bevy_mesh::{Mesh, PrimitiveTopology, Meshable, MeshBuilder}; + /// # use bevy_math::{Vec3, primitives::Cuboid}; + /// # let mut mesh = Cuboid::default().mesh().build(); + /// mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| { + /// let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c])); + /// for idx in [a, b, c] { + /// normals[idx] += normal; + /// } + /// }); + /// ``` + /// + /// # Panics + /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. + /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. + /// Panics if the mesh does not have indices defined. + // + // FIXME: This should handle more cases since this is called as a part of gltf + // mesh loading where we can't really blame users for loading meshes that might + // not conform to the limitations here! + // + // When fixed, also update "Panics" sections of + // - [Mesh::compute_smooth_normals] + // - [Mesh::with_computed_smooth_normals] + // - [Mesh::compute_area_weighted_normals] + // - [Mesh::with_computed_area_weighted_normals] + pub fn compute_custom_smooth_normals( + &mut self, + per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]), + ) { + self.0 + .compute_custom_smooth_normals(per_triangle) + .expect(MESH_EXTRACTED_ERROR); + } + + /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`]. + /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat + /// normals. + /// + /// (Alternatively, you can use [`InfallibleMesh::compute_normals`] to mutate an existing mesh in-place) + /// + /// # Panics + /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. + /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. + #[must_use] + pub fn with_computed_normals(mut self) -> Self { + self.compute_normals(); + self + } + + /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`]. + /// + /// (Alternatively, you can use [`InfallibleMesh::compute_flat_normals`] to mutate an existing mesh in-place) + /// + /// # Panics + /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. + /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. + /// Panics if the mesh has indices defined + pub fn with_computed_flat_normals(mut self) -> Self { + self.compute_flat_normals(); + self + } + + /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`]. + /// + /// (Alternatively, you can use [`InfallibleMesh::compute_smooth_normals`] to mutate an existing mesh in-place) + /// + /// This method weights normals by the angles of triangle corners connected to each vertex. If + /// you would rather have the computed normals be weighted by triangle area, see + /// [`InfallibleMesh::with_computed_area_weighted_normals`] instead. + /// + /// # Panics + /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. + /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. + /// Panics if the mesh does not have indices defined. + pub fn with_computed_smooth_normals(mut self) -> Self { + self.compute_smooth_normals(); + self + } + + /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`]. + /// + /// (Alternatively, you can use [`InfallibleMesh::compute_area_weighted_normals`] to mutate an existing mesh in-place) + /// + /// This method weights normals by the area of each triangle containing the vertex. Thus, + /// larger triangles will skew the normals of their vertices towards their own normal more + /// than smaller triangles will. If you would rather have the computed normals be influenced + /// only by the angles of connected edges, see [`InfallibleMesh::with_computed_smooth_normals`] instead. + /// + /// # Panics + /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. + /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. + /// Panics if the mesh does not have indices defined. + pub fn with_computed_area_weighted_normals(mut self) -> Self { + self.compute_area_weighted_normals(); + self + } + + /// Generate tangents for the mesh using the `mikktspace` algorithm. + /// + /// Sets the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful. + /// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set. + #[cfg(feature = "bevy_mikktspace")] + pub fn generate_tangents(&mut self) -> Result<(), super::GenerateTangentsError> { + let tangents = super::generate_tangents_for_mesh(&self.0)?; + self.insert_attribute(Mesh::ATTRIBUTE_TANGENT, tangents); + 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 [`InfallibleMesh::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. + #[cfg(feature = "bevy_mikktspace")] + pub fn with_generated_tangents(mut self) -> Result { + self.generate_tangents()?; + Ok(self) + } + + /// Merges the [`Mesh`] data of `other` with `self`. The attributes and indices of `other` will be appended to `self`. + /// + /// Note that attributes of `other` that don't exist on `self` will be ignored. + /// + /// `Aabb` of entities with modified mesh are not updated automatically. + /// + /// # Errors + /// + /// If any of the following conditions are not met, this function errors: + /// * All of the vertex attributes that have the same attribute id, must also + /// have the same attribute type. + /// For example two attributes with the same id, but where one is a + /// [`VertexAttributeValues::Float32`] and the other is a + /// [`VertexAttributeValues::Float32x3`], would be invalid. + /// * Both meshes must have the same primitive topology. + pub fn merge(&mut self, other: &Mesh) -> Result<(), MeshMergeError> { + self.0.merge(other) + } + + /// Transforms the vertex positions, normals, and tangents of the mesh by the given [`Transform`]. + /// + /// `Aabb` of entities with modified mesh are not updated automatically. + pub fn transformed_by(mut self, transform: Transform) -> Self { + self.transform_by(transform); + self + } + + /// Transforms the vertex positions, normals, and tangents of the mesh in place by the given [`Transform`]. + /// + /// `Aabb` of entities with modified mesh are not updated automatically. + pub fn transform_by(&mut self, transform: Transform) { + self.0.transform_by(transform).expect(MESH_EXTRACTED_ERROR); + } + + /// Translates the vertex positions of the mesh by the given [`Vec3`]. + /// + /// `Aabb` of entities with modified mesh are not updated automatically. + pub fn translated_by(mut self, translation: Vec3) -> Self { + self.translate_by(translation); + self + } + + /// Translates the vertex positions of the mesh in place by the given [`Vec3`]. + /// + /// `Aabb` of entities with modified mesh are not updated automatically. + pub fn translate_by(&mut self, translation: Vec3) { + self.0 + .translate_by(translation) + .expect(MESH_EXTRACTED_ERROR); + } + + /// Rotates the vertex positions, normals, and tangents of the mesh by the given [`Quat`]. + /// + /// `Aabb` of entities with modified mesh are not updated automatically. + pub fn rotated_by(mut self, rotation: Quat) -> Self { + self.rotate_by(rotation); + self + } + + /// Rotates the vertex positions, normals, and tangents of the mesh in place by the given [`Quat`]. + /// + /// `Aabb` of entities with modified mesh are not updated automatically. + pub fn rotate_by(&mut self, rotation: Quat) { + self.0.rotate_by(rotation).expect(MESH_EXTRACTED_ERROR); + } + + /// Scales the vertex positions, normals, and tangents of the mesh by the given [`Vec3`]. + /// + /// `Aabb` of entities with modified mesh are not updated automatically. + pub fn scaled_by(mut self, scale: Vec3) -> Self { + self.scale_by(scale); + self + } + + /// Scales the vertex positions, normals, and tangents of the mesh in place by the given [`Vec3`]. + /// + /// `Aabb` of entities with modified mesh are not updated automatically. + pub fn scale_by(&mut self, scale: Vec3) { + self.0.scale_by(scale).expect(MESH_EXTRACTED_ERROR); + } + + /// Normalize joint weights so they sum to 1. + pub fn normalize_joint_weights(&mut self) { + self.0 + .normalize_joint_weights() + .expect(MESH_EXTRACTED_ERROR); + } + + /// Get a list of this Mesh's [triangles] as an iterator if possible. + /// + /// Returns an error if any of the following conditions are met (see [`MeshTrianglesError`]): + /// * The Mesh's [primitive topology] is not `TriangleList` or `TriangleStrip`. + /// * The Mesh is missing position or index data. + /// * The Mesh's position data has the wrong format (not `Float32x3`). + /// + /// [primitive topology]: PrimitiveTopology + /// [triangles]: Triangle3d + pub fn triangles(&self) -> Result + '_, MeshTrianglesError> { + self.0.triangles() + } + + pub fn into_mesh(self) -> Mesh { + self.0 + } +} + +impl From for Mesh { + fn from(value: InfallibleMesh) -> Self { + value.into_mesh() + } +} + +#[cfg(feature = "morph")] +impl InfallibleMesh { + /// Whether this mesh has morph targets. + pub fn has_morph_targets(&self) -> bool { + self.0.has_morph_targets().expect(MESH_EXTRACTED_ERROR) + } + + /// Set [morph targets] image for this mesh. This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info. + /// + /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation + pub fn set_morph_targets(&mut self, morph_targets: Handle) { + self.0 + .set_morph_targets(morph_targets) + .expect(MESH_EXTRACTED_ERROR); + } + + /// Retrieve the morph targets for this mesh, or None if there are no morph targets. + pub fn morph_targets(&self) -> Option<&Handle> { + match self.0.morph_targets() { + Ok(targets) => Some(targets), + Err(MeshAccessError::NotFound) => None, + Err(MeshAccessError::ExtractedToRenderWorld) => panic!("{MESH_EXTRACTED_ERROR}"), + } + } + + /// Consumes the mesh and returns a mesh with the given [morph targets]. + /// + /// This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info. + /// + /// (Alternatively, you can use [`InfallibleMesh::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.0 + .set_morph_target_names(names) + .expect(MESH_EXTRACTED_ERROR); + } + + /// 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 [`InfallibleMesh::set_morph_target_names`] to mutate an existing mesh in-place) + #[must_use] + pub fn with_morph_target_names(self, names: Vec) -> Self { + Self( + self.0 + .with_morph_target_names(names) + .expect(MESH_EXTRACTED_ERROR), + ) + } + + /// Gets a list of all morph target names, if they exist. + pub fn morph_target_names(&self) -> Option<&[String]> { + self.0.morph_target_names().expect(MESH_EXTRACTED_ERROR) + } +} + +impl core::ops::Deref for InfallibleMesh { + type Target = Mesh; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + /// A 3D object made out of vertices representing triangles, lines, or points, /// with "attribute" values for each vertex. /// @@ -357,25 +955,6 @@ impl Mesh { self.primitive_topology } - /// Sets the data for a vertex attribute (position, normal, etc.). The name will - /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`]. - /// - /// `Aabb` of entities with modified mesh are not updated automatically. - /// - /// # Panics - /// Panics when the format of the values does not match the attribute's format. - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_insert_attribute`] - #[inline] - pub fn insert_attribute( - &mut self, - attribute: MeshVertexAttribute, - values: impl Into, - ) { - self.try_insert_attribute(attribute, values) - .expect(MESH_EXTRACTED_ERROR); - } - /// Sets the data for a vertex attribute (position, normal, etc.). The name will /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`]. /// @@ -386,7 +965,7 @@ impl Mesh { /// # Panics /// Panics when the format of the values does not match the attribute's format. #[inline] - pub fn try_insert_attribute( + pub fn insert_attribute( &mut self, attribute: MeshVertexAttribute, values: impl Into, @@ -406,28 +985,6 @@ impl Mesh { Ok(()) } - /// 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) - /// - /// `Aabb` of entities with modified mesh are not updated automatically. - /// - /// # Panics - /// Panics when the format of the values does not match the attribute's format. - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_with_inserted_attribute`] - #[must_use] - #[inline] - pub fn with_inserted_attribute( - mut self, - attribute: MeshVertexAttribute, - values: impl Into, - ) -> Self { - self.insert_attribute(attribute, values); - self - } - /// 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`]. /// @@ -437,35 +994,19 @@ impl Mesh { /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. #[inline] - pub fn try_with_inserted_attribute( + pub fn with_inserted_attribute( mut self, attribute: MeshVertexAttribute, values: impl Into, ) -> Result { - self.try_insert_attribute(attribute, values)?; + self.insert_attribute(attribute, values)?; Ok(self) } - /// Removes the data for a vertex attribute - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_remove_attribute`] - pub fn remove_attribute( - &mut self, - attribute: impl Into, - ) -> Option { - self.attributes - .as_mut() - .expect(MESH_EXTRACTED_ERROR) - .remove(&attribute.into()) - .map(|data| data.values) - } - /// Removes the data for a vertex attribute /// Returns an error if the mesh data has been extracted to `RenderWorld`or /// if the attribute does not exist. - pub fn try_remove_attribute( + pub fn remove_attribute( &mut self, attribute: impl Into, ) -> Result { @@ -477,88 +1018,48 @@ impl Mesh { .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) - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_with_removed_attribute`] - #[must_use] - pub fn with_removed_attribute(mut self, attribute: impl Into) -> Self { - self.remove_attribute(attribute); - self - } - /// 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) /// /// Returns an error if the mesh data has been extracted to `RenderWorld`or /// if the attribute does not exist. - pub fn try_with_removed_attribute( + pub fn with_removed_attribute( mut self, attribute: impl Into, ) -> Result { - self.try_remove_attribute(attribute)?; + self.remove_attribute(attribute)?; Ok(self) } - /// Returns a bool indicating if the attribute is present in this mesh's vertex data. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_contains_attribute`] - #[inline] - pub fn contains_attribute(&self, id: impl Into) -> bool { - self.attributes - .as_ref() - .expect(MESH_EXTRACTED_ERROR) - .contains_key(&id.into()) - } - /// Returns a bool indicating if the attribute is present in this mesh's vertex data. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. #[inline] - pub fn try_contains_attribute( + pub fn contains_attribute( &self, id: impl Into, ) -> Result { Ok(self.attributes.as_ref()?.contains_key(&id.into())) } - /// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`]. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_attribute`] or [`Mesh::try_attribute_option`] - #[inline] - pub fn attribute( - &self, - id: impl Into, - ) -> Option<&VertexAttributeValues> { - self.try_attribute_option(id).expect(MESH_EXTRACTED_ERROR) - } - /// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`]. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`or /// if the attribute does not exist. #[inline] - pub fn try_attribute( + pub fn attribute( &self, id: impl Into, ) -> Result<&VertexAttributeValues, MeshAccessError> { - self.try_attribute_option(id)? - .ok_or(MeshAccessError::NotFound) + self.attribute_option(id)?.ok_or(MeshAccessError::NotFound) } /// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`]. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. #[inline] - pub fn try_attribute_option( + pub fn attribute_option( &self, id: impl Into, ) -> Result, MeshAccessError> { @@ -571,37 +1072,23 @@ impl Mesh { /// Retrieves the full data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`]. #[inline] - pub(crate) fn try_attribute_data( + pub(crate) fn attribute_data( &self, id: impl Into, ) -> Result, MeshAccessError> { Ok(self.attributes.as_ref()?.get(&id.into())) } - /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_attribute_mut`] - #[inline] - pub fn attribute_mut( - &mut self, - id: impl Into, - ) -> Option<&mut VertexAttributeValues> { - self.try_attribute_mut_option(id) - .expect(MESH_EXTRACTED_ERROR) - } - /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`or /// if the attribute does not exist. #[inline] - pub fn try_attribute_mut( + pub fn attribute_mut( &mut self, id: impl Into, ) -> Result<&mut VertexAttributeValues, MeshAccessError> { - self.try_attribute_mut_option(id)? + self.attribute_mut_option(id)? .ok_or(MeshAccessError::NotFound) } @@ -609,7 +1096,7 @@ impl Mesh { /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. #[inline] - pub fn try_attribute_mut_option( + pub fn attribute_mut_option( &mut self, id: impl Into, ) -> Result, MeshAccessError> { @@ -620,20 +1107,9 @@ impl Mesh { .map(|data| &mut data.values)) } - /// Returns an iterator that yields references to the data of each vertex attribute. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_attributes`] - pub fn attributes( - &self, - ) -> impl Iterator { - self.try_attributes().expect(MESH_EXTRACTED_ERROR) - } - /// Returns an iterator that yields references to the data of each vertex attribute. /// Returns an error if data has been extracted to `RenderWorld` - pub fn try_attributes( + pub fn attributes( &self, ) -> Result, MeshAccessError> { @@ -644,21 +1120,10 @@ impl Mesh { .map(|data| (&data.attribute, &data.values))) } - /// Returns an iterator that yields mutable references to the data of each vertex attribute. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_attributes_mut`] - pub fn attributes_mut( - &mut self, - ) -> impl Iterator { - self.try_attributes_mut().expect(MESH_EXTRACTED_ERROR) - } - /// Returns an iterator that yields mutable references to the data of each vertex attribute. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. - pub fn try_attributes_mut( + pub fn attributes_mut( &mut self, ) -> Result< impl Iterator, @@ -671,27 +1136,13 @@ impl Mesh { .map(|data| (&data.attribute, &mut data.values))) } - /// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the - /// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants - /// that use triangles. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_insert_indices`] - #[inline] - pub fn insert_indices(&mut self, indices: Indices) { - self.indices - .replace(Some(indices)) - .expect(MESH_EXTRACTED_ERROR); - } - /// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the /// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants /// that use triangles. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. #[inline] - pub fn try_insert_indices(&mut self, indices: Indices) -> Result<(), MeshAccessError> { + pub fn insert_indices(&mut self, indices: Indices) -> Result<(), MeshAccessError> { self.indices.replace(Some(indices))?; Ok(()) } @@ -702,45 +1153,19 @@ impl Mesh { /// /// (Alternatively, you can use [`Mesh::insert_indices`] to mutate an existing mesh in-place) /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_with_inserted_indices`] - #[must_use] - #[inline] - pub fn with_inserted_indices(mut self, indices: Indices) -> Self { - self.insert_indices(indices); - self - } - - /// 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::try_insert_indices`] to mutate an existing mesh in-place) - /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. #[inline] - pub fn try_with_inserted_indices(mut self, indices: Indices) -> Result { - self.try_insert_indices(indices)?; + pub fn with_inserted_indices(mut self, indices: Indices) -> Result { + self.insert_indices(indices)?; Ok(self) } - /// Retrieves the vertex `indices` of the mesh, returns None if not found. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_indices`] - #[inline] - pub fn indices(&self) -> Option<&Indices> { - self.indices.as_ref_option().expect(MESH_EXTRACTED_ERROR) - } - /// Retrieves the vertex `indices` of the mesh. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`or /// if the attribute does not exist. #[inline] - pub fn try_indices(&self) -> Result<&Indices, MeshAccessError> { + pub fn indices(&self) -> Result<&Indices, MeshAccessError> { self.indices.as_ref() } @@ -748,21 +1173,15 @@ impl Mesh { /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. #[inline] - pub fn try_indices_option(&self) -> Result, MeshAccessError> { + pub fn indices_option(&self) -> Result, MeshAccessError> { self.indices.as_ref_option() } - /// Retrieves the vertex `indices` of the mesh mutably. - #[inline] - pub fn indices_mut(&mut self) -> Option<&mut Indices> { - self.try_indices_mut_option().expect(MESH_EXTRACTED_ERROR) - } - /// Retrieves the vertex `indices` of the mesh mutably. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. #[inline] - pub fn try_indices_mut(&mut self) -> Result<&mut Indices, MeshAccessError> { + pub fn indices_mut(&mut self) -> Result<&mut Indices, MeshAccessError> { self.indices.as_mut() } @@ -770,25 +1189,15 @@ impl Mesh { /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. #[inline] - pub fn try_indices_mut_option(&mut self) -> Result, MeshAccessError> { + pub fn indices_mut_option(&mut self) -> Result, MeshAccessError> { self.indices.as_mut_option() } - /// Removes the vertex `indices` from the mesh and returns them. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_remove_indices`] - #[inline] - pub fn remove_indices(&mut self) -> Option { - self.try_remove_indices().expect(MESH_EXTRACTED_ERROR) - } - /// Removes the vertex `indices` from the mesh and returns them. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. #[inline] - pub fn try_remove_indices(&mut self) -> Result, MeshAccessError> { + pub fn remove_indices(&mut self) -> Result, MeshAccessError> { self.indices.replace(None) } @@ -796,22 +1205,9 @@ impl Mesh { /// /// (Alternatively, you can use [`Mesh::remove_indices`] to mutate an existing mesh in-place) /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_with_removed_indices`] - #[must_use] - pub fn with_removed_indices(mut self) -> Self { - self.remove_indices(); - self - } - - /// Consumes the mesh and returns a mesh without the vertex `indices` of the mesh. - /// - /// (Alternatively, you can use [`Mesh::try_remove_indices`] to mutate an existing mesh in-place) - /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. - pub fn try_with_removed_indices(mut self) -> Result { - self.try_remove_indices()?; + pub fn with_removed_indices(mut self) -> Result { + self.remove_indices()?; Ok(self) } @@ -967,25 +1363,13 @@ impl Mesh { } } - /// Duplicates the vertex attributes so that no vertices are shared. - /// - /// This can dramatically increase the vertex count, so make sure this is what you want. - /// Does nothing if no [Indices] are set. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_duplicate_vertices`] - pub fn duplicate_vertices(&mut self) { - self.try_duplicate_vertices().expect(MESH_EXTRACTED_ERROR); - } - /// Duplicates the vertex attributes so that no vertices are shared. /// /// This can dramatically increase the vertex count, so make sure this is what you want. /// Does nothing if no [Indices] are set. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. - pub fn try_duplicate_vertices(&mut self) -> Result<(), MeshAccessError> { + pub fn duplicate_vertices(&mut self) -> Result<(), MeshAccessError> { fn duplicate(values: &[T], indices: impl Iterator) -> Vec { indices.map(|i| values[i]).collect() } @@ -1044,25 +1428,9 @@ impl Mesh { /// /// (Alternatively, you can use [`Mesh::duplicate_vertices`] to mutate an existing mesh in-place) /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_with_duplicated_vertices`] - #[must_use] - pub fn with_duplicated_vertices(mut self) -> Self { - self.duplicate_vertices(); - self - } - - /// 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::try_duplicate_vertices`] to mutate an existing mesh in-place) - /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. - pub fn try_with_duplicated_vertices(mut self) -> Result { - self.try_duplicate_vertices()?; + pub fn with_duplicated_vertices(mut self) -> Result { + self.duplicate_vertices()?; Ok(self) } @@ -1132,48 +1500,18 @@ impl Mesh { /// # Panics /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].= - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_compute_normals`] - pub fn compute_normals(&mut self) { - self.try_compute_normals().expect(MESH_EXTRACTED_ERROR); - } - - /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh. - /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat - /// normals. - /// - /// # Panics - /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. - /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].= - pub fn try_compute_normals(&mut self) -> Result<(), MeshAccessError> { + pub fn compute_normals(&mut self) -> Result<(), MeshAccessError> { assert!( matches!(self.primitive_topology, PrimitiveTopology::TriangleList), "`compute_normals` can only work on `TriangleList`s" ); - if self.try_indices_option()?.is_none() { - self.try_compute_flat_normals() + if self.indices_option()?.is_none() { + self.compute_flat_normals() } else { - self.try_compute_smooth_normals() + self.compute_smooth_normals() } } - /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh. - /// - /// # Panics - /// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. - /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. - /// Consider calling [`Mesh::duplicate_vertices`] or exporting your mesh with normal - /// attributes. - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_compute_flat_normals`] - /// - /// FIXME: This should handle more cases since this is called as a part of gltf - /// mesh loading where we can't really blame users for loading meshes that might - /// not conform to the limitations here! - pub fn compute_flat_normals(&mut self) { - self.try_compute_flat_normals().expect(MESH_EXTRACTED_ERROR); - } - /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh. /// /// # Panics @@ -1185,9 +1523,9 @@ impl Mesh { /// FIXME: This should handle more cases since this is called as a part of gltf /// mesh loading where we can't really blame users for loading meshes that might /// not conform to the limitations here! - pub fn try_compute_flat_normals(&mut self) -> Result<(), MeshAccessError> { + pub fn compute_flat_normals(&mut self) -> Result<(), MeshAccessError> { assert!( - self.try_indices_option()?.is_none(), + self.indices_option()?.is_none(), "`compute_flat_normals` can't work on indexed geometry. Consider calling either `Mesh::compute_smooth_normals` or `Mesh::duplicate_vertices` followed by `Mesh::compute_flat_normals`." ); assert!( @@ -1196,7 +1534,7 @@ impl Mesh { ); let positions = self - .try_attribute(Mesh::ATTRIBUTE_POSITION)? + .attribute(Mesh::ATTRIBUTE_POSITION)? .as_float3() .expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`"); @@ -1206,30 +1544,7 @@ impl Mesh { .flat_map(|normal| [normal; 3]) .collect(); - self.try_insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - } - - /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared - /// vertices. - /// - /// This method weights normals by the angles of the corners of connected triangles, thus - /// eliminating triangle area and count as factors in the final normal. This does make it - /// somewhat slower than [`Mesh::compute_area_weighted_normals`] which does not need to - /// greedily normalize each triangle's normal or calculate corner angles. - /// - /// If you would rather have the computed normals be weighted by triangle area, see - /// [`Mesh::compute_area_weighted_normals`] instead. If you need to weight them in some other - /// way, see [`Mesh::compute_custom_smooth_normals`]. - /// - /// # Panics - /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. - /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. - /// Panics if the mesh does not have indices defined. - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_compute_smooth_normals`] - pub fn compute_smooth_normals(&mut self) { - self.try_compute_smooth_normals() - .expect(MESH_EXTRACTED_ERROR); + self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals) } /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared @@ -1248,8 +1563,8 @@ impl Mesh { /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. /// Panics if the mesh does not have indices defined. - pub fn try_compute_smooth_normals(&mut self) -> Result<(), MeshAccessError> { - self.try_compute_custom_smooth_normals(|[a, b, c], positions, normals| { + pub fn compute_smooth_normals(&mut self) -> Result<(), MeshAccessError> { + self.compute_custom_smooth_normals(|[a, b, c], positions, normals| { let pa = Vec3::from(positions[a]); let pb = Vec3::from(positions[b]); let pc = Vec3::from(positions[c]); @@ -1291,107 +1606,30 @@ impl Mesh { /// /// This method weights normals by the area of each triangle containing the vertex. Thus, /// larger triangles will skew the normals of their vertices towards their own normal more - /// than smaller triangles will. - /// - /// This method is actually somewhat faster than [`Mesh::compute_smooth_normals`] because an - /// intermediate result of triangle normal calculation is already scaled by the triangle's area. - /// - /// If you would rather have the computed normals be influenced only by the angles of connected - /// edges, see [`Mesh::compute_smooth_normals`] instead. If you need to weight them in some - /// other way, see [`Mesh::compute_custom_smooth_normals`]. - /// - /// # Panics - /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. - /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. - /// Panics if the mesh does not have indices defined. - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_compute_area_weighted_normals`] - pub fn compute_area_weighted_normals(&mut self) { - self.try_compute_area_weighted_normals() - .expect(MESH_EXTRACTED_ERROR); - } - - /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared - /// vertices. - /// - /// This method weights normals by the area of each triangle containing the vertex. Thus, - /// larger triangles will skew the normals of their vertices towards their own normal more - /// than smaller triangles will. - /// - /// This method is actually somewhat faster than [`Mesh::compute_smooth_normals`] because an - /// intermediate result of triangle normal calculation is already scaled by the triangle's area. - /// - /// If you would rather have the computed normals be influenced only by the angles of connected - /// edges, see [`Mesh::compute_smooth_normals`] instead. If you need to weight them in some - /// other way, see [`Mesh::compute_custom_smooth_normals`]. - /// - /// # Panics - /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. - /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. - /// Panics if the mesh does not have indices defined. - pub fn try_compute_area_weighted_normals(&mut self) -> Result<(), MeshAccessError> { - self.try_compute_custom_smooth_normals(|[a, b, c], positions, normals| { - let normal = Vec3::from(triangle_area_normal( - positions[a], - positions[b], - positions[c], - )); - [a, b, c].into_iter().for_each(|pos| { - normals[pos] += normal; - }); - }) - } - - /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared - /// vertices. - /// - /// This method allows you to customize how normals are weighted via the `per_triangle` parameter, - /// which must be a function or closure that accepts 3 parameters: - /// - The indices of the three vertices of the triangle as a `[usize; 3]`. - /// - A reference to the values of the [`Mesh::ATTRIBUTE_POSITION`] of the mesh (`&[[f32; 3]]`). - /// - A mutable reference to the sums of all normals so far. - /// - /// See also the standard methods included in Bevy for calculating smooth normals: - /// - [`Mesh::compute_smooth_normals`] - /// - [`Mesh::compute_area_weighted_normals`] - /// - /// An example that would weight each connected triangle's normal equally, thus skewing normals - /// towards the planes divided into the most triangles: - /// ``` - /// # use bevy_asset::RenderAssetUsages; - /// # use bevy_mesh::{Mesh, PrimitiveTopology, Meshable, MeshBuilder}; - /// # use bevy_math::{Vec3, primitives::Cuboid}; - /// # let mut mesh = Cuboid::default().mesh().build(); - /// mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| { - /// let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c])); - /// for idx in [a, b, c] { - /// normals[idx] += normal; - /// } - /// }); - /// ``` - /// - /// # Panics - /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. - /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. - /// Panics if the mesh does not have indices defined. - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_compute_custom_smooth_normals`] - // - // FIXME: This should handle more cases since this is called as a part of gltf - // mesh loading where we can't really blame users for loading meshes that might - // not conform to the limitations here! - // - // When fixed, also update "Panics" sections of - // - [Mesh::compute_smooth_normals] - // - [Mesh::with_computed_smooth_normals] - // - [Mesh::compute_area_weighted_normals] - // - [Mesh::with_computed_area_weighted_normals] - pub fn compute_custom_smooth_normals( - &mut self, - per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]), - ) { - self.try_compute_custom_smooth_normals(per_triangle) - .expect(MESH_EXTRACTED_ERROR); + /// than smaller triangles will. + /// + /// This method is actually somewhat faster than [`Mesh::compute_smooth_normals`] because an + /// intermediate result of triangle normal calculation is already scaled by the triangle's area. + /// + /// If you would rather have the computed normals be influenced only by the angles of connected + /// edges, see [`Mesh::compute_smooth_normals`] instead. If you need to weight them in some + /// other way, see [`Mesh::compute_custom_smooth_normals`]. + /// + /// # Panics + /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. + /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. + /// Panics if the mesh does not have indices defined. + pub fn compute_area_weighted_normals(&mut self) -> Result<(), MeshAccessError> { + self.compute_custom_smooth_normals(|[a, b, c], positions, normals| { + let normal = Vec3::from(triangle_area_normal( + positions[a], + positions[b], + positions[c], + )); + [a, b, c].into_iter().for_each(|pos| { + normals[pos] += normal; + }); + }) } /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared @@ -1436,7 +1674,7 @@ impl Mesh { // - [Mesh::with_computed_smooth_normals] // - [Mesh::compute_area_weighted_normals] // - [Mesh::with_computed_area_weighted_normals] - pub fn try_compute_custom_smooth_normals( + pub fn compute_custom_smooth_normals( &mut self, mut per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]), ) -> Result<(), MeshAccessError> { @@ -1445,18 +1683,18 @@ impl Mesh { "smooth normals can only be computed on `TriangleList`s" ); assert!( - self.try_indices_option()?.is_some(), + self.indices_option()?.is_some(), "smooth normals can only be computed on indexed meshes" ); let positions = self - .try_attribute(Mesh::ATTRIBUTE_POSITION)? + .attribute(Mesh::ATTRIBUTE_POSITION)? .as_float3() .expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`"); let mut normals = vec![Vec3::ZERO; positions.len()]; - self.try_indices()? + self.indices()? .iter() .collect::>() .chunks_exact(3) @@ -1466,24 +1704,7 @@ impl Mesh { *normal = normal.try_normalize().unwrap_or(Vec3::ZERO); } - self.try_insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - } - - /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`]. - /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat - /// normals. - /// - /// (Alternatively, you can use [`Mesh::compute_normals`] to mutate an existing mesh in-place) - /// - /// # Panics - /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. - /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_with_computed_normals`] - #[must_use] - pub fn with_computed_normals(self) -> Self { - self.try_with_computed_normals() - .expect(MESH_EXTRACTED_ERROR) + self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals) } /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`]. @@ -1495,8 +1716,8 @@ impl Mesh { /// # Panics /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. - pub fn try_with_computed_normals(mut self) -> Result { - self.try_compute_normals()?; + pub fn with_computed_normals(mut self) -> Result { + self.compute_normals()?; Ok(self) } @@ -1508,23 +1729,8 @@ impl Mesh { /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. /// Panics if the mesh has indices defined - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_with_computed_flat_normals`] - pub fn with_computed_flat_normals(mut self) -> Self { - self.compute_flat_normals(); - self - } - - /// 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 [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. - /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. - /// Panics if the mesh has indices defined - pub fn try_with_computed_flat_normals(mut self) -> Result { - self.try_compute_flat_normals()?; + pub fn with_computed_flat_normals(mut self) -> Result { + self.compute_flat_normals()?; Ok(self) } @@ -1540,26 +1746,8 @@ impl Mesh { /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. /// Panics if the mesh does not have indices defined. - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_with_computed_smooth_normals`] - pub fn with_computed_smooth_normals(mut self) -> Self { - self.compute_smooth_normals(); - self - } - /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`]. - /// - /// (Alternatively, you can use [`Mesh::compute_smooth_normals`] to mutate an existing mesh in-place) - /// - /// This method weights normals by the angles of triangle corners connected to each vertex. If - /// you would rather have the computed normals be weighted by triangle area, see - /// [`Mesh::with_computed_area_weighted_normals`] instead. - /// - /// # Panics - /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. - /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. - /// Panics if the mesh does not have indices defined. - pub fn try_with_computed_smooth_normals(mut self) -> Result { - self.try_compute_smooth_normals()?; + pub fn with_computed_smooth_normals(mut self) -> Result { + self.compute_smooth_normals()?; Ok(self) } @@ -1576,28 +1764,8 @@ impl Mesh { /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. /// Panics if the mesh does not have indices defined. - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_with_computed_area_weighted_normals`] - pub fn with_computed_area_weighted_normals(mut self) -> Self { - self.compute_area_weighted_normals(); - self - } - - /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`]. - /// - /// (Alternatively, you can use [`Mesh::compute_area_weighted_normals`] to mutate an existing mesh in-place) - /// - /// This method weights normals by the area of each triangle containing the vertex. Thus, - /// larger triangles will skew the normals of their vertices towards their own normal more - /// than smaller triangles will. If you would rather have the computed normals be influenced - /// only by the angles of connected edges, see [`Mesh::with_computed_smooth_normals`] instead. - /// - /// # Panics - /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. - /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. - /// Panics if the mesh does not have indices defined. - pub fn try_with_computed_area_weighted_normals(mut self) -> Result { - self.try_compute_area_weighted_normals()?; + pub fn with_computed_area_weighted_normals(mut self) -> Result { + self.compute_area_weighted_normals()?; Ok(self) } @@ -1608,7 +1776,7 @@ impl Mesh { #[cfg(feature = "bevy_mikktspace")] pub fn generate_tangents(&mut self) -> Result<(), super::GenerateTangentsError> { let tangents = super::generate_tangents_for_mesh(self)?; - self.try_insert_attribute(Mesh::ATTRIBUTE_TANGENT, tangents)?; + self.insert_attribute(Mesh::ATTRIBUTE_TANGENT, tangents)?; Ok(()) } @@ -1657,8 +1825,8 @@ impl Mesh { let index_offset = self.count_vertices(); // Extend attributes of `self` with attributes of `other`. - for (attribute, values) in self.try_attributes_mut()? { - if let Some(other_values) = other.try_attribute_option(attribute.id)? { + for (attribute, values) in self.attributes_mut()? { + if let Some(other_values) = other.attribute_option(attribute.id)? { #[expect( clippy::match_same_arms, reason = "Although the bindings on some match arms may have different types, each variant has different semantics; thus it's not guaranteed that they will use the same type forever." @@ -1696,7 +1864,7 @@ impl Mesh { return Err(MeshMergeError::IncompatibleVertexAttributes { self_attribute: *attribute, other_attribute: other - .try_attribute_data(attribute.id)? + .attribute_data(attribute.id)? .map(|data| data.attribute), }) } @@ -1706,7 +1874,7 @@ impl Mesh { // Extend indices of `self` with indices of `other`. if let (Some(indices), Some(other_indices)) = - (self.try_indices_mut_option()?, other.try_indices_option()?) + (self.indices_mut_option()?, other.indices_option()?) { indices.extend(other_indices.iter().map(|i| (i + index_offset) as u32)); } @@ -1716,39 +1884,15 @@ impl Mesh { /// Transforms the vertex positions, normals, and tangents of the mesh by the given [`Transform`]. /// /// `Aabb` of entities with modified mesh are not updated automatically. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_transformed_by`] - pub fn transformed_by(mut self, transform: Transform) -> Self { - self.transform_by(transform); - self - } - - /// Transforms the vertex positions, normals, and tangents of the mesh by the given [`Transform`]. - /// - /// `Aabb` of entities with modified mesh are not updated automatically. - pub fn try_transformed_by(mut self, transform: Transform) -> Result { - self.try_transform_by(transform)?; + pub fn transformed_by(mut self, transform: Transform) -> Result { + self.transform_by(transform)?; Ok(self) } /// Transforms the vertex positions, normals, and tangents of the mesh in place by the given [`Transform`]. /// /// `Aabb` of entities with modified mesh are not updated automatically. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_transform_by`] - pub fn transform_by(&mut self, transform: Transform) { - self.try_transform_by(transform) - .expect(MESH_EXTRACTED_ERROR); - } - - /// Transforms the vertex positions, normals, and tangents of the mesh in place by the given [`Transform`]. - /// - /// `Aabb` of entities with modified mesh are not updated automatically. - pub fn try_transform_by(&mut self, transform: Transform) -> Result<(), MeshAccessError> { + pub fn transform_by(&mut self, transform: Transform) -> Result<(), MeshAccessError> { // Needed when transforming normals and tangents let scale_recip = 1. / transform.scale; debug_assert!( @@ -1757,7 +1901,7 @@ impl Mesh { ); if let Some(VertexAttributeValues::Float32x3(positions)) = - self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)? + self.attribute_mut_option(Mesh::ATTRIBUTE_POSITION)? { // Apply scale, rotation, and translation to vertex positions positions @@ -1774,7 +1918,7 @@ impl Mesh { } if let Some(VertexAttributeValues::Float32x3(normals)) = - self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)? + self.attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)? { // Transform normals, taking into account non-uniform scaling and rotation normals.iter_mut().for_each(|normal| { @@ -1785,7 +1929,7 @@ impl Mesh { } if let Some(VertexAttributeValues::Float32x4(tangents)) = - self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)? + self.attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)? { // Transform tangents, taking into account non-uniform scaling and rotation tangents.iter_mut().for_each(|tangent| { @@ -1803,45 +1947,21 @@ impl Mesh { /// Translates the vertex positions of the mesh by the given [`Vec3`]. /// /// `Aabb` of entities with modified mesh are not updated automatically. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_translated_by`] - pub fn translated_by(mut self, translation: Vec3) -> Self { - self.translate_by(translation); - self - } - - /// Translates the vertex positions of the mesh by the given [`Vec3`]. - /// - /// `Aabb` of entities with modified mesh are not updated automatically. - pub fn try_translated_by(mut self, translation: Vec3) -> Result { - self.try_translate_by(translation)?; + pub fn translated_by(mut self, translation: Vec3) -> Result { + self.translate_by(translation)?; Ok(self) } /// Translates the vertex positions of the mesh in place by the given [`Vec3`]. /// /// `Aabb` of entities with modified mesh are not updated automatically. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_translate_by`] - pub fn translate_by(&mut self, translation: Vec3) { - self.try_translate_by(translation) - .expect(MESH_EXTRACTED_ERROR); - } - - /// Translates the vertex positions of the mesh in place by the given [`Vec3`]. - /// - /// `Aabb` of entities with modified mesh are not updated automatically. - pub fn try_translate_by(&mut self, translation: Vec3) -> Result<(), MeshAccessError> { + pub fn translate_by(&mut self, translation: Vec3) -> Result<(), MeshAccessError> { if translation == Vec3::ZERO { return Ok(()); } if let Some(VertexAttributeValues::Float32x3(positions)) = - self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)? + self.attribute_mut_option(Mesh::ATTRIBUTE_POSITION)? { // Apply translation to vertex positions positions @@ -1855,40 +1975,17 @@ impl Mesh { /// Rotates the vertex positions, normals, and tangents of the mesh by the given [`Quat`]. /// /// `Aabb` of entities with modified mesh are not updated automatically. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_rotated_by`] - pub fn rotated_by(mut self, rotation: Quat) -> Self { - self.try_rotate_by(rotation).expect(MESH_EXTRACTED_ERROR); - self - } - - /// Rotates the vertex positions, normals, and tangents of the mesh by the given [`Quat`]. - /// - /// `Aabb` of entities with modified mesh are not updated automatically. - pub fn try_rotated_by(mut self, rotation: Quat) -> Result { - self.try_rotate_by(rotation)?; + pub fn rotated_by(mut self, rotation: Quat) -> Result { + self.rotate_by(rotation)?; Ok(self) } /// Rotates the vertex positions, normals, and tangents of the mesh in place by the given [`Quat`]. /// /// `Aabb` of entities with modified mesh are not updated automatically. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_rotate_by`] - pub fn rotate_by(&mut self, rotation: Quat) { - self.try_rotate_by(rotation).expect(MESH_EXTRACTED_ERROR); - } - - /// Rotates the vertex positions, normals, and tangents of the mesh in place by the given [`Quat`]. - /// - /// `Aabb` of entities with modified mesh are not updated automatically. - pub fn try_rotate_by(&mut self, rotation: Quat) -> Result<(), MeshAccessError> { + pub fn rotate_by(&mut self, rotation: Quat) -> Result<(), MeshAccessError> { if let Some(VertexAttributeValues::Float32x3(positions)) = - self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)? + self.attribute_mut_option(Mesh::ATTRIBUTE_POSITION)? { // Apply rotation to vertex positions positions @@ -1902,7 +1999,7 @@ impl Mesh { } if let Some(VertexAttributeValues::Float32x3(normals)) = - self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)? + self.attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)? { // Transform normals normals.iter_mut().for_each(|normal| { @@ -1911,7 +2008,7 @@ impl Mesh { } if let Some(VertexAttributeValues::Float32x4(tangents)) = - self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)? + self.attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)? { // Transform tangents tangents.iter_mut().for_each(|tangent| { @@ -1928,38 +2025,15 @@ impl Mesh { /// Scales the vertex positions, normals, and tangents of the mesh by the given [`Vec3`]. /// /// `Aabb` of entities with modified mesh are not updated automatically. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_scaled_by`] - pub fn scaled_by(mut self, scale: Vec3) -> Self { - self.scale_by(scale); - self - } - - /// Scales the vertex positions, normals, and tangents of the mesh by the given [`Vec3`]. - /// - /// `Aabb` of entities with modified mesh are not updated automatically. - pub fn try_scaled_by(mut self, scale: Vec3) -> Result { - self.try_scale_by(scale)?; + pub fn scaled_by(mut self, scale: Vec3) -> Result { + self.scale_by(scale)?; Ok(self) } /// Scales the vertex positions, normals, and tangents of the mesh in place by the given [`Vec3`]. /// /// `Aabb` of entities with modified mesh are not updated automatically. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_scale_by`] - pub fn scale_by(&mut self, scale: Vec3) { - self.try_scale_by(scale).expect(MESH_EXTRACTED_ERROR); - } - - /// Scales the vertex positions, normals, and tangents of the mesh in place by the given [`Vec3`]. - /// - /// `Aabb` of entities with modified mesh are not updated automatically. - pub fn try_scale_by(&mut self, scale: Vec3) -> Result<(), MeshAccessError> { + pub fn scale_by(&mut self, scale: Vec3) -> Result<(), MeshAccessError> { // Needed when transforming normals and tangents let scale_recip = 1. / scale; debug_assert!( @@ -1968,7 +2042,7 @@ impl Mesh { ); if let Some(VertexAttributeValues::Float32x3(positions)) = - self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)? + self.attribute_mut_option(Mesh::ATTRIBUTE_POSITION)? { // Apply scale to vertex positions positions @@ -1982,7 +2056,7 @@ impl Mesh { } if let Some(VertexAttributeValues::Float32x3(normals)) = - self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)? + self.attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)? { // Transform normals, taking into account non-uniform scaling normals.iter_mut().for_each(|normal| { @@ -1991,7 +2065,7 @@ impl Mesh { } if let Some(VertexAttributeValues::Float32x4(tangents)) = - self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)? + self.attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)? { // Transform tangents, taking into account non-uniform scaling tangents.iter_mut().for_each(|tangent| { @@ -2008,19 +2082,9 @@ impl Mesh { } /// Normalize joint weights so they sum to 1. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_normalize_joint_weights`] - pub fn normalize_joint_weights(&mut self) { - self.try_normalize_joint_weights() - .expect(MESH_EXTRACTED_ERROR); - } - - /// Normalize joint weights so they sum to 1. - pub fn try_normalize_joint_weights(&mut self) -> Result<(), MeshAccessError> { + pub fn normalize_joint_weights(&mut self) -> Result<(), MeshAccessError> { if let Some(VertexAttributeValues::Float32x4(joints)) = - self.try_attribute_mut_option(Self::ATTRIBUTE_JOINT_WEIGHT)? + self.attribute_mut_option(Self::ATTRIBUTE_JOINT_WEIGHT)? { for weights in joints.iter_mut() { // force negative weights to zero @@ -2052,13 +2116,13 @@ impl Mesh { /// [primitive topology]: PrimitiveTopology /// [triangles]: Triangle3d pub fn triangles(&self) -> Result + '_, MeshTrianglesError> { - let position_data = self.try_attribute(Mesh::ATTRIBUTE_POSITION)?; + let position_data = self.attribute(Mesh::ATTRIBUTE_POSITION)?; let Some(vertices) = position_data.as_float3() else { return Err(MeshTrianglesError::PositionsFormat); }; - let indices = self.try_indices()?; + let indices = self.indices()?; match self.primitive_topology { PrimitiveTopology::TriangleList => { @@ -2183,35 +2247,22 @@ impl Mesh { #[cfg(feature = "morph")] impl Mesh { /// Whether this mesh has morph targets. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_has_morph_targets`] - pub fn has_morph_targets(&self) -> bool { - self.try_has_morph_targets().expect(MESH_EXTRACTED_ERROR) - } - - /// Whether this mesh has morph targets. - pub fn try_has_morph_targets(&self) -> Result { + pub fn has_morph_targets(&self) -> Result { Ok(self.morph_targets.as_ref_option()?.is_some()) } - /// Set [morph targets] image for this mesh. This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info. - /// - /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation + /// Retrieve the morph targets for this mesh, or None if there are no morph targets. /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_set_morph_targets`] - pub fn set_morph_targets(&mut self, morph_targets: Handle) { - self.try_set_morph_targets(morph_targets) - .expect(MESH_EXTRACTED_ERROR); + /// Returns an error if the mesh data has been extracted to `RenderWorld`or + /// if the morph targets do not exist. + pub fn morph_targets(&self) -> Result<&Handle, MeshAccessError> { + self.morph_targets.as_ref() } /// Set [morph targets] image for this mesh. This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info. /// /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation - pub fn try_set_morph_targets( + pub fn set_morph_targets( &mut self, morph_targets: Handle, ) -> Result<(), MeshAccessError> { @@ -2219,41 +2270,6 @@ impl Mesh { Ok(()) } - /// Retrieve the morph targets for this mesh, or None if there are no morph targets. - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_morph_targets`] - pub fn morph_targets(&self) -> Option<&Handle> { - self.morph_targets - .as_ref_option() - .expect(MESH_EXTRACTED_ERROR) - } - - /// Retrieve the morph targets for this mesh, or None if there are no morph targets. - /// - /// Returns an error if the mesh data has been extracted to `RenderWorld`or - /// if the morph targets do not exist. - pub fn try_morph_targets(&self) -> Result<&Handle, MeshAccessError> { - self.morph_targets.as_ref() - } - - /// Consumes the mesh and returns a mesh with the given [morph targets]. - /// - /// This requires a "morph target image". See [`MorphTargetImage`](crate::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 - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_with_morph_targets`] - #[must_use] - pub fn with_morph_targets(mut self, morph_targets: Handle) -> Self { - self.set_morph_targets(morph_targets); - self - } - /// Consumes the mesh and returns a mesh with the given [morph targets]. /// /// This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info. @@ -2263,77 +2279,38 @@ impl Mesh { /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. - pub fn try_with_morph_targets( + pub fn with_morph_targets( mut self, morph_targets: Handle, ) -> Result { - self.try_set_morph_targets(morph_targets)?; + self.set_morph_targets(morph_targets)?; Ok(self) } - /// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_set_morph_target_names`] - pub fn set_morph_target_names(&mut self, names: Vec) { - self.try_set_morph_target_names(names) - .expect(MESH_EXTRACTED_ERROR); - } - /// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. - pub fn try_set_morph_target_names( - &mut self, - names: Vec, - ) -> Result<(), MeshAccessError> { + pub fn set_morph_target_names(&mut self, names: Vec) -> Result<(), MeshAccessError> { self.morph_target_names.replace(Some(names))?; Ok(()) } - /// 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) - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_set_morph_target_names`] - #[must_use] - pub fn with_morph_target_names(self, names: Vec) -> Self { - self.try_with_morph_target_names(names) - .expect(MESH_EXTRACTED_ERROR) - } - /// 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) /// /// Returns an error if the mesh data has been extracted to `RenderWorld`. - pub fn try_with_morph_target_names( - mut self, - names: Vec, - ) -> Result { - self.try_set_morph_target_names(names)?; + pub fn with_morph_target_names(mut self, names: Vec) -> Result { + self.set_morph_target_names(names)?; Ok(self) } - /// Gets a list of all morph target names, if they exist. - /// - /// # Panics - /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle - /// this as an error use [`Mesh::try_morph_target_names`] - pub fn morph_target_names(&self) -> Option<&[String]> { - self.try_morph_target_names().expect(MESH_EXTRACTED_ERROR) - } - /// Gets a list of all morph target names, if they exist. /// /// Returns an error if the mesh data has been extracted to `RenderWorld`or /// if the morph targets do not exist. - pub fn try_morph_target_names(&self) -> Result, MeshAccessError> { + pub fn morph_target_names(&self) -> Result, MeshAccessError> { Ok(self .morph_target_names .as_ref_option()? @@ -2360,7 +2337,7 @@ impl core::ops::Mul for Transform { type Output = Mesh; fn mul(self, rhs: Mesh) -> Self::Output { - rhs.transformed_by(self) + rhs.transformed_by(self).expect(MESH_EXTRACTED_ERROR) } } @@ -2517,9 +2494,9 @@ pub enum MeshMergeError { #[cfg(test)] mod tests { - use super::Mesh; #[cfg(feature = "serialize")] use super::SerializedMesh; + use super::{InfallibleMesh, Mesh}; use crate::mesh::{Indices, MeshWindingInvertError, VertexAttributeValues}; use crate::PrimitiveTopology; use bevy_asset::RenderAssetUsages; @@ -2539,7 +2516,7 @@ mod tests { #[test] fn transform_mesh() { - let mesh = Mesh::new( + let mesh = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -2591,7 +2568,7 @@ mod tests { #[test] fn point_list_mesh_invert_winding() { - let mesh = Mesh::new(PrimitiveTopology::PointList, RenderAssetUsages::default()) + let mesh = InfallibleMesh::new(PrimitiveTopology::PointList, RenderAssetUsages::default()) .with_inserted_indices(Indices::U32(vec![])); assert!(matches!( mesh.with_inverted_winding(), @@ -2601,7 +2578,7 @@ mod tests { #[test] fn line_list_mesh_invert_winding() { - let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) + let mesh = InfallibleMesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) .with_inserted_indices(Indices::U32(vec![0, 1, 1, 2, 2, 3])); let mesh = mesh.with_inverted_winding().unwrap(); assert_eq!( @@ -2612,7 +2589,7 @@ mod tests { #[test] fn line_list_mesh_invert_winding_fail() { - let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) + let mesh = InfallibleMesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) .with_inserted_indices(Indices::U32(vec![0, 1, 1])); assert!(matches!( mesh.with_inverted_winding(), @@ -2622,7 +2599,7 @@ mod tests { #[test] fn line_strip_mesh_invert_winding() { - let mesh = Mesh::new(PrimitiveTopology::LineStrip, RenderAssetUsages::default()) + let mesh = InfallibleMesh::new(PrimitiveTopology::LineStrip, RenderAssetUsages::default()) .with_inserted_indices(Indices::U32(vec![0, 1, 2, 3])); let mesh = mesh.with_inverted_winding().unwrap(); assert_eq!( @@ -2633,7 +2610,7 @@ mod tests { #[test] fn triangle_list_mesh_invert_winding() { - let mesh = Mesh::new( + let mesh = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -2653,7 +2630,7 @@ mod tests { #[test] fn triangle_list_mesh_invert_winding_fail() { - let mesh = Mesh::new( + let mesh = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -2666,7 +2643,7 @@ mod tests { #[test] fn triangle_strip_mesh_invert_winding() { - let mesh = Mesh::new( + let mesh = InfallibleMesh::new( PrimitiveTopology::TriangleStrip, RenderAssetUsages::default(), ) @@ -2680,7 +2657,7 @@ mod tests { #[test] fn compute_area_weighted_normals() { - let mut mesh = Mesh::new( + let mut mesh = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ); @@ -2715,7 +2692,7 @@ mod tests { #[test] fn compute_area_weighted_normals_proportionate() { - let mut mesh = Mesh::new( + let mut mesh = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ); @@ -2777,7 +2754,7 @@ mod tests { 4, 5, 1, 1, 0, 4, // top 3, 2, 6, 6, 7, 3, // bottom ]); - let mut mesh = Mesh::new( + let mut mesh = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ); @@ -2803,7 +2780,7 @@ mod tests { #[test] fn triangles_from_triangle_list() { - let mut mesh = Mesh::new( + let mut mesh = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ); @@ -2835,7 +2812,7 @@ mod tests { #[test] fn triangles_from_triangle_strip() { - let mut mesh = Mesh::new( + let mut mesh = InfallibleMesh::new( PrimitiveTopology::TriangleStrip, RenderAssetUsages::default(), ); @@ -2881,7 +2858,7 @@ mod tests { #[cfg(feature = "serialize")] #[test] fn serialize_deserialize_mesh() { - let mut mesh = Mesh::new( + let mut mesh = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ); @@ -2892,6 +2869,8 @@ mod tests { ); mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3])); + let mesh = mesh.into_mesh(); + let serialized_mesh = SerializedMesh::from_mesh(mesh.clone()); let serialized_string = serde_json::to_string(&serialized_mesh).unwrap(); let serialized_mesh_from_string: SerializedMesh = diff --git a/crates/bevy_mesh/src/mikktspace.rs b/crates/bevy_mesh/src/mikktspace.rs index 36b24353c7c13..528b5d076aa00 100644 --- a/crates/bevy_mesh/src/mikktspace.rs +++ b/crates/bevy_mesh/src/mikktspace.rs @@ -84,7 +84,7 @@ pub(crate) fn generate_tangents_for_mesh( other => return Err(GenerateTangentsError::UnsupportedTopology(other)), }; - let positions = mesh.try_attribute_option(Mesh::ATTRIBUTE_POSITION)?.ok_or( + let positions = mesh.attribute_option(Mesh::ATTRIBUTE_POSITION)?.ok_or( GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name), )?; let VertexAttributeValues::Float32x3(positions) = positions else { @@ -93,7 +93,7 @@ pub(crate) fn generate_tangents_for_mesh( VertexFormat::Float32x3, )); }; - let normals = mesh.try_attribute_option(Mesh::ATTRIBUTE_NORMAL)?.ok_or( + let normals = mesh.attribute_option(Mesh::ATTRIBUTE_NORMAL)?.ok_or( GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_NORMAL.name), )?; let VertexAttributeValues::Float32x3(normals) = normals else { @@ -102,7 +102,7 @@ pub(crate) fn generate_tangents_for_mesh( VertexFormat::Float32x3, )); }; - let uvs = mesh.try_attribute_option(Mesh::ATTRIBUTE_UV_0)?.ok_or( + let uvs = mesh.attribute_option(Mesh::ATTRIBUTE_UV_0)?.ok_or( GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_UV_0.name), )?; let VertexAttributeValues::Float32x2(uvs) = uvs else { @@ -115,7 +115,7 @@ pub(crate) fn generate_tangents_for_mesh( let len = positions.len(); let tangents = vec![[0., 0., 0., 0.]; len]; let mut mikktspace_mesh = MikktspaceGeometryHelper { - indices: mesh.try_indices_option()?, + indices: mesh.indices_option()?, positions, normals, uvs, diff --git a/crates/bevy_mesh/src/primitives/dim2.rs b/crates/bevy_mesh/src/primitives/dim2.rs index 2a8bb4edf50cf..960bf8b1231c8 100644 --- a/crates/bevy_mesh/src/primitives/dim2.rs +++ b/crates/bevy_mesh/src/primitives/dim2.rs @@ -1,6 +1,7 @@ use core::f32::consts::FRAC_PI_2; use core::mem; +use crate::InfallibleMesh; use crate::{primitives::dim3::triangle3d, Indices, Mesh, PerimeterSegment, VertexAttributeValues}; use bevy_asset::RenderAssetUsages; @@ -59,11 +60,11 @@ impl CircleMeshBuilder { } impl MeshBuilder for CircleMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { Ellipse::new(self.circle.radius, self.circle.radius) .mesh() .resolution(self.resolution) - .build() + .build_infallible() } } @@ -175,7 +176,7 @@ impl CircularSectorMeshBuilder { } impl MeshBuilder for CircularSectorMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let resolution = self.resolution as usize; let mut indices = Vec::with_capacity((resolution - 1) * 3); let mut positions = Vec::with_capacity(resolution + 1); @@ -210,7 +211,7 @@ impl MeshBuilder for CircularSectorMeshBuilder { indices.extend_from_slice(&[0, i, i + 1]); } - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -313,7 +314,7 @@ impl CircularSegmentMeshBuilder { } impl MeshBuilder for CircularSegmentMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let resolution = self.resolution as usize; let mut indices = Vec::with_capacity((resolution - 1) * 3); let mut positions = Vec::with_capacity(resolution + 1); @@ -357,7 +358,7 @@ impl MeshBuilder for CircularSegmentMeshBuilder { indices.extend_from_slice(&[0, i, i + 1]); } - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -427,7 +428,7 @@ impl Meshable for ConvexPolygon { } impl MeshBuilder for ConvexPolygonMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let len = self.vertices.len(); let mut indices = Vec::with_capacity((len - 2) * 3); let mut positions = Vec::with_capacity(len); @@ -438,7 +439,7 @@ impl MeshBuilder for ConvexPolygonMeshBuilder { for i in 2..len as u32 { indices.extend_from_slice(&[0, i - 1, i]); } - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -512,12 +513,12 @@ impl Meshable for RegularPolygon { } impl MeshBuilder for RegularPolygonMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { // The ellipse mesh is just a regular polygon with two radii Ellipse::new(self.circumradius, self.circumradius) .mesh() .resolution(self.sides) - .build() + .build_infallible() } } @@ -576,7 +577,7 @@ impl EllipseMeshBuilder { } impl MeshBuilder for EllipseMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let resolution = self.resolution as usize; let mut indices = Vec::with_capacity((resolution - 2) * 3); let mut positions = Vec::with_capacity(resolution); @@ -602,7 +603,7 @@ impl MeshBuilder for EllipseMeshBuilder { indices.extend_from_slice(&[0, i, i + 1]); } - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -655,11 +656,11 @@ impl Segment2dMeshBuilder { } impl MeshBuilder for Segment2dMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let positions = self.segment.vertices.map(|v| v.extend(0.0)).to_vec(); let indices = Indices::U32(vec![0, 1]); - Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) + InfallibleMesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) .with_inserted_indices(indices) } @@ -688,7 +689,7 @@ pub struct Polyline2dMeshBuilder { } impl MeshBuilder for Polyline2dMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let positions: Vec<_> = self .polyline .vertices @@ -702,7 +703,7 @@ impl MeshBuilder for Polyline2dMeshBuilder { .collect(), ); - Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) + InfallibleMesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) .with_inserted_indices(indices) .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) } @@ -764,7 +765,7 @@ impl AnnulusMeshBuilder { } impl MeshBuilder for AnnulusMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let inner_radius = self.annulus.inner_circle.radius; let outer_radius = self.annulus.outer_circle.radius; @@ -811,7 +812,7 @@ impl MeshBuilder for AnnulusMeshBuilder { indices.extend_from_slice(&[next_outer, next_inner, inner_vertex]); } - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -896,7 +897,7 @@ impl RhombusMeshBuilder { } impl MeshBuilder for RhombusMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let [hhd, vhd] = [self.half_diagonals.x, self.half_diagonals.y]; let positions = vec![ [hhd, 0.0, 0.0], @@ -908,7 +909,7 @@ impl MeshBuilder for RhombusMeshBuilder { let uvs = vec![[1.0, 0.5], [0.5, 0.0], [0.0, 0.5], [0.5, 1.0]]; let indices = Indices::U32(vec![2, 0, 1, 2, 3, 0]); - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -968,7 +969,7 @@ impl Meshable for Triangle2d { } impl MeshBuilder for Triangle2dMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let vertices_3d = self.triangle.vertices.map(|v| v.extend(0.)); let positions: Vec<_> = vertices_3d.into(); @@ -988,7 +989,7 @@ impl MeshBuilder for Triangle2dMeshBuilder { Indices::U32(vec![2, 1, 0]) }; - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -1053,7 +1054,7 @@ impl RectangleMeshBuilder { } impl MeshBuilder for RectangleMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let [hw, hh] = [self.half_size.x, self.half_size.y]; let positions = vec![ [hw, hh, 0.0], @@ -1065,7 +1066,7 @@ impl MeshBuilder for RectangleMeshBuilder { 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( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -1143,7 +1144,7 @@ impl Capsule2dMeshBuilder { } impl MeshBuilder for Capsule2dMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { // The resolution is the number of vertices for one semicircle let resolution = self.resolution; let vertex_count = 2 * resolution; @@ -1207,7 +1208,7 @@ impl MeshBuilder for Capsule2dMeshBuilder { // Add indices for bottom right triangle of the part between the semicircles indices.extend_from_slice(&[resolution, vertex_count - 1, 0]); - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -1290,7 +1291,7 @@ where } fn get_vertex_attributes(&self) -> Option { - fn get_positions(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 3]>> { + fn get_positions(mesh: &mut InfallibleMesh) -> Option<&mut Vec<[f32; 3]>> { if let VertexAttributeValues::Float32x3(data) = mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION)? { @@ -1300,7 +1301,7 @@ where } } - fn get_uvs(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 2]>> { + fn get_uvs(mesh: &mut InfallibleMesh) -> Option<&mut Vec<[f32; 2]>> { if let VertexAttributeValues::Float32x2(data) = mesh.attribute_mut(Mesh::ATTRIBUTE_UV_0)? { @@ -1310,7 +1311,7 @@ where } } - fn get_normals(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 3]>> { + fn get_normals(mesh: &mut InfallibleMesh) -> Option<&mut Vec<[f32; 3]>> { if let VertexAttributeValues::Float32x3(data) = mesh.attribute_mut(Mesh::ATTRIBUTE_NORMAL)? { @@ -1320,8 +1321,8 @@ where } } - let mut outer = self.outer_shape_builder.build(); - let mut inner = self.inner_shape_builder.build(); + let mut outer = self.outer_shape_builder.build_infallible(); + let mut inner = self.inner_shape_builder.build_infallible(); assert_eq!( outer.primitive_topology(), @@ -1371,7 +1372,7 @@ where /// It is assumed that the `primitive_topology` of the mesh returned by /// the underlying builder is [`PrimitiveTopology::TriangleList`] /// and that the mesh has [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes. - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { if let Some(RingMeshBuilderVertexAttributes { outer_uvs, inner_uvs, @@ -1424,7 +1425,7 @@ where let mut positions = outer_positions; positions.extend_from_slice(&inner_positions); - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -1569,7 +1570,7 @@ mod tests { #[test] fn test_regular_polygon() { - let mut mesh = Mesh::from(RegularPolygon::new(7.0, 4)); + let mut mesh = RegularPolygon::new(7.0, 4).mesh().build_infallible(); let Some(VertexAttributeValues::Float32x3(mut positions)) = mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION) diff --git a/crates/bevy_mesh/src/primitives/dim3/capsule.rs b/crates/bevy_mesh/src/primitives/dim3/capsule.rs index f46ebce0d15a7..64f5ae86ee6f3 100644 --- a/crates/bevy_mesh/src/primitives/dim3/capsule.rs +++ b/crates/bevy_mesh/src/primitives/dim3/capsule.rs @@ -1,4 +1,4 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::{ops, primitives::Capsule3d, Vec2, Vec3}; use bevy_reflect::prelude::*; @@ -94,7 +94,7 @@ impl Capsule3dMeshBuilder { } impl MeshBuilder for Capsule3dMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { // code adapted from https://behreajj.medium.com/making-a-capsule-mesh-via-script-in-five-3d-environments-c2214abf02db let Capsule3dMeshBuilder { capsule, @@ -407,7 +407,7 @@ impl MeshBuilder for Capsule3dMeshBuilder { assert_eq!(vs.len(), vert_len); assert_eq!(tris.len(), fs_len as usize); - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) diff --git a/crates/bevy_mesh/src/primitives/dim3/cone.rs b/crates/bevy_mesh/src/primitives/dim3/cone.rs index d06a57f832ea5..b773f05a71e74 100644 --- a/crates/bevy_mesh/src/primitives/dim3/cone.rs +++ b/crates/bevy_mesh/src/primitives/dim3/cone.rs @@ -1,4 +1,4 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::{ops, primitives::Cone, Vec3}; use bevy_reflect::prelude::*; @@ -69,7 +69,7 @@ impl ConeMeshBuilder { } impl MeshBuilder for ConeMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let half_height = self.cone.height / 2.0; // `resolution` vertices for the base, `resolution` vertices for the bottom of the lateral surface, @@ -160,7 +160,7 @@ impl MeshBuilder for ConeMeshBuilder { ConeAnchor::MidPoint => (), }; - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -213,7 +213,7 @@ mod tests { } .mesh() .resolution(4) - .build(); + .build_infallible(); let Some(VertexAttributeValues::Float32x3(mut positions)) = mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION) diff --git a/crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs b/crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs index 8c69378c01e64..a0ca1fe9519ec 100644 --- a/crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs +++ b/crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs @@ -1,4 +1,4 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::{ops, primitives::ConicalFrustum, Vec3}; use bevy_reflect::prelude::*; @@ -61,7 +61,7 @@ impl ConicalFrustumMeshBuilder { } impl MeshBuilder for ConicalFrustumMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { debug_assert!(self.resolution > 2); debug_assert!(self.segments > 0); @@ -155,7 +155,7 @@ impl MeshBuilder for ConicalFrustumMeshBuilder { build_cap(true, radius_top); build_cap(false, radius_bottom); - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) diff --git a/crates/bevy_mesh/src/primitives/dim3/cuboid.rs b/crates/bevy_mesh/src/primitives/dim3/cuboid.rs index 40a7cd45d4433..7edd0c3d71945 100644 --- a/crates/bevy_mesh/src/primitives/dim3/cuboid.rs +++ b/crates/bevy_mesh/src/primitives/dim3/cuboid.rs @@ -1,4 +1,4 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::{primitives::Cuboid, Vec3}; use bevy_reflect::prelude::*; @@ -20,7 +20,7 @@ impl Default for CuboidMeshBuilder { } impl MeshBuilder for CuboidMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let min = -self.half_size; let max = self.half_size; @@ -71,7 +71,7 @@ impl MeshBuilder for CuboidMeshBuilder { 20, 21, 22, 22, 23, 20, // bottom ]); - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) diff --git a/crates/bevy_mesh/src/primitives/dim3/cylinder.rs b/crates/bevy_mesh/src/primitives/dim3/cylinder.rs index 7b1b45974ea62..dc8956243f5c5 100644 --- a/crates/bevy_mesh/src/primitives/dim3/cylinder.rs +++ b/crates/bevy_mesh/src/primitives/dim3/cylinder.rs @@ -1,4 +1,4 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::{ops, primitives::Cylinder}; use bevy_reflect::prelude::*; @@ -94,7 +94,7 @@ impl CylinderMeshBuilder { } impl MeshBuilder for CylinderMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let resolution = self.resolution; let segments = self.segments; @@ -193,7 +193,7 @@ impl MeshBuilder for CylinderMeshBuilder { CylinderAnchor::MidPoint => (), }; - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) diff --git a/crates/bevy_mesh/src/primitives/dim3/plane.rs b/crates/bevy_mesh/src/primitives/dim3/plane.rs index fd892469be6af..3b174f5bb48f8 100644 --- a/crates/bevy_mesh/src/primitives/dim3/plane.rs +++ b/crates/bevy_mesh/src/primitives/dim3/plane.rs @@ -1,4 +1,4 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::{primitives::Plane3d, Dir3, Quat, Vec2, Vec3}; use bevy_reflect::prelude::*; @@ -94,7 +94,7 @@ impl PlaneMeshBuilder { } impl MeshBuilder for PlaneMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let z_vertex_count = self.subdivisions + 2; let x_vertex_count = self.subdivisions + 2; let num_vertices = (z_vertex_count * x_vertex_count) as usize; @@ -131,7 +131,7 @@ impl MeshBuilder for PlaneMeshBuilder { } } - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) diff --git a/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs b/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs index 4d13112579f09..f0e8ae42b0c54 100644 --- a/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs +++ b/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs @@ -1,4 +1,4 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::primitives::Polyline3d; use bevy_reflect::prelude::*; @@ -11,7 +11,7 @@ pub struct Polyline3dMeshBuilder { } impl MeshBuilder for Polyline3dMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let positions: Vec<_> = self.polyline.vertices.clone(); let indices = Indices::U32( @@ -20,7 +20,7 @@ impl MeshBuilder for Polyline3dMeshBuilder { .collect(), ); - Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) + InfallibleMesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) .with_inserted_indices(indices) .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) } diff --git a/crates/bevy_mesh/src/primitives/dim3/segment3d.rs b/crates/bevy_mesh/src/primitives/dim3/segment3d.rs index d032285283afb..4326955cb2324 100644 --- a/crates/bevy_mesh/src/primitives/dim3/segment3d.rs +++ b/crates/bevy_mesh/src/primitives/dim3/segment3d.rs @@ -1,4 +1,4 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::primitives::Segment3d; use bevy_reflect::prelude::*; @@ -11,11 +11,11 @@ pub struct Segment3dMeshBuilder { } impl MeshBuilder for Segment3dMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let positions: Vec<_> = self.segment.vertices.into(); let indices = Indices::U32(vec![0, 1]); - Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) + InfallibleMesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) .with_inserted_indices(indices) .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) } diff --git a/crates/bevy_mesh/src/primitives/dim3/sphere.rs b/crates/bevy_mesh/src/primitives/dim3/sphere.rs index 6ae8eec5ed8bf..946b850430262 100644 --- a/crates/bevy_mesh/src/primitives/dim3/sphere.rs +++ b/crates/bevy_mesh/src/primitives/dim3/sphere.rs @@ -1,4 +1,4 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::{ops, primitives::Sphere}; use bevy_reflect::prelude::*; @@ -81,7 +81,7 @@ impl SphereMeshBuilder { /// and an [`IcosphereError`] is returned. /// /// A good default is `5` subdivisions. - pub fn ico(&self, subdivisions: u32) -> Result { + pub fn ico(&self, subdivisions: u32) -> Result { if subdivisions >= 80 { /* Number of triangles: @@ -151,7 +151,7 @@ impl SphereMeshBuilder { let indices = Indices::U32(indices); - Ok(Mesh::new( + Ok(InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -165,7 +165,7 @@ impl SphereMeshBuilder { /// longitudinal sectors and latitudinal stacks, aka horizontal and vertical resolution. /// /// A good default is `32` sectors and `18` stacks. - pub fn uv(&self, sectors: u32, stacks: u32) -> Mesh { + pub fn uv(&self, sectors: u32, stacks: u32) -> InfallibleMesh { // Largely inspired from http://www.songho.ca/opengl/gl_sphere.html let sectors_f32 = sectors as f32; @@ -220,7 +220,7 @@ impl SphereMeshBuilder { } } - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) @@ -238,7 +238,7 @@ impl MeshBuilder for SphereMeshBuilder { /// /// Panics if the sphere is a [`SphereKind::Ico`] with a subdivision count /// that is greater than or equal to `80` because there will be too many vertices. - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { match self.kind { SphereKind::Ico { subdivisions } => self.ico(subdivisions).unwrap(), SphereKind::Uv { sectors, stacks } => self.uv(sectors, stacks), diff --git a/crates/bevy_mesh/src/primitives/dim3/tetrahedron.rs b/crates/bevy_mesh/src/primitives/dim3/tetrahedron.rs index 529805d9a603f..432aa4337c4a5 100644 --- a/crates/bevy_mesh/src/primitives/dim3/tetrahedron.rs +++ b/crates/bevy_mesh/src/primitives/dim3/tetrahedron.rs @@ -1,5 +1,5 @@ use super::triangle3d; -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::primitives::{Tetrahedron, Triangle3d}; use bevy_reflect::prelude::*; @@ -12,7 +12,7 @@ pub struct TetrahedronMeshBuilder { } impl MeshBuilder for TetrahedronMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let mut faces: Vec<_> = self.tetrahedron.faces().into(); // If the tetrahedron has negative orientation, reverse all the triangles so that @@ -40,7 +40,7 @@ impl MeshBuilder for TetrahedronMeshBuilder { // There are four faces and none of them share vertices. let indices = Indices::U32(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) diff --git a/crates/bevy_mesh/src/primitives/dim3/torus.rs b/crates/bevy_mesh/src/primitives/dim3/torus.rs index 6f370c13418ca..d2267774c764a 100644 --- a/crates/bevy_mesh/src/primitives/dim3/torus.rs +++ b/crates/bevy_mesh/src/primitives/dim3/torus.rs @@ -1,4 +1,4 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::{ops, primitives::Torus, Vec3}; use bevy_reflect::prelude::*; @@ -77,7 +77,7 @@ impl TorusMeshBuilder { } impl MeshBuilder for TorusMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { // code adapted from http://apparat-engine.blogspot.com/2013/04/procedural-meshes-torus.html let n_vertices = (self.major_resolution + 1) * (self.minor_resolution + 1); @@ -147,7 +147,7 @@ impl MeshBuilder for TorusMeshBuilder { } } - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) diff --git a/crates/bevy_mesh/src/primitives/dim3/triangle3d.rs b/crates/bevy_mesh/src/primitives/dim3/triangle3d.rs index e35f272ab9bb1..2337feb512ecd 100644 --- a/crates/bevy_mesh/src/primitives/dim3/triangle3d.rs +++ b/crates/bevy_mesh/src/primitives/dim3/triangle3d.rs @@ -1,4 +1,4 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{Indices, InfallibleMesh, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::{primitives::Triangle3d, Vec3}; use bevy_reflect::prelude::*; @@ -11,7 +11,7 @@ pub struct Triangle3dMeshBuilder { } impl MeshBuilder for Triangle3dMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let positions: Vec<_> = self.triangle.vertices.into(); let uvs: Vec<_> = uv_coords(&self.triangle).into(); @@ -21,7 +21,7 @@ impl MeshBuilder for Triangle3dMeshBuilder { let indices = Indices::U32(vec![0, 1, 2]); - Mesh::new( + InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) diff --git a/crates/bevy_mesh/src/primitives/extrusion.rs b/crates/bevy_mesh/src/primitives/extrusion.rs index 091735dc8349c..2670805080904 100644 --- a/crates/bevy_mesh/src/primitives/extrusion.rs +++ b/crates/bevy_mesh/src/primitives/extrusion.rs @@ -4,7 +4,7 @@ use bevy_math::{ }; use super::{MeshBuilder, Meshable}; -use crate::{Indices, Mesh, PrimitiveTopology, VertexAttributeValues}; +use crate::{Indices, InfallibleMesh, Mesh, PrimitiveTopology, VertexAttributeValues}; /// A type representing a segment of the perimeter of an extrudable mesh. pub enum PerimeterSegment { @@ -177,12 +177,12 @@ where P: Primitive2d + Meshable, P::Output: Extrudable, { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { // Create and move the base mesh to the front - let mut front_face = - self.base_builder - .build() - .translated_by(Vec3::new(0., 0., self.half_depth)); + let mut front_face = self + .base_builder + .build_infallible() + .translated_by(Vec3::new(0., 0., self.half_depth)); // Move the uvs of the front face to be between (0., 0.) and (0.5, 0.5) if let Some(VertexAttributeValues::Float32x2(uvs)) = @@ -413,7 +413,7 @@ where } } - Mesh::new(PrimitiveTopology::TriangleList, front_face.asset_usage) + InfallibleMesh::new(PrimitiveTopology::TriangleList, front_face.asset_usage) .with_inserted_indices(Indices::U32(indices)) .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) diff --git a/crates/bevy_mesh/src/primitives/mod.rs b/crates/bevy_mesh/src/primitives/mod.rs index 02aa0f1caf41e..64f0ab16a1242 100644 --- a/crates/bevy_mesh/src/primitives/mod.rs +++ b/crates/bevy_mesh/src/primitives/mod.rs @@ -28,6 +28,8 @@ pub use dim3::*; mod extrusion; pub use extrusion::*; +use crate::InfallibleMesh; + use super::Mesh; /// A trait for shapes that can be turned into a [`Mesh`]. @@ -41,8 +43,13 @@ pub trait Meshable { /// A trait used to build [`Mesh`]es from a configuration pub trait MeshBuilder { + /// Builds an [`InfallibleMesh`] based on the configuration in `self`. + fn build_infallible(&self) -> InfallibleMesh; + /// Builds a [`Mesh`] based on the configuration in `self`. - fn build(&self) -> Mesh; + fn build(&self) -> Mesh { + self.build_infallible().into() + } } impl From for Mesh { @@ -50,3 +57,9 @@ impl From for Mesh { builder.build() } } + +impl From for InfallibleMesh { + fn from(builder: T) -> Self { + builder.build_infallible() + } +} diff --git a/crates/bevy_pbr/src/decal/forward.rs b/crates/bevy_pbr/src/decal/forward.rs index fa38e084dd7c7..d51263eb621f0 100644 --- a/crates/bevy_pbr/src/decal/forward.rs +++ b/crates/bevy_pbr/src/decal/forward.rs @@ -32,7 +32,7 @@ impl Plugin for ForwardDecalPlugin { let mesh = app.world_mut().resource_mut::>().add( Rectangle::from_size(Vec2::ONE) .mesh() - .build() + .build_infallible() .rotated_by(Quat::from_rotation_arc(Vec3::Z, Vec3::Y)) .with_generated_tangents() .unwrap(), diff --git a/crates/bevy_pbr/src/meshlet/from_mesh.rs b/crates/bevy_pbr/src/meshlet/from_mesh.rs index 23de97909404f..ab4632500b341 100644 --- a/crates/bevy_pbr/src/meshlet/from_mesh.rs +++ b/crates/bevy_pbr/src/meshlet/from_mesh.rs @@ -7,7 +7,7 @@ use bevy_math::{ ops::log2, IVec3, Isometry3d, Vec2, Vec3, Vec3A, Vec3Swizzles, }; -use bevy_mesh::{Indices, Mesh}; +use bevy_mesh::{Indices, Mesh, MeshAccessError}; use bevy_platform::collections::HashMap; use bevy_render::render_resource::PrimitiveTopology; use bevy_tasks::{AsyncComputeTaskPool, ParallelSlice}; @@ -251,19 +251,19 @@ fn validate_input_mesh(mesh: &Mesh) -> Result, MeshToMeshletMeshC return Err(MeshToMeshletMeshConversionError::WrongMeshPrimitiveTopology); } - if mesh.attributes().map(|(attribute, _)| attribute.id).ne([ + if mesh.attributes()?.map(|(attribute, _)| attribute.id).ne([ Mesh::ATTRIBUTE_POSITION.id, Mesh::ATTRIBUTE_NORMAL.id, Mesh::ATTRIBUTE_UV_0.id, ]) { return Err(MeshToMeshletMeshConversionError::WrongMeshVertexAttributes( - mesh.attributes() + mesh.attributes()? .map(|(attribute, _)| format!("{attribute:?}")) .collect(), )); } - match mesh.indices() { + match mesh.indices_option()? { Some(Indices::U32(indices)) => Ok(Cow::Borrowed(indices.as_slice())), Some(Indices::U16(indices)) => Ok(indices.iter().map(|i| *i as u32).collect()), _ => Err(MeshToMeshletMeshConversionError::MeshMissingIndices), @@ -1088,4 +1088,6 @@ pub enum MeshToMeshletMeshConversionError { WrongMeshVertexAttributes(Vec), #[error("Mesh has no indices")] MeshMissingIndices, + #[error("{0}")] + MeshAccessError(#[from] MeshAccessError), } diff --git a/crates/bevy_picking/src/mesh_picking/ray_cast/intersections.rs b/crates/bevy_picking/src/mesh_picking/ray_cast/intersections.rs index 24c6effab046c..8b13ef8994432 100644 --- a/crates/bevy_picking/src/mesh_picking/ray_cast/intersections.rs +++ b/crates/bevy_picking/src/mesh_picking/ray_cast/intersections.rs @@ -35,6 +35,8 @@ pub struct RayTriangleHit { pub barycentric_coords: (f32, f32), } +const MESH_EXTRACTED_ERROR: &str = "Cannot pick on extracted mesh, the mesh `asset_usage` must include `RenderAssetUsages::MAIN_WORLD`"; + /// Casts a ray on a mesh, and returns the intersection. pub(super) fn ray_intersection_over_mesh( mesh: &Mesh, @@ -46,21 +48,26 @@ pub(super) fn ray_intersection_over_mesh( return None; // ray_mesh_intersection assumes vertices are laid out in a triangle list } // Vertex positions are required - let positions = mesh.attribute(Mesh::ATTRIBUTE_POSITION)?.as_float3()?; + let positions = mesh + .attribute_option(Mesh::ATTRIBUTE_POSITION) + .expect(MESH_EXTRACTED_ERROR)? + .as_float3()?; // Normals are optional let normals = mesh - .attribute(Mesh::ATTRIBUTE_NORMAL) + .attribute_option(Mesh::ATTRIBUTE_NORMAL) + .expect("") .and_then(|normal_values| normal_values.as_float3()); let uvs = mesh - .attribute(Mesh::ATTRIBUTE_UV_0) + .attribute_option(Mesh::ATTRIBUTE_UV_0) + .expect(MESH_EXTRACTED_ERROR) .and_then(|uvs| match uvs { VertexAttributeValues::Float32x2(uvs) => Some(uvs.as_slice()), _ => None, }); - match mesh.indices() { + match mesh.indices_option().expect(MESH_EXTRACTED_ERROR) { Some(Indices::U16(indices)) => { ray_mesh_intersection(ray, transform, positions, normals, Some(indices), uvs, cull) } diff --git a/crates/bevy_render/src/mesh/allocator.rs b/crates/bevy_render/src/mesh/allocator.rs index 654114e758f22..73100f1ec7cd9 100644 --- a/crates/bevy_render/src/mesh/allocator.rs +++ b/crates/bevy_render/src/mesh/allocator.rs @@ -1029,7 +1029,10 @@ impl ElementLayout { /// Creates the appropriate [`ElementLayout`] for the given mesh's index /// data. fn index(mesh: &Mesh) -> Option { - let size = match mesh.indices()? { + let size = match mesh + .indices_option() + .expect("mesh data should always be available in render world")? + { Indices::U16(_) => 2, Indices::U32(_) => 4, }; diff --git a/crates/bevy_render/src/mesh/mod.rs b/crates/bevy_render/src/mesh/mod.rs index fb849cd51b3aa..33bc2a61a9bb8 100644 --- a/crates/bevy_render/src/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mod.rs @@ -144,7 +144,10 @@ impl RenderAsset for RenderMesh { fn byte_len(mesh: &Self::SourceAsset) -> Option { let mut vertex_size = 0; - for attribute_data in mesh.attributes() { + for attribute_data in mesh + .attributes() + .expect("mesh data should always be available in render world") + { let vertex_format = attribute_data.0.format; vertex_size += vertex_format.size() as usize; } @@ -163,21 +166,27 @@ impl RenderAsset for RenderMesh { ) -> Result> { #[cfg(feature = "morph")] let morph_targets = match mesh.morph_targets() { - Some(mt) => { + Ok(mt) => { let Some(target_image) = _images.get(mt) else { return Err(PrepareAssetError::RetryNextUpdate(mesh)); }; Some(target_image.texture_view.clone()) } - None => None, + Err(MeshAccessError::NotFound) => None, + Err(MeshAccessError::ExtractedToRenderWorld) => { + panic!("mesh data should always be available in render world") + } }; let buffer_info = match mesh.indices() { - Some(indices) => RenderMeshBufferInfo::Indexed { + Ok(indices) => RenderMeshBufferInfo::Indexed { count: indices.len() as u32, index_format: indices.into(), }, - None => RenderMeshBufferInfo::NonIndexed, + Err(MeshAccessError::NotFound) => RenderMeshBufferInfo::NonIndexed, + Err(MeshAccessError::ExtractedToRenderWorld) => { + panic!("mesh data should always be available in render world") + } }; let mesh_vertex_buffer_layout = @@ -185,7 +194,7 @@ impl RenderAsset for RenderMesh { let key_bits = BaseMeshPipelineKey::from_primitive_topology(mesh.primitive_topology()); #[cfg(feature = "morph")] - let key_bits = if mesh.morph_targets().is_some() { + let key_bits = if mesh.morph_targets().is_ok() { key_bits | BaseMeshPipelineKey::MORPH_TARGETS } else { key_bits diff --git a/crates/bevy_solari/src/scene/blas.rs b/crates/bevy_solari/src/scene/blas.rs index 58d6b7fd3abbb..a26078a91025a 100644 --- a/crates/bevy_solari/src/scene/blas.rs +++ b/crates/bevy_solari/src/scene/blas.rs @@ -172,12 +172,15 @@ fn allocate_blas( fn is_mesh_raytracing_compatible(mesh: &Mesh) -> bool { let triangle_list = mesh.primitive_topology() == PrimitiveTopology::TriangleList; - let vertex_attributes = mesh.attributes().map(|(attribute, _)| attribute.id).eq([ + let Ok(attributes) = mesh.attributes() else { + return false; + }; + let vertex_attributes = attributes.map(|(attribute, _)| attribute.id).eq([ Mesh::ATTRIBUTE_POSITION.id, Mesh::ATTRIBUTE_NORMAL.id, Mesh::ATTRIBUTE_UV_0.id, Mesh::ATTRIBUTE_TANGENT.id, ]); - let indexed_32 = matches!(mesh.indices(), Some(Indices::U32(..))); + let indexed_32 = matches!(mesh.indices(), Ok(Indices::U32(..))); mesh.enable_raytracing && triangle_list && vertex_attributes && indexed_32 } diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 79913288767a8..5b45daaca2cb6 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -10,7 +10,7 @@ use bevy::{ color::palettes::basic::YELLOW, core_pipeline::core_2d::{Transparent2d, CORE_2D_DEPTH_FORMAT}, math::{ops, FloatOrd}, - mesh::{Indices, MeshVertexAttribute, VertexBufferLayout}, + mesh::{Indices, InfallibleMesh, MeshVertexAttribute, VertexBufferLayout}, prelude::*, render::{ mesh::RenderMesh, @@ -58,7 +58,7 @@ fn star( // specified. We set `RenderAssetUsages::RENDER_WORLD`, meaning this mesh // will not be accessible in future frames from the `meshes` resource, in // order to save on memory once it has been uploaded to the GPU. - let mut star = Mesh::new( + let mut star = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::RENDER_WORLD, ); diff --git a/examples/2d/mesh2d_vertex_color_texture.rs b/examples/2d/mesh2d_vertex_color_texture.rs index bd6e8199ffb6d..fda92be3c97c3 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 [`Rectangle`] primitive, in a 2D scene. //! Adds a texture and colored vertices, giving per-vertex tinting. -use bevy::prelude::*; +use bevy::{mesh::InfallibleMesh, prelude::*}; fn main() { App::new() @@ -19,7 +19,7 @@ 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(Rectangle::default()); + let mut mesh = InfallibleMesh::from(Rectangle::default().mesh()); // Build vertex colors for the quad. One entry per vertex (the corners of the quad) let vertex_colors: Vec<[f32; 4]> = vec![ LinearRgba::RED.to_f32_array(), diff --git a/examples/3d/generate_custom_mesh.rs b/examples/3d/generate_custom_mesh.rs index 7b4219effbd61..de0452caf7ded 100644 --- a/examples/3d/generate_custom_mesh.rs +++ b/examples/3d/generate_custom_mesh.rs @@ -4,7 +4,7 @@ use bevy::{ asset::RenderAssetUsages, - mesh::{Indices, VertexAttributeValues}, + mesh::{Indices, InfallibleMesh, VertexAttributeValues}, prelude::*, render::render_resource::PrimitiveTopology, }; @@ -103,9 +103,9 @@ fn input_handler( } #[rustfmt::skip] -fn create_cube_mesh() -> Mesh { +fn create_cube_mesh() -> InfallibleMesh { // Keep the mesh data accessible in future frames to be able to mutate it in toggle_texture. - Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD) + InfallibleMesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD) .with_inserted_attribute( Mesh::ATTRIBUTE_POSITION, // Each array is an [x, y, z] coordinate in local space. diff --git a/examples/3d/lines.rs b/examples/3d/lines.rs index c29e9fb45c0a8..4c2e3fdf29a49 100644 --- a/examples/3d/lines.rs +++ b/examples/3d/lines.rs @@ -2,7 +2,7 @@ use bevy::{ asset::RenderAssetUsages, - mesh::{MeshVertexBufferLayoutRef, PrimitiveTopology}, + mesh::{InfallibleMesh, MeshVertexBufferLayoutRef, PrimitiveTopology}, pbr::{MaterialPipeline, MaterialPipelineKey}, prelude::*, reflect::TypePath, @@ -96,7 +96,7 @@ impl From for Mesh { fn from(line: LineList) -> Self { let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect(); - Mesh::new( + InfallibleMesh::new( // This tells wgpu that the positions are list of lines // where every pair is a start and end point PrimitiveTopology::LineList, @@ -104,6 +104,7 @@ impl From for Mesh { ) // Add the vertices positions as an attribute .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices) + .into() } } @@ -115,7 +116,7 @@ struct LineStrip { impl From for Mesh { fn from(line: LineStrip) -> Self { - Mesh::new( + InfallibleMesh::new( // This tells wgpu that the positions are a list of points // where a line will be drawn between each consecutive point PrimitiveTopology::LineStrip, @@ -123,5 +124,6 @@ impl From for Mesh { ) // Add the point positions as an attribute .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, line.points) + .into() } } diff --git a/examples/3d/motion_blur.rs b/examples/3d/motion_blur.rs index ef9349ee7b19d..50b9e9b2faf2c 100644 --- a/examples/3d/motion_blur.rs +++ b/examples/3d/motion_blur.rs @@ -4,6 +4,7 @@ use bevy::{ image::{ImageAddressMode, ImageFilterMode, ImageSampler, ImageSamplerDescriptor}, math::ops, + mesh::InfallibleMesh, post_process::motion_blur::MotionBlur, prelude::*, }; @@ -82,7 +83,7 @@ fn setup_scene( Transform::default().with_scale(Vec3::splat(-4000.0)), )); // Ground - let mut plane: Mesh = Plane3d::default().into(); + let mut plane: InfallibleMesh = Plane3d::default().mesh().into(); let uv_size = 4000.0; let uvs = vec![[uv_size, 0.0], [0.0, 0.0], [0.0, uv_size], [uv_size; 2]]; plane.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); diff --git a/examples/3d/solari.rs b/examples/3d/solari.rs index 6449b40d2bb66..e398332b9d8ca 100644 --- a/examples/3d/solari.rs +++ b/examples/3d/solari.rs @@ -179,19 +179,21 @@ fn add_raytracing_meshes_on_scene_load( // Ensure meshes are Solari compatible let mesh = meshes.get_mut(mesh_handle).unwrap(); - if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) { + if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0).unwrap() { let vertex_count = mesh.count_vertices(); - mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]) + .unwrap(); mesh.insert_attribute( Mesh::ATTRIBUTE_TANGENT, vec![[0.0, 0.0, 0.0, 0.0]; vertex_count], - ); + ) + .unwrap(); } - if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) { + if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT).unwrap() { mesh.generate_tangents().unwrap(); } - if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) { - mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1); + if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1).unwrap() { + let _ = mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1); } // Prevent rasterization if using pathtracer diff --git a/examples/3d/vertex_colors.rs b/examples/3d/vertex_colors.rs index 3a5c31f94a7bf..c16e0319fbee2 100644 --- a/examples/3d/vertex_colors.rs +++ b/examples/3d/vertex_colors.rs @@ -1,6 +1,9 @@ //! Illustrates the use of vertex colors. -use bevy::{mesh::VertexAttributeValues, prelude::*}; +use bevy::{ + mesh::{InfallibleMesh, VertexAttributeValues}, + prelude::*, +}; fn main() { App::new() @@ -22,7 +25,7 @@ fn setup( )); // cube // Assign vertex colors based on vertex positions - let mut colorful_cube = Mesh::from(Cuboid::default()); + let mut colorful_cube = InfallibleMesh::from(Cuboid::default().mesh()); if let Some(VertexAttributeValues::Float32x3(positions)) = colorful_cube.attribute(Mesh::ATTRIBUTE_POSITION) { diff --git a/examples/animation/custom_skinned_mesh.rs b/examples/animation/custom_skinned_mesh.rs index e45d21bbc2282..62ca5a7dfadba 100644 --- a/examples/animation/custom_skinned_mesh.rs +++ b/examples/animation/custom_skinned_mesh.rs @@ -8,7 +8,7 @@ use bevy::{ math::ops, mesh::{ skinning::{SkinnedMesh, SkinnedMeshInverseBindposes}, - Indices, PrimitiveTopology, VertexAttributeValues, + Indices, InfallibleMesh, PrimitiveTopology, VertexAttributeValues, }, prelude::*, }; @@ -54,7 +54,7 @@ fn setup( ]); // Create a mesh - let mesh = Mesh::new( + let mesh = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::RENDER_WORLD, ) diff --git a/examples/animation/morph_targets.rs b/examples/animation/morph_targets.rs index ffb468f26592c..675d8605434b2 100644 --- a/examples/animation/morph_targets.rs +++ b/examples/animation/morph_targets.rs @@ -86,7 +86,7 @@ fn name_morphs( if let AssetEvent::::Added { id } = event && let Some(path) = asset_server.get_path(*id) && let Some(mesh) = meshes.get(*id) - && let Some(names) = mesh.morph_target_names() + && let Some(names) = mesh.morph_target_names().unwrap() { info!("Morph target names for {path:?}:"); diff --git a/examples/asset/alter_mesh.rs b/examples/asset/alter_mesh.rs index d62178e10cbc5..92eb73572597f 100644 --- a/examples/asset/alter_mesh.rs +++ b/examples/asset/alter_mesh.rs @@ -188,7 +188,7 @@ fn alter_mesh( // `ATTRIBUTE_POSITION` is a constant indicating that we want to know where the vertex is // located in space (as opposed to which way its normal is facing, vertex color, or other // details). - if let Some(VertexAttributeValues::Float32x3(positions)) = + if let Ok(VertexAttributeValues::Float32x3(positions)) = mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION) { // Check a Local value (which only this system can make use of) to determine if we're diff --git a/examples/gltf/query_gltf_primitives.rs b/examples/gltf/query_gltf_primitives.rs index 892d087e2f24a..7e87811eff414 100644 --- a/examples/gltf/query_gltf_primitives.rs +++ b/examples/gltf/query_gltf_primitives.rs @@ -35,7 +35,7 @@ fn find_top_material_and_mesh( } if let Some(mesh) = meshes.get_mut(mesh_handle) - && let Some(VertexAttributeValues::Float32x3(positions)) = + && let Ok(VertexAttributeValues::Float32x3(positions)) = mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION) { for position in positions { diff --git a/examples/math/custom_primitives.rs b/examples/math/custom_primitives.rs index ba2976d7a8472..a24f033701000 100644 --- a/examples/math/custom_primitives.rs +++ b/examples/math/custom_primitives.rs @@ -17,7 +17,7 @@ use bevy::{ }, Isometry2d, }, - mesh::{Extrudable, ExtrusionBuilder, PerimeterSegment}, + mesh::{Extrudable, ExtrusionBuilder, InfallibleMesh, PerimeterSegment}, prelude::*, }; @@ -510,7 +510,7 @@ impl HeartBuilder for ExtrusionBuilder { impl MeshBuilder for HeartMeshBuilder { // This is where you should build the actual mesh. - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { let radius = self.heart.radius; // The curved parts of each wing (half) of the heart have an angle of `PI * 1.25` or 225° let wing_angle = PI * 1.25; @@ -553,7 +553,7 @@ impl MeshBuilder for HeartMeshBuilder { } // Here, the actual `Mesh` is created. We set the indices, vertices, normals and UVs created above and specify the topology of the mesh. - Mesh::new( + InfallibleMesh::new( bevy::mesh::PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) diff --git a/examples/math/sampling_primitives.rs b/examples/math/sampling_primitives.rs index b5b815f416064..75cbc77118d34 100644 --- a/examples/math/sampling_primitives.rs +++ b/examples/math/sampling_primitives.rs @@ -6,6 +6,7 @@ use bevy::{ core_pipeline::tonemapping::Tonemapping, input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButtonInput}, math::prelude::*, + mesh::InfallibleMesh, post_process::bloom::Bloom, prelude::*, }; @@ -206,7 +207,7 @@ impl Meshable for Shape { } impl MeshBuilder for ShapeMeshBuilder { - fn build(&self) -> Mesh { + fn build_infallible(&self) -> InfallibleMesh { match self.shape { Shape::Cuboid => CUBOID.mesh().into(), Shape::Sphere => SPHERE.mesh().into(), diff --git a/examples/shader_advanced/custom_vertex_attribute.rs b/examples/shader_advanced/custom_vertex_attribute.rs index 1741b08e39fea..95a645e389241 100644 --- a/examples/shader_advanced/custom_vertex_attribute.rs +++ b/examples/shader_advanced/custom_vertex_attribute.rs @@ -1,7 +1,7 @@ //! A shader that reads a mesh's custom vertex attribute. use bevy::{ - mesh::{MeshVertexAttribute, MeshVertexBufferLayoutRef}, + mesh::{InfallibleMesh, MeshVertexAttribute, MeshVertexBufferLayoutRef}, pbr::{MaterialPipeline, MaterialPipelineKey}, prelude::*, reflect::TypePath, @@ -32,7 +32,7 @@ fn setup( mut meshes: ResMut>, mut materials: ResMut>, ) { - let mesh = Mesh::from(Cuboid::default()) + let mesh = InfallibleMesh::from(Cuboid::default().mesh()) // Sets the custom attribute .with_inserted_attribute( ATTRIBUTE_BLEND_COLOR, diff --git a/examples/shader_advanced/specialized_mesh_pipeline.rs b/examples/shader_advanced/specialized_mesh_pipeline.rs index ee5abdb54d647..35b79d1b3d80e 100644 --- a/examples/shader_advanced/specialized_mesh_pipeline.rs +++ b/examples/shader_advanced/specialized_mesh_pipeline.rs @@ -12,7 +12,7 @@ use bevy::{ core_pipeline::core_3d::{Opaque3d, Opaque3dBatchSetKey, Opaque3dBinKey, CORE_3D_DEPTH_FORMAT}, ecs::change_detection::Tick, math::{vec3, vec4}, - mesh::{Indices, MeshVertexBufferLayoutRef, PrimitiveTopology}, + mesh::{Indices, InfallibleMesh, MeshVertexBufferLayoutRef, PrimitiveTopology}, pbr::{ DrawMesh, MeshPipeline, MeshPipelineKey, MeshPipelineViewLayoutKey, RenderMeshInstances, SetMeshBindGroup, SetMeshViewBindGroup, SetMeshViewEmptyBindGroup, @@ -53,7 +53,7 @@ fn setup(mut commands: Commands, mut meshes: ResMut>) { // Build a custom triangle mesh with colors // We define a custom mesh because the examples only uses a limited // set of vertex attributes for simplicity - let mesh = Mesh::new( + let mesh = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) diff --git a/examples/tools/scene_viewer/morph_viewer_plugin.rs b/examples/tools/scene_viewer/morph_viewer_plugin.rs index 404c07d479e32..cc70943dd3f1c 100644 --- a/examples/tools/scene_viewer/morph_viewer_plugin.rs +++ b/examples/tools/scene_viewer/morph_viewer_plugin.rs @@ -260,7 +260,7 @@ fn detect_morphs( let target_names = weights .first_mesh() .and_then(|h| meshes.get(h)) - .and_then(|m| m.morph_target_names()); + .and_then(|m| m.morph_target_names().unwrap_or_default()); let targets = Target::new(name, weights.weights(), target_names, entity); detected.extend(targets); } diff --git a/tests/3d/test_invalid_skinned_mesh.rs b/tests/3d/test_invalid_skinned_mesh.rs index f371a06b7130e..a2ba66cda7b6e 100644 --- a/tests/3d/test_invalid_skinned_mesh.rs +++ b/tests/3d/test_invalid_skinned_mesh.rs @@ -6,7 +6,7 @@ use bevy::{ math::ops, mesh::{ skinning::{SkinnedMesh, SkinnedMeshInverseBindposes}, - Indices, PrimitiveTopology, VertexAttributeValues, + Indices, InfallibleMesh, PrimitiveTopology, VertexAttributeValues, }, post_process::motion_blur::MotionBlur, prelude::*, @@ -96,7 +96,7 @@ fn setup_meshes( mut inverse_bindposes_assets: ResMut>, ) { // Create a mesh with two rectangles. - let unskinned_mesh = Mesh::new( + let unskinned_mesh = InfallibleMesh::new( PrimitiveTopology::TriangleList, RenderAssetUsages::default(), ) From fe6bf7f48216013c009324ebdc529a20f8d5fa2d Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Sun, 30 Nov 2025 21:28:21 +0000 Subject: [PATCH 2/5] fix doctest --- crates/bevy_mesh/src/mesh.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bevy_mesh/src/mesh.rs b/crates/bevy_mesh/src/mesh.rs index a1ce73ddeb943..65930d470f27b 100644 --- a/crates/bevy_mesh/src/mesh.rs +++ b/crates/bevy_mesh/src/mesh.rs @@ -750,11 +750,11 @@ impl core::ops::Deref for InfallibleMesh { /// `StandardMaterial` or `ColorMaterial`: /// /// ``` -/// # use bevy_mesh::{Mesh, Indices, PrimitiveTopology}; +/// # use bevy_mesh::{Mesh, InfallibleMesh, Indices, PrimitiveTopology}; /// # use bevy_asset::RenderAssetUsages; /// fn create_simple_parallelogram() -> Mesh { /// // Create a new mesh using a triangle list topology, where each set of 3 vertices composes a triangle. -/// Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default()) +/// InfallibleMesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default()) /// // Add 4 vertices, each with its own position attribute (coordinate in /// // 3D space), for each of the corners of the parallelogram. /// .with_inserted_attribute( @@ -779,6 +779,7 @@ impl core::ops::Deref for InfallibleMesh { /// // Second triangle /// 1, 3, 2 /// ])) +/// .into() /// } /// ``` /// From 8c6e6bb857f98e341348324e3a0866eafbff77f1 Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Sun, 30 Nov 2025 21:28:47 +0000 Subject: [PATCH 3/5] clean up From for Mesh --- crates/bevy_math/src/primitives/dim2.rs | 2 +- crates/bevy_mesh/src/primitives/dim2.rs | 146 +++--------------- .../bevy_mesh/src/primitives/dim3/capsule.rs | 10 +- crates/bevy_mesh/src/primitives/dim3/cone.rs | 10 +- .../src/primitives/dim3/conical_frustum.rs | 10 +- .../bevy_mesh/src/primitives/dim3/cuboid.rs | 8 +- .../bevy_mesh/src/primitives/dim3/cylinder.rs | 10 +- crates/bevy_mesh/src/primitives/dim3/plane.rs | 10 +- .../src/primitives/dim3/polyline3d.rs | 12 +- .../src/primitives/dim3/segment3d.rs | 10 +- .../bevy_mesh/src/primitives/dim3/sphere.rs | 10 +- .../src/primitives/dim3/tetrahedron.rs | 10 +- crates/bevy_mesh/src/primitives/dim3/torus.rs | 10 +- .../src/primitives/dim3/triangle3d.rs | 10 +- crates/bevy_mesh/src/primitives/extrusion.rs | 14 +- crates/bevy_mesh/src/primitives/mod.rs | 22 ++- examples/math/custom_primitives.rs | 4 +- examples/math/sampling_primitives.rs | 4 +- 18 files changed, 70 insertions(+), 242 deletions(-) diff --git a/crates/bevy_math/src/primitives/dim2.rs b/crates/bevy_math/src/primitives/dim2.rs index 572ff1473ce97..e0cc8f86da7f6 100644 --- a/crates/bevy_math/src/primitives/dim2.rs +++ b/crates/bevy_math/src/primitives/dim2.rs @@ -1956,7 +1956,7 @@ impl From for Polygon { )] pub struct ConvexPolygon { /// The vertices of the [`ConvexPolygon`]. - vertices: Vec, + pub vertices: Vec, } #[cfg(feature = "alloc")] diff --git a/crates/bevy_mesh/src/primitives/dim2.rs b/crates/bevy_mesh/src/primitives/dim2.rs index 960bf8b1231c8..cba9058820b0e 100644 --- a/crates/bevy_mesh/src/primitives/dim2.rs +++ b/crates/bevy_mesh/src/primitives/dim2.rs @@ -81,20 +81,14 @@ impl Extrudable for CircleMeshBuilder { impl Meshable for Circle { type Output = CircleMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { CircleMeshBuilder { - circle: *self, + circle: self, ..Default::default() } } } -impl From for Mesh { - fn from(circle: Circle) -> Self { - circle.mesh().build() - } -} - /// Specifies how to generate UV-mappings for the [`CircularSector`] and [`CircularSegment`] shapes. /// /// Currently the only variant is `Mask`, which is good for showing a portion of a texture that includes @@ -243,23 +237,14 @@ impl Extrudable for CircularSectorMeshBuilder { impl Meshable for CircularSector { type Output = CircularSectorMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { CircularSectorMeshBuilder { - sector: *self, + sector: self, ..Default::default() } } } -impl From for Mesh { - /// Converts this sector into a [`Mesh`] using a default [`CircularSectorMeshBuilder`]. - /// - /// See the documentation of [`CircularSectorMeshBuilder`] for more details. - fn from(sector: CircularSector) -> Self { - sector.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with a [`CircularSegment`] shape. /// /// The resulting mesh will have a UV-map such that the center of the circle is @@ -390,23 +375,14 @@ impl Extrudable for CircularSegmentMeshBuilder { impl Meshable for CircularSegment { type Output = CircularSegmentMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { CircularSegmentMeshBuilder { - segment: *self, + segment: self, ..Default::default() } } } -impl From for Mesh { - /// Converts this sector into a [`Mesh`] using a default [`CircularSegmentMeshBuilder`]. - /// - /// See the documentation of [`CircularSegmentMeshBuilder`] for more details. - fn from(segment: CircularSegment) -> Self { - segment.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with a [`ConvexPolygon`] shape. /// /// You must verify that the `vertices` are not concave when constructing this type. You can @@ -420,9 +396,9 @@ pub struct ConvexPolygonMeshBuilder { impl Meshable for ConvexPolygon { type Output = ConvexPolygonMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { Self::Output { - vertices: self.vertices().to_vec(), + vertices: self.vertices, } } } @@ -456,12 +432,6 @@ impl Extrudable for ConvexPolygonMeshBuilder { } } -impl From for Mesh { - fn from(polygon: ConvexPolygon) -> Self { - polygon.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with a [`RegularPolygon`] shape. #[derive(Clone, Copy, Debug, Reflect)] #[reflect(Default, Debug, Clone)] @@ -504,7 +474,7 @@ impl RegularPolygonMeshBuilder { impl Meshable for RegularPolygon { type Output = RegularPolygonMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { Self::Output { circumradius: self.circumcircle.radius, sides: self.sides, @@ -530,12 +500,6 @@ impl Extrudable for RegularPolygonMeshBuilder { } } -impl From for Mesh { - fn from(polygon: RegularPolygon) -> Self { - polygon.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with an [`Ellipse`] shape. #[derive(Clone, Copy, Debug, Reflect)] #[reflect(Default, Debug, Clone)] @@ -627,20 +591,14 @@ impl Extrudable for EllipseMeshBuilder { impl Meshable for Ellipse { type Output = EllipseMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { EllipseMeshBuilder { - ellipse: *self, + ellipse: self, ..Default::default() } } } -impl From for Mesh { - fn from(ellipse: Ellipse) -> Self { - ellipse.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with a [`Segment2d`]. pub struct Segment2dMeshBuilder { /// The [`Segment2d`] shape. @@ -669,15 +627,8 @@ impl MeshBuilder for Segment2dMeshBuilder { impl Meshable for Segment2d { type Output = Segment2dMeshBuilder; - fn mesh(&self) -> Self::Output { - Segment2dMeshBuilder::new(*self) - } -} - -impl From for Mesh { - /// Converts this segment into a [`Mesh`] using a default [`Segment2dMeshBuilder`]. - fn from(segment: Segment2d) -> Self { - segment.mesh().build() + fn mesh(self) -> Self::Output { + Segment2dMeshBuilder::new(self) } } @@ -712,16 +663,8 @@ impl MeshBuilder for Polyline2dMeshBuilder { impl Meshable for Polyline2d { type Output = Polyline2dMeshBuilder; - fn mesh(&self) -> Self::Output { - Polyline2dMeshBuilder { - polyline: self.clone(), - } - } -} - -impl From for Mesh { - fn from(polyline: Polyline2d) -> Self { - polyline.mesh().build() + fn mesh(self) -> Self::Output { + Polyline2dMeshBuilder { polyline: self } } } @@ -844,20 +787,14 @@ impl Extrudable for AnnulusMeshBuilder { impl Meshable for Annulus { type Output = AnnulusMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { AnnulusMeshBuilder { - annulus: *self, + annulus: self, ..Default::default() } } } -impl From for Mesh { - fn from(annulus: Annulus) -> Self { - annulus.mesh().build() - } -} - /// A builder for creating a [`Mesh`] with an [`Rhombus`] shape. #[derive(Clone, Copy, Debug, Reflect)] #[reflect(Default, Debug, Clone)] @@ -931,19 +868,13 @@ impl Extrudable for RhombusMeshBuilder { impl Meshable for Rhombus { type Output = RhombusMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { Self::Output { half_diagonals: self.half_diagonals, } } } -impl From for Mesh { - fn from(rhombus: Rhombus) -> Self { - rhombus.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with a [`Triangle2d`] shape. #[derive(Clone, Copy, Debug, Default, Reflect)] #[reflect(Default, Debug, Clone)] @@ -963,8 +894,8 @@ impl Triangle2dMeshBuilder { impl Meshable for Triangle2d { type Output = Triangle2dMeshBuilder; - fn mesh(&self) -> Self::Output { - Self::Output { triangle: *self } + fn mesh(self) -> Self::Output { + Self::Output { triangle: self } } } @@ -1015,12 +946,6 @@ impl Extrudable for Triangle2dMeshBuilder { } } -impl From for Mesh { - fn from(triangle: Triangle2d) -> Self { - triangle.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with a [`Rectangle`] shape. #[derive(Clone, Copy, Debug, Reflect)] #[reflect(Default, Debug, Clone)] @@ -1088,19 +1013,13 @@ impl Extrudable for RectangleMeshBuilder { impl Meshable for Rectangle { type Output = RectangleMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { RectangleMeshBuilder { half_size: self.half_size, } } } -impl From for Mesh { - fn from(rectangle: Rectangle) -> Self { - rectangle.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with a [`Capsule2d`] shape. #[derive(Clone, Copy, Debug, Reflect)] #[reflect(Default, Debug, Clone)] @@ -1248,20 +1167,14 @@ impl Extrudable for Capsule2dMeshBuilder { impl Meshable for Capsule2d { type Output = Capsule2dMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { Capsule2dMeshBuilder { - capsule: *self, + capsule: self, ..Default::default() } } } -impl From for Mesh { - fn from(capsule: Capsule2d) -> Self { - capsule.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with a [`Ring`] shape. pub struct RingMeshBuilder

where @@ -1276,7 +1189,7 @@ where P: Primitive2d + Meshable, { /// Create a new `RingMeshBuilder

` from a given `Ring

` shape. - pub fn new(ring: &Ring

) -> Self { + pub fn new(ring: Ring

) -> Self { Self { outer_shape_builder: ring.outer_shape.mesh(), inner_shape_builder: ring.inner_shape.mesh(), @@ -1511,20 +1424,11 @@ where { type Output = RingMeshBuilder

; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { RingMeshBuilder::new(self) } } -impl

From> for Mesh -where - P: Primitive2d + Meshable, -{ - fn from(ring: Ring

) -> Self { - ring.mesh().build() - } -} - #[cfg(test)] mod tests { use bevy_math::{prelude::Annulus, primitives::RegularPolygon, FloatOrd}; diff --git a/crates/bevy_mesh/src/primitives/dim3/capsule.rs b/crates/bevy_mesh/src/primitives/dim3/capsule.rs index 64f5ae86ee6f3..13b937f05ca8e 100644 --- a/crates/bevy_mesh/src/primitives/dim3/capsule.rs +++ b/crates/bevy_mesh/src/primitives/dim3/capsule.rs @@ -421,16 +421,10 @@ impl MeshBuilder for Capsule3dMeshBuilder { impl Meshable for Capsule3d { type Output = Capsule3dMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { Capsule3dMeshBuilder { - capsule: *self, + capsule: self, ..Default::default() } } } - -impl From for Mesh { - fn from(capsule: Capsule3d) -> Self { - capsule.mesh().build() - } -} diff --git a/crates/bevy_mesh/src/primitives/dim3/cone.rs b/crates/bevy_mesh/src/primitives/dim3/cone.rs index b773f05a71e74..7f4551768dd51 100644 --- a/crates/bevy_mesh/src/primitives/dim3/cone.rs +++ b/crates/bevy_mesh/src/primitives/dim3/cone.rs @@ -174,20 +174,14 @@ impl MeshBuilder for ConeMeshBuilder { impl Meshable for Cone { type Output = ConeMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { ConeMeshBuilder { - cone: *self, + cone: self, ..Default::default() } } } -impl From for Mesh { - fn from(cone: Cone) -> Self { - cone.mesh().build() - } -} - #[cfg(test)] mod tests { use crate::{Mesh, MeshBuilder, Meshable, VertexAttributeValues}; diff --git a/crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs b/crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs index a0ca1fe9519ec..f6821bf638133 100644 --- a/crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs +++ b/crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs @@ -169,16 +169,10 @@ impl MeshBuilder for ConicalFrustumMeshBuilder { impl Meshable for ConicalFrustum { type Output = ConicalFrustumMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { ConicalFrustumMeshBuilder { - frustum: *self, + frustum: self, ..Default::default() } } } - -impl From for Mesh { - fn from(frustum: ConicalFrustum) -> Self { - frustum.mesh().build() - } -} diff --git a/crates/bevy_mesh/src/primitives/dim3/cuboid.rs b/crates/bevy_mesh/src/primitives/dim3/cuboid.rs index 7edd0c3d71945..de0cc0bcd93f7 100644 --- a/crates/bevy_mesh/src/primitives/dim3/cuboid.rs +++ b/crates/bevy_mesh/src/primitives/dim3/cuboid.rs @@ -85,15 +85,9 @@ impl MeshBuilder for CuboidMeshBuilder { impl Meshable for Cuboid { type Output = CuboidMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { CuboidMeshBuilder { half_size: self.half_size, } } } - -impl From for Mesh { - fn from(cuboid: Cuboid) -> Self { - cuboid.mesh().build() - } -} diff --git a/crates/bevy_mesh/src/primitives/dim3/cylinder.rs b/crates/bevy_mesh/src/primitives/dim3/cylinder.rs index dc8956243f5c5..575978ad313c4 100644 --- a/crates/bevy_mesh/src/primitives/dim3/cylinder.rs +++ b/crates/bevy_mesh/src/primitives/dim3/cylinder.rs @@ -207,16 +207,10 @@ impl MeshBuilder for CylinderMeshBuilder { impl Meshable for Cylinder { type Output = CylinderMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { CylinderMeshBuilder { - cylinder: *self, + cylinder: self, ..Default::default() } } } - -impl From for Mesh { - fn from(cylinder: Cylinder) -> Self { - cylinder.mesh().build() - } -} diff --git a/crates/bevy_mesh/src/primitives/dim3/plane.rs b/crates/bevy_mesh/src/primitives/dim3/plane.rs index 3b174f5bb48f8..d696123774344 100644 --- a/crates/bevy_mesh/src/primitives/dim3/plane.rs +++ b/crates/bevy_mesh/src/primitives/dim3/plane.rs @@ -145,16 +145,10 @@ impl MeshBuilder for PlaneMeshBuilder { impl Meshable for Plane3d { type Output = PlaneMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { PlaneMeshBuilder { - plane: *self, + plane: self, subdivisions: 0, } } } - -impl From for Mesh { - fn from(plane: Plane3d) -> Self { - plane.mesh().build() - } -} diff --git a/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs b/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs index f0e8ae42b0c54..7690ce1a9e94e 100644 --- a/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs +++ b/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs @@ -29,15 +29,7 @@ impl MeshBuilder for Polyline3dMeshBuilder { impl Meshable for Polyline3d { type Output = Polyline3dMeshBuilder; - fn mesh(&self) -> Self::Output { - Polyline3dMeshBuilder { - polyline: self.clone(), - } - } -} - -impl From for Mesh { - fn from(polyline: Polyline3d) -> Self { - polyline.mesh().build() + fn mesh(self) -> Self::Output { + Polyline3dMeshBuilder { polyline: self } } } diff --git a/crates/bevy_mesh/src/primitives/dim3/segment3d.rs b/crates/bevy_mesh/src/primitives/dim3/segment3d.rs index 4326955cb2324..60927ba7245b6 100644 --- a/crates/bevy_mesh/src/primitives/dim3/segment3d.rs +++ b/crates/bevy_mesh/src/primitives/dim3/segment3d.rs @@ -24,14 +24,8 @@ impl MeshBuilder for Segment3dMeshBuilder { impl Meshable for Segment3d { type Output = Segment3dMeshBuilder; - fn mesh(&self) -> Self::Output { - Segment3dMeshBuilder { segment: *self } - } -} - -impl From for Mesh { - fn from(segment: Segment3d) -> Self { - segment.mesh().build() + fn mesh(self) -> Self::Output { + Segment3dMeshBuilder { segment: self } } } diff --git a/crates/bevy_mesh/src/primitives/dim3/sphere.rs b/crates/bevy_mesh/src/primitives/dim3/sphere.rs index 946b850430262..3be960c6a0ed9 100644 --- a/crates/bevy_mesh/src/primitives/dim3/sphere.rs +++ b/crates/bevy_mesh/src/primitives/dim3/sphere.rs @@ -249,16 +249,10 @@ impl MeshBuilder for SphereMeshBuilder { impl Meshable for Sphere { type Output = SphereMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { SphereMeshBuilder { - sphere: *self, + sphere: self, ..Default::default() } } } - -impl From for Mesh { - fn from(sphere: Sphere) -> Self { - sphere.mesh().build() - } -} diff --git a/crates/bevy_mesh/src/primitives/dim3/tetrahedron.rs b/crates/bevy_mesh/src/primitives/dim3/tetrahedron.rs index 432aa4337c4a5..265223088ab88 100644 --- a/crates/bevy_mesh/src/primitives/dim3/tetrahedron.rs +++ b/crates/bevy_mesh/src/primitives/dim3/tetrahedron.rs @@ -54,13 +54,7 @@ impl MeshBuilder for TetrahedronMeshBuilder { impl Meshable for Tetrahedron { type Output = TetrahedronMeshBuilder; - fn mesh(&self) -> Self::Output { - TetrahedronMeshBuilder { tetrahedron: *self } - } -} - -impl From for Mesh { - fn from(tetrahedron: Tetrahedron) -> Self { - tetrahedron.mesh().build() + fn mesh(self) -> Self::Output { + TetrahedronMeshBuilder { tetrahedron: self } } } diff --git a/crates/bevy_mesh/src/primitives/dim3/torus.rs b/crates/bevy_mesh/src/primitives/dim3/torus.rs index d2267774c764a..8da04cc2b36a5 100644 --- a/crates/bevy_mesh/src/primitives/dim3/torus.rs +++ b/crates/bevy_mesh/src/primitives/dim3/torus.rs @@ -161,16 +161,10 @@ impl MeshBuilder for TorusMeshBuilder { impl Meshable for Torus { type Output = TorusMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { TorusMeshBuilder { - torus: *self, + torus: self, ..Default::default() } } } - -impl From for Mesh { - fn from(torus: Torus) -> Self { - torus.mesh().build() - } -} diff --git a/crates/bevy_mesh/src/primitives/dim3/triangle3d.rs b/crates/bevy_mesh/src/primitives/dim3/triangle3d.rs index 2337feb512ecd..64c941cb68fdc 100644 --- a/crates/bevy_mesh/src/primitives/dim3/triangle3d.rs +++ b/crates/bevy_mesh/src/primitives/dim3/triangle3d.rs @@ -35,8 +35,8 @@ impl MeshBuilder for Triangle3dMeshBuilder { impl Meshable for Triangle3d { type Output = Triangle3dMeshBuilder; - fn mesh(&self) -> Self::Output { - Triangle3dMeshBuilder { triangle: *self } + fn mesh(self) -> Self::Output { + Triangle3dMeshBuilder { triangle: self } } } @@ -94,12 +94,6 @@ pub(crate) fn uv_coords(triangle: &Triangle3d) -> [[f32; 2]; 3] { } } -impl From for Mesh { - fn from(triangle: Triangle3d) -> Self { - triangle.mesh().build() - } -} - #[cfg(test)] mod tests { use super::uv_coords; diff --git a/crates/bevy_mesh/src/primitives/extrusion.rs b/crates/bevy_mesh/src/primitives/extrusion.rs index 2670805080904..3dc9a4593923e 100644 --- a/crates/bevy_mesh/src/primitives/extrusion.rs +++ b/crates/bevy_mesh/src/primitives/extrusion.rs @@ -92,7 +92,7 @@ where { type Output = ExtrusionBuilder

; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { ExtrusionBuilder { base_builder: self.base_shape.mesh(), half_depth: self.half_depth, @@ -118,7 +118,7 @@ where P::Output: Extrudable, { /// Create a new `ExtrusionBuilder

` from a given `base_shape` and the full `depth` of the extrusion. - pub fn new(base_shape: &P, depth: f32) -> Self { + pub fn new(base_shape: P, depth: f32) -> Self { Self { base_builder: base_shape.mesh(), half_depth: depth / 2., @@ -425,13 +425,3 @@ where front_face } } - -impl

From> for Mesh -where - P: Primitive2d + Meshable, - P::Output: Extrudable, -{ - fn from(value: Extrusion

) -> Self { - value.mesh().build() - } -} diff --git a/crates/bevy_mesh/src/primitives/mod.rs b/crates/bevy_mesh/src/primitives/mod.rs index 64f0ab16a1242..74655ae5087bd 100644 --- a/crates/bevy_mesh/src/primitives/mod.rs +++ b/crates/bevy_mesh/src/primitives/mod.rs @@ -38,7 +38,7 @@ pub trait Meshable { type Output: MeshBuilder; /// Creates a [`Mesh`] for a shape. - fn mesh(&self) -> Self::Output; + fn mesh(self) -> Self::Output; } /// A trait used to build [`Mesh`]es from a configuration @@ -52,14 +52,22 @@ pub trait MeshBuilder { } } -impl From for Mesh { - fn from(builder: T) -> Self { - builder.build() +impl Meshable for T { + type Output = Self; + + fn mesh(self) -> Self::Output { + self + } +} + +impl From for Mesh { + fn from(meshable: T) -> Self { + meshable.mesh().build() } } -impl From for InfallibleMesh { - fn from(builder: T) -> Self { - builder.build_infallible() +impl From for InfallibleMesh { + fn from(meshable: T) -> Self { + meshable.mesh().build_infallible() } } diff --git a/examples/math/custom_primitives.rs b/examples/math/custom_primitives.rs index a24f033701000..2315c054041df 100644 --- a/examples/math/custom_primitives.rs +++ b/examples/math/custom_primitives.rs @@ -472,9 +472,9 @@ impl Meshable for Heart { // The `MeshBuilder` can be used to create the actual mesh for that primitive. type Output = HeartMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh(self) -> Self::Output { Self::Output { - heart: *self, + heart: self, resolution: 32, } } diff --git a/examples/math/sampling_primitives.rs b/examples/math/sampling_primitives.rs index 75cbc77118d34..75bb370a81116 100644 --- a/examples/math/sampling_primitives.rs +++ b/examples/math/sampling_primitives.rs @@ -201,8 +201,8 @@ impl ShapeSample for Shape { impl Meshable for Shape { type Output = ShapeMeshBuilder; - fn mesh(&self) -> Self::Output { - ShapeMeshBuilder { shape: *self } + fn mesh(self) -> Self::Output { + ShapeMeshBuilder { shape: self } } } From ab9a52aa63bbb65d3520a9abb790f7de613dd741 Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Sun, 7 Dec 2025 13:03:06 +0000 Subject: [PATCH 4/5] remove Asset derive --- crates/bevy_mesh/src/mesh.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_mesh/src/mesh.rs b/crates/bevy_mesh/src/mesh.rs index 65930d470f27b..49937912b3755 100644 --- a/crates/bevy_mesh/src/mesh.rs +++ b/crates/bevy_mesh/src/mesh.rs @@ -137,7 +137,7 @@ impl From> for MeshExtractableData { /// that can be converted into a normal `Mesh` using `InfallibleMesh::into_mesh`. /// Designed for use during cpu-side mesh construction. Methods on this struct whose `Mesh` /// counterparts may return `MeshAccessError::ExtractedToRenderWorld` instead return plain values. -#[derive(Asset, Debug, Clone, Reflect, PartialEq)] +#[derive(Debug, Clone, Reflect, PartialEq)] #[reflect(Clone)] pub struct InfallibleMesh(Mesh); From 7d6aae4c885039c18e9ec3ff214d8a795ff18c03 Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Tue, 16 Dec 2025 00:06:36 +0000 Subject: [PATCH 5/5] fix new example --- examples/3d/clustered_decal_maps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/3d/clustered_decal_maps.rs b/examples/3d/clustered_decal_maps.rs index 0f88747fd1fd9..31b07d58fb84c 100644 --- a/examples/3d/clustered_decal_maps.rs +++ b/examples/3d/clustered_decal_maps.rs @@ -197,7 +197,7 @@ fn spawn_plane_mesh( half_size: Vec2::splat(PLANE_HALF_SIZE), } .mesh() - .build() + .build_infallible() .with_duplicated_vertices() .with_computed_flat_normals() .with_generated_tangents()