use bevy_transform::components::Transform;1pub use wgpu_types::PrimitiveTopology;23use super::{4skinning::{SkinnedMeshBounds, SkinnedMeshBoundsError},5triangle_area_normal, triangle_normal, FourIterators, Indices, MeshAttributeData,6MeshTrianglesError, MeshVertexAttribute, MeshVertexAttributeId, MeshVertexBufferLayout,7MeshVertexBufferLayoutRef, MeshVertexBufferLayouts, MeshWindingInvertError,8VertexAttributeValues, VertexBufferLayout,9};10#[cfg(feature = "serialize")]11use crate::SerializedMeshAttributeData;12use alloc::collections::BTreeMap;13#[cfg(feature = "morph")]14use bevy_asset::Handle;15use bevy_asset::{Asset, RenderAssetUsages};16#[cfg(feature = "morph")]17use bevy_image::Image;18use bevy_math::{bounding::Aabb3d, primitives::Triangle3d, *};19use bevy_platform::collections::{hash_map, HashMap};20use bevy_reflect::{std_traits::ReflectDefault, Reflect};21use bytemuck::cast_slice;22use core::hash::{Hash, Hasher};23use core::ptr;24#[cfg(feature = "serialize")]25use serde::{Deserialize, Serialize};26use thiserror::Error;27use tracing::warn;28use wgpu_types::{VertexAttribute, VertexFormat, VertexStepMode};2930pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;31pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;3233/// Error from accessing mesh vertex attributes or indices34#[derive(Error, Debug, Clone)]35pub enum MeshAccessError {36#[error("The mesh vertex/index data has been extracted to the RenderWorld (via `Mesh::asset_usage`)")]37ExtractedToRenderWorld,38#[error("The requested mesh data wasn't found in this mesh")]39NotFound,40}4142const MESH_EXTRACTED_ERROR: &str = "Mesh has been extracted to RenderWorld. To access vertex attributes, the mesh `asset_usage` must include `MAIN_WORLD`";4344// storage for extractable data with access methods which return errors if the45// contents have already been extracted46#[derive(Debug, Clone, PartialEq, Reflect, Default)]47enum MeshExtractableData<T> {48Data(T),49#[default]50NoData,51ExtractedToRenderWorld,52}5354impl<T> MeshExtractableData<T> {55// get a reference to internal data. returns error if data has been extracted, or if no56// data exists57fn as_ref(&self) -> Result<&T, MeshAccessError> {58match self {59MeshExtractableData::Data(data) => Ok(data),60MeshExtractableData::NoData => Err(MeshAccessError::NotFound),61MeshExtractableData::ExtractedToRenderWorld => {62Err(MeshAccessError::ExtractedToRenderWorld)63}64}65}6667// get an optional reference to internal data. returns error if data has been extracted68fn as_ref_option(&self) -> Result<Option<&T>, MeshAccessError> {69match self {70MeshExtractableData::Data(data) => Ok(Some(data)),71MeshExtractableData::NoData => Ok(None),72MeshExtractableData::ExtractedToRenderWorld => {73Err(MeshAccessError::ExtractedToRenderWorld)74}75}76}7778// get a mutable reference to internal data. returns error if data has been extracted,79// or if no data exists80fn as_mut(&mut self) -> Result<&mut T, MeshAccessError> {81match self {82MeshExtractableData::Data(data) => Ok(data),83MeshExtractableData::NoData => Err(MeshAccessError::NotFound),84MeshExtractableData::ExtractedToRenderWorld => {85Err(MeshAccessError::ExtractedToRenderWorld)86}87}88}8990// get an optional mutable reference to internal data. returns error if data has been extracted91fn as_mut_option(&mut self) -> Result<Option<&mut T>, MeshAccessError> {92match self {93MeshExtractableData::Data(data) => Ok(Some(data)),94MeshExtractableData::NoData => Ok(None),95MeshExtractableData::ExtractedToRenderWorld => {96Err(MeshAccessError::ExtractedToRenderWorld)97}98}99}100101// extract data and replace self with `ExtractedToRenderWorld`. returns error if102// data has been extracted103fn extract(&mut self) -> Result<MeshExtractableData<T>, MeshAccessError> {104match core::mem::replace(self, MeshExtractableData::ExtractedToRenderWorld) {105MeshExtractableData::ExtractedToRenderWorld => {106Err(MeshAccessError::ExtractedToRenderWorld)107}108not_extracted => Ok(not_extracted),109}110}111112// replace internal data. returns the existing data, or an error if data has been extracted113fn replace(114&mut self,115data: impl Into<MeshExtractableData<T>>,116) -> Result<Option<T>, MeshAccessError> {117match core::mem::replace(self, data.into()) {118MeshExtractableData::ExtractedToRenderWorld => {119*self = MeshExtractableData::ExtractedToRenderWorld;120Err(MeshAccessError::ExtractedToRenderWorld)121}122MeshExtractableData::Data(t) => Ok(Some(t)),123MeshExtractableData::NoData => Ok(None),124}125}126}127128impl<T> From<Option<T>> for MeshExtractableData<T> {129fn from(value: Option<T>) -> Self {130match value {131Some(data) => MeshExtractableData::Data(data),132None => MeshExtractableData::NoData,133}134}135}136137/// A 3D object made out of vertices representing triangles, lines, or points,138/// with "attribute" values for each vertex.139///140/// Meshes can be automatically generated by a bevy `AssetLoader` (generally by loading a `Gltf` file),141/// or by converting a [primitive](bevy_math::primitives) using [`into`](Into).142/// It is also possible to create one manually. They can be edited after creation.143///144/// Meshes can be rendered with a [`Mesh2d`](crate::Mesh2d) and `MeshMaterial2d`145/// or [`Mesh3d`](crate::Mesh3d) and `MeshMaterial3d` for 2D and 3D respectively.146///147/// A [`Mesh`] in Bevy is equivalent to a "primitive" in the glTF format, for a148/// glTF Mesh representation, see `GltfMesh`.149///150/// ## Manual creation151///152/// The following function will construct a flat mesh, to be rendered with a153/// `StandardMaterial` or `ColorMaterial`:154///155/// ```156/// # use bevy_mesh::{Mesh, Indices, PrimitiveTopology};157/// # use bevy_asset::RenderAssetUsages;158/// fn create_simple_parallelogram() -> Mesh {159/// // Create a new mesh using a triangle list topology, where each set of 3 vertices composes a triangle.160/// Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default())161/// // Add 4 vertices, each with its own position attribute (coordinate in162/// // 3D space), for each of the corners of the parallelogram.163/// .with_inserted_attribute(164/// Mesh::ATTRIBUTE_POSITION,165/// vec![[0.0, 0.0, 0.0], [1.0, 2.0, 0.0], [2.0, 2.0, 0.0], [1.0, 0.0, 0.0]]166/// )167/// // Assign a UV coordinate to each vertex.168/// .with_inserted_attribute(169/// Mesh::ATTRIBUTE_UV_0,170/// vec![[0.0, 1.0], [0.5, 0.0], [1.0, 0.0], [0.5, 1.0]]171/// )172/// // Assign normals (everything points outwards)173/// .with_inserted_attribute(174/// Mesh::ATTRIBUTE_NORMAL,175/// vec![[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]]176/// )177/// // After defining all the vertices and their attributes, build each triangle using the178/// // indices of the vertices that make it up in a counter-clockwise order.179/// .with_inserted_indices(Indices::U32(vec![180/// // First triangle181/// 0, 3, 1,182/// // Second triangle183/// 1, 3, 2184/// ]))185/// }186/// ```187///188/// You can see how it looks like [here](https://github.com/bevyengine/bevy/blob/main/assets/docs/Mesh.png),189/// used in a [`Mesh3d`](crate::Mesh3d) with a square bevy logo texture, with added axis, points,190/// lines and text for clarity.191///192/// ## Other examples193///194/// For further visualization, explanation, and examples, see the built-in Bevy examples,195/// and the [implementation of the built-in shapes](https://github.com/bevyengine/bevy/tree/main/crates/bevy_mesh/src/primitives).196/// In particular, [generate_custom_mesh](https://github.com/bevyengine/bevy/blob/main/examples/3d/generate_custom_mesh.rs)197/// teaches you to access and modify the attributes of a [`Mesh`] after creating it.198///199/// ## Common points of confusion200///201/// - UV maps in Bevy start at the top-left, see [`ATTRIBUTE_UV_0`](Mesh::ATTRIBUTE_UV_0),202/// other APIs can have other conventions, `OpenGL` starts at bottom-left.203/// - It is possible and sometimes useful for multiple vertices to have the same204/// [position attribute](Mesh::ATTRIBUTE_POSITION) value,205/// it's a common technique in 3D modeling for complex UV mapping or other calculations.206/// - Bevy performs frustum culling based on the `Aabb` of meshes, which is calculated207/// and added automatically for new meshes only. If a mesh is modified, the entity's `Aabb`208/// needs to be updated manually or deleted so that it is re-calculated.209///210/// ## Use with `StandardMaterial`211///212/// To render correctly with `StandardMaterial`, a mesh needs to have properly defined:213/// - [`UVs`](Mesh::ATTRIBUTE_UV_0): Bevy needs to know how to map a texture onto the mesh214/// (also true for `ColorMaterial`).215/// - [`Normals`](Mesh::ATTRIBUTE_NORMAL): Bevy needs to know how light interacts with your mesh.216/// [0.0, 0.0, 1.0] is very common for simple flat meshes on the XY plane,217/// because simple meshes are smooth and they don't require complex light calculations.218/// - Vertex winding order: by default, `StandardMaterial.cull_mode` is `Some(Face::Back)`,219/// which means that Bevy would *only* render the "front" of each triangle, which220/// is the side of the triangle from where the vertices appear in a *counter-clockwise* order.221///222/// ## Remote Inspection223///224/// To transmit a [`Mesh`] between two running Bevy apps, e.g. through BRP, use [`SerializedMesh`].225/// This type is only meant for short-term transmission between same versions and should not be stored anywhere.226#[derive(Asset, Debug, Clone, Reflect, PartialEq)]227#[reflect(Clone)]228pub struct Mesh {229#[reflect(ignore, clone)]230primitive_topology: PrimitiveTopology,231/// `std::collections::BTreeMap` with all defined vertex attributes (Positions, Normals, ...)232/// for this mesh. Attribute ids to attribute values.233/// Uses a [`BTreeMap`] because, unlike `HashMap`, it has a defined iteration order,234/// which allows easy stable `VertexBuffers` (i.e. same buffer order)235#[reflect(ignore, clone)]236attributes: MeshExtractableData<BTreeMap<MeshVertexAttributeId, MeshAttributeData>>,237indices: MeshExtractableData<Indices>,238#[cfg(feature = "morph")]239morph_targets: MeshExtractableData<Handle<Image>>,240#[cfg(feature = "morph")]241morph_target_names: MeshExtractableData<Vec<String>>,242pub asset_usage: RenderAssetUsages,243/// Whether or not to build a BLAS for use with `bevy_solari` raytracing.244///245/// Note that this is _not_ whether the mesh is _compatible_ with `bevy_solari` raytracing.246/// This field just controls whether or not a BLAS gets built for this mesh, assuming that247/// the mesh is compatible.248///249/// The use case for this field is using lower-resolution proxy meshes for raytracing (to save on BLAS memory usage),250/// while using higher-resolution meshes for raster. You can set this field to true for the lower-resolution proxy mesh,251/// and to false for the high-resolution raster mesh.252///253/// Alternatively, you can use the same mesh for both raster and raytracing, with this field set to true.254///255/// Does nothing if not used with `bevy_solari`, or if the mesh is not compatible256/// with `bevy_solari` (see `bevy_solari`'s docs).257pub enable_raytracing: bool,258/// Precomputed min and max extents of the mesh position data. Used mainly for constructing `Aabb`s for frustum culling.259/// This data will be set if/when a mesh is extracted to the GPU260pub final_aabb: Option<Aabb3d>,261skinned_mesh_bounds: Option<SkinnedMeshBounds>,262}263264impl Mesh {265/// Where the vertex is located in space. Use in conjunction with [`Mesh::insert_attribute`]266/// or [`Mesh::with_inserted_attribute`].267///268/// The format of this attribute is [`VertexFormat::Float32x3`].269pub const ATTRIBUTE_POSITION: MeshVertexAttribute =270MeshVertexAttribute::new("Vertex_Position", 0, VertexFormat::Float32x3);271272/// The direction the vertex normal is facing in.273/// Use in conjunction with [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`].274///275/// The format of this attribute is [`VertexFormat::Float32x3`].276pub const ATTRIBUTE_NORMAL: MeshVertexAttribute =277MeshVertexAttribute::new("Vertex_Normal", 1, VertexFormat::Float32x3);278279/// Texture coordinates for the vertex. Use in conjunction with [`Mesh::insert_attribute`]280/// or [`Mesh::with_inserted_attribute`].281///282/// Generally `[0.,0.]` is mapped to the top left of the texture, and `[1.,1.]` to the bottom-right.283///284/// By default values outside will be clamped per pixel not for the vertex,285/// "stretching" the borders of the texture.286/// This behavior can be useful in some cases, usually when the borders have only287/// one color, for example a logo, and you want to "extend" those borders.288///289/// For different mapping outside of `0..=1` range,290/// see [`ImageAddressMode`](bevy_image::ImageAddressMode).291///292/// The format of this attribute is [`VertexFormat::Float32x2`].293pub const ATTRIBUTE_UV_0: MeshVertexAttribute =294MeshVertexAttribute::new("Vertex_Uv", 2, VertexFormat::Float32x2);295296/// Alternate texture coordinates for the vertex. Use in conjunction with297/// [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`].298///299/// Typically, these are used for lightmaps, textures that provide300/// precomputed illumination.301///302/// The format of this attribute is [`VertexFormat::Float32x2`].303pub const ATTRIBUTE_UV_1: MeshVertexAttribute =304MeshVertexAttribute::new("Vertex_Uv_1", 3, VertexFormat::Float32x2);305306/// The direction of the vertex tangent. Used for normal mapping.307/// Usually generated with [`generate_tangents`](Mesh::generate_tangents) or308/// [`with_generated_tangents`](Mesh::with_generated_tangents).309///310/// The format of this attribute is [`VertexFormat::Float32x4`].311pub const ATTRIBUTE_TANGENT: MeshVertexAttribute =312MeshVertexAttribute::new("Vertex_Tangent", 4, VertexFormat::Float32x4);313314/// Per vertex coloring. Use in conjunction with [`Mesh::insert_attribute`]315/// or [`Mesh::with_inserted_attribute`].316///317/// The format of this attribute is [`VertexFormat::Float32x4`].318pub const ATTRIBUTE_COLOR: MeshVertexAttribute =319MeshVertexAttribute::new("Vertex_Color", 5, VertexFormat::Float32x4);320321/// Per vertex joint transform matrix weight. Use in conjunction with [`Mesh::insert_attribute`]322/// or [`Mesh::with_inserted_attribute`].323///324/// The format of this attribute is [`VertexFormat::Float32x4`].325pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute =326MeshVertexAttribute::new("Vertex_JointWeight", 6, VertexFormat::Float32x4);327328/// Per vertex joint transform matrix index. Use in conjunction with [`Mesh::insert_attribute`]329/// or [`Mesh::with_inserted_attribute`].330///331/// The format of this attribute is [`VertexFormat::Uint16x4`].332pub const ATTRIBUTE_JOINT_INDEX: MeshVertexAttribute =333MeshVertexAttribute::new("Vertex_JointIndex", 7, VertexFormat::Uint16x4);334335/// The first index that can be used for custom vertex attributes.336/// Only the attributes with an index below this are used by Bevy.337pub const FIRST_AVAILABLE_CUSTOM_ATTRIBUTE: u64 = 8;338339/// Construct a new mesh. You need to provide a [`PrimitiveTopology`] so that the340/// renderer knows how to treat the vertex data. Most of the time this will be341/// [`PrimitiveTopology::TriangleList`].342pub fn new(primitive_topology: PrimitiveTopology, asset_usage: RenderAssetUsages) -> Self {343Mesh {344primitive_topology,345attributes: MeshExtractableData::Data(Default::default()),346indices: MeshExtractableData::NoData,347#[cfg(feature = "morph")]348morph_targets: MeshExtractableData::NoData,349#[cfg(feature = "morph")]350morph_target_names: MeshExtractableData::NoData,351asset_usage,352enable_raytracing: true,353final_aabb: None,354skinned_mesh_bounds: None,355}356}357358/// Returns the topology of the mesh.359pub fn primitive_topology(&self) -> PrimitiveTopology {360self.primitive_topology361}362363/// Sets the data for a vertex attribute (position, normal, etc.). The name will364/// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].365///366/// `Aabb` of entities with modified mesh are not updated automatically.367///368/// # Panics369/// Panics when the format of the values does not match the attribute's format.370/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle371/// this as an error use [`Mesh::try_insert_attribute`]372#[inline]373pub fn insert_attribute(374&mut self,375attribute: MeshVertexAttribute,376values: impl Into<VertexAttributeValues>,377) {378self.try_insert_attribute(attribute, values)379.expect(MESH_EXTRACTED_ERROR);380}381382/// Sets the data for a vertex attribute (position, normal, etc.). The name will383/// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].384///385/// `Aabb` of entities with modified mesh are not updated automatically.386///387/// Returns an error if the mesh data has been extracted to `RenderWorld`.388///389/// # Panics390/// Panics when the format of the values does not match the attribute's format.391#[inline]392pub fn try_insert_attribute(393&mut self,394attribute: MeshVertexAttribute,395values: impl Into<VertexAttributeValues>,396) -> Result<(), MeshAccessError> {397let values = values.into();398let values_format = VertexFormat::from(&values);399if values_format != attribute.format {400panic!(401"Failed to insert attribute. Invalid attribute format for {}. Given format is {values_format:?} but expected {:?}",402attribute.name, attribute.format403);404}405406self.attributes407.as_mut()?408.insert(attribute.id, MeshAttributeData { attribute, values });409Ok(())410}411412/// Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.).413/// The name will often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].414///415/// (Alternatively, you can use [`Mesh::insert_attribute`] to mutate an existing mesh in-place)416///417/// `Aabb` of entities with modified mesh are not updated automatically.418///419/// # Panics420/// Panics when the format of the values does not match the attribute's format.421/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle422/// this as an error use [`Mesh::try_with_inserted_attribute`]423#[must_use]424#[inline]425pub fn with_inserted_attribute(426mut self,427attribute: MeshVertexAttribute,428values: impl Into<VertexAttributeValues>,429) -> Self {430self.insert_attribute(attribute, values);431self432}433434/// Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.).435/// The name will often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].436///437/// (Alternatively, you can use [`Mesh::insert_attribute`] to mutate an existing mesh in-place)438///439/// `Aabb` of entities with modified mesh are not updated automatically.440///441/// Returns an error if the mesh data has been extracted to `RenderWorld`.442#[inline]443pub fn try_with_inserted_attribute(444mut self,445attribute: MeshVertexAttribute,446values: impl Into<VertexAttributeValues>,447) -> Result<Self, MeshAccessError> {448self.try_insert_attribute(attribute, values)?;449Ok(self)450}451452/// Removes the data for a vertex attribute453///454/// # Panics455/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle456/// this as an error use [`Mesh::try_remove_attribute`]457pub fn remove_attribute(458&mut self,459attribute: impl Into<MeshVertexAttributeId>,460) -> Option<VertexAttributeValues> {461self.attributes462.as_mut()463.expect(MESH_EXTRACTED_ERROR)464.remove(&attribute.into())465.map(|data| data.values)466}467468/// Removes the data for a vertex attribute469/// Returns an error if the mesh data has been extracted to `RenderWorld`or470/// if the attribute does not exist.471pub fn try_remove_attribute(472&mut self,473attribute: impl Into<MeshVertexAttributeId>,474) -> Result<VertexAttributeValues, MeshAccessError> {475Ok(self476.attributes477.as_mut()?478.remove(&attribute.into())479.ok_or(MeshAccessError::NotFound)?480.values)481}482483/// Consumes the mesh and returns a mesh without the data for a vertex attribute484///485/// (Alternatively, you can use [`Mesh::remove_attribute`] to mutate an existing mesh in-place)486///487/// # Panics488/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle489/// this as an error use [`Mesh::try_with_removed_attribute`]490#[must_use]491pub fn with_removed_attribute(mut self, attribute: impl Into<MeshVertexAttributeId>) -> Self {492self.remove_attribute(attribute);493self494}495496/// Consumes the mesh and returns a mesh without the data for a vertex attribute497///498/// (Alternatively, you can use [`Mesh::remove_attribute`] to mutate an existing mesh in-place)499///500/// Returns an error if the mesh data has been extracted to `RenderWorld`or501/// if the attribute does not exist.502pub fn try_with_removed_attribute(503mut self,504attribute: impl Into<MeshVertexAttributeId>,505) -> Result<Self, MeshAccessError> {506self.try_remove_attribute(attribute)?;507Ok(self)508}509510/// Returns a bool indicating if the attribute is present in this mesh's vertex data.511///512/// # Panics513/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle514/// this as an error use [`Mesh::try_contains_attribute`]515#[inline]516pub fn contains_attribute(&self, id: impl Into<MeshVertexAttributeId>) -> bool {517self.attributes518.as_ref()519.expect(MESH_EXTRACTED_ERROR)520.contains_key(&id.into())521}522523/// Returns a bool indicating if the attribute is present in this mesh's vertex data.524///525/// Returns an error if the mesh data has been extracted to `RenderWorld`.526#[inline]527pub fn try_contains_attribute(528&self,529id: impl Into<MeshVertexAttributeId>,530) -> Result<bool, MeshAccessError> {531Ok(self.attributes.as_ref()?.contains_key(&id.into()))532}533534/// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].535///536/// # Panics537/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle538/// this as an error use [`Mesh::try_attribute`] or [`Mesh::try_attribute_option`]539#[inline]540pub fn attribute(541&self,542id: impl Into<MeshVertexAttributeId>,543) -> Option<&VertexAttributeValues> {544self.try_attribute_option(id).expect(MESH_EXTRACTED_ERROR)545}546547/// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].548///549/// Returns an error if the mesh data has been extracted to `RenderWorld`or550/// if the attribute does not exist.551#[inline]552pub fn try_attribute(553&self,554id: impl Into<MeshVertexAttributeId>,555) -> Result<&VertexAttributeValues, MeshAccessError> {556self.try_attribute_option(id)?557.ok_or(MeshAccessError::NotFound)558}559560/// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].561///562/// Returns an error if the mesh data has been extracted to `RenderWorld`.563#[inline]564pub fn try_attribute_option(565&self,566id: impl Into<MeshVertexAttributeId>,567) -> Result<Option<&VertexAttributeValues>, MeshAccessError> {568Ok(self569.attributes570.as_ref()?571.get(&id.into())572.map(|data| &data.values))573}574575/// Retrieves the full data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].576#[inline]577pub(crate) fn try_attribute_data(578&self,579id: impl Into<MeshVertexAttributeId>,580) -> Result<Option<&MeshAttributeData>, MeshAccessError> {581Ok(self.attributes.as_ref()?.get(&id.into()))582}583584/// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.585///586/// # Panics587/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle588/// this as an error use [`Mesh::try_attribute_mut`]589#[inline]590pub fn attribute_mut(591&mut self,592id: impl Into<MeshVertexAttributeId>,593) -> Option<&mut VertexAttributeValues> {594self.try_attribute_mut_option(id)595.expect(MESH_EXTRACTED_ERROR)596}597598/// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.599///600/// Returns an error if the mesh data has been extracted to `RenderWorld`or601/// if the attribute does not exist.602#[inline]603pub fn try_attribute_mut(604&mut self,605id: impl Into<MeshVertexAttributeId>,606) -> Result<&mut VertexAttributeValues, MeshAccessError> {607self.try_attribute_mut_option(id)?608.ok_or(MeshAccessError::NotFound)609}610611/// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.612///613/// Returns an error if the mesh data has been extracted to `RenderWorld`.614#[inline]615pub fn try_attribute_mut_option(616&mut self,617id: impl Into<MeshVertexAttributeId>,618) -> Result<Option<&mut VertexAttributeValues>, MeshAccessError> {619Ok(self620.attributes621.as_mut()?622.get_mut(&id.into())623.map(|data| &mut data.values))624}625626/// Returns an iterator that yields references to the data of each vertex attribute.627///628/// # Panics629/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle630/// this as an error use [`Mesh::try_attributes`]631pub fn attributes(632&self,633) -> impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)> {634self.try_attributes().expect(MESH_EXTRACTED_ERROR)635}636637/// Returns an iterator that yields references to the data of each vertex attribute.638/// Returns an error if data has been extracted to `RenderWorld`639pub fn try_attributes(640&self,641) -> Result<impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)>, MeshAccessError>642{643Ok(self644.attributes645.as_ref()?646.values()647.map(|data| (&data.attribute, &data.values)))648}649650/// Returns an iterator that yields mutable references to the data of each vertex attribute.651///652/// # Panics653/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle654/// this as an error use [`Mesh::try_attributes_mut`]655pub fn attributes_mut(656&mut self,657) -> impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)> {658self.try_attributes_mut().expect(MESH_EXTRACTED_ERROR)659}660661/// Returns an iterator that yields mutable references to the data of each vertex attribute.662///663/// Returns an error if the mesh data has been extracted to `RenderWorld`.664pub fn try_attributes_mut(665&mut self,666) -> Result<667impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)>,668MeshAccessError,669> {670Ok(self671.attributes672.as_mut()?673.values_mut()674.map(|data| (&data.attribute, &mut data.values)))675}676677/// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the678/// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants679/// that use triangles.680///681/// # Panics682/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle683/// this as an error use [`Mesh::try_insert_indices`]684#[inline]685pub fn insert_indices(&mut self, indices: Indices) {686self.indices687.replace(Some(indices))688.expect(MESH_EXTRACTED_ERROR);689}690691/// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the692/// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants693/// that use triangles.694///695/// Returns an error if the mesh data has been extracted to `RenderWorld`.696#[inline]697pub fn try_insert_indices(&mut self, indices: Indices) -> Result<(), MeshAccessError> {698self.indices.replace(Some(indices))?;699Ok(())700}701702/// Consumes the mesh and returns a mesh with the given vertex indices. They describe how triangles703/// are constructed out of the vertex attributes and are therefore only useful for the704/// [`PrimitiveTopology`] variants that use triangles.705///706/// (Alternatively, you can use [`Mesh::insert_indices`] to mutate an existing mesh in-place)707///708/// # Panics709/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle710/// this as an error use [`Mesh::try_with_inserted_indices`]711#[must_use]712#[inline]713pub fn with_inserted_indices(mut self, indices: Indices) -> Self {714self.insert_indices(indices);715self716}717718/// Consumes the mesh and returns a mesh with the given vertex indices. They describe how triangles719/// are constructed out of the vertex attributes and are therefore only useful for the720/// [`PrimitiveTopology`] variants that use triangles.721///722/// (Alternatively, you can use [`Mesh::try_insert_indices`] to mutate an existing mesh in-place)723///724/// Returns an error if the mesh data has been extracted to `RenderWorld`.725#[inline]726pub fn try_with_inserted_indices(mut self, indices: Indices) -> Result<Self, MeshAccessError> {727self.try_insert_indices(indices)?;728Ok(self)729}730731/// Retrieves the vertex `indices` of the mesh, returns None if not found.732///733/// # Panics734/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle735/// this as an error use [`Mesh::try_indices`]736#[inline]737pub fn indices(&self) -> Option<&Indices> {738self.indices.as_ref_option().expect(MESH_EXTRACTED_ERROR)739}740741/// Retrieves the vertex `indices` of the mesh.742///743/// Returns an error if the mesh data has been extracted to `RenderWorld`or744/// if the attribute does not exist.745#[inline]746pub fn try_indices(&self) -> Result<&Indices, MeshAccessError> {747self.indices.as_ref()748}749750/// Retrieves the vertex `indices` of the mesh, returns None if not found.751///752/// Returns an error if the mesh data has been extracted to `RenderWorld`.753#[inline]754pub fn try_indices_option(&self) -> Result<Option<&Indices>, MeshAccessError> {755self.indices.as_ref_option()756}757758/// Retrieves the vertex `indices` of the mesh mutably.759#[inline]760pub fn indices_mut(&mut self) -> Option<&mut Indices> {761self.try_indices_mut_option().expect(MESH_EXTRACTED_ERROR)762}763764/// Retrieves the vertex `indices` of the mesh mutably.765///766/// Returns an error if the mesh data has been extracted to `RenderWorld`.767#[inline]768pub fn try_indices_mut(&mut self) -> Result<&mut Indices, MeshAccessError> {769self.indices.as_mut()770}771772/// Retrieves the vertex `indices` of the mesh mutably.773///774/// Returns an error if the mesh data has been extracted to `RenderWorld`.775#[inline]776pub fn try_indices_mut_option(&mut self) -> Result<Option<&mut Indices>, MeshAccessError> {777self.indices.as_mut_option()778}779780/// Removes the vertex `indices` from the mesh and returns them.781///782/// # Panics783/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle784/// this as an error use [`Mesh::try_remove_indices`]785#[inline]786pub fn remove_indices(&mut self) -> Option<Indices> {787self.try_remove_indices().expect(MESH_EXTRACTED_ERROR)788}789790/// Removes the vertex `indices` from the mesh and returns them.791///792/// Returns an error if the mesh data has been extracted to `RenderWorld`.793#[inline]794pub fn try_remove_indices(&mut self) -> Result<Option<Indices>, MeshAccessError> {795self.indices.replace(None)796}797798/// Consumes the mesh and returns a mesh without the vertex `indices` of the mesh.799///800/// (Alternatively, you can use [`Mesh::remove_indices`] to mutate an existing mesh in-place)801///802/// # Panics803/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle804/// this as an error use [`Mesh::try_with_removed_indices`]805#[must_use]806pub fn with_removed_indices(mut self) -> Self {807self.remove_indices();808self809}810811/// Consumes the mesh and returns a mesh without the vertex `indices` of the mesh.812///813/// (Alternatively, you can use [`Mesh::try_remove_indices`] to mutate an existing mesh in-place)814///815/// Returns an error if the mesh data has been extracted to `RenderWorld`.816pub fn try_with_removed_indices(mut self) -> Result<Self, MeshAccessError> {817self.try_remove_indices()?;818Ok(self)819}820821/// Returns the size of a vertex in bytes.822///823/// # Panics824/// Panics when the mesh data has already been extracted to `RenderWorld`.825pub fn get_vertex_size(&self) -> u64 {826self.attributes827.as_ref()828.expect(MESH_EXTRACTED_ERROR)829.values()830.map(|data| data.attribute.format.size())831.sum()832}833834/// Returns the size required for the vertex buffer in bytes.835///836/// # Panics837/// Panics when the mesh data has already been extracted to `RenderWorld`.838pub fn get_vertex_buffer_size(&self) -> usize {839let vertex_size = self.get_vertex_size() as usize;840let vertex_count = self.count_vertices();841vertex_count * vertex_size842}843844/// Computes and returns the index data of the mesh as bytes.845/// This is used to transform the index data into a GPU friendly format.846///847/// # Panics848/// Panics when the mesh data has already been extracted to `RenderWorld`.849pub fn get_index_buffer_bytes(&self) -> Option<&[u8]> {850let mesh_indices = self.indices.as_ref_option().expect(MESH_EXTRACTED_ERROR);851852mesh_indices.as_ref().map(|indices| match &indices {853Indices::U16(indices) => cast_slice(&indices[..]),854Indices::U32(indices) => cast_slice(&indices[..]),855})856}857858/// Get this `Mesh`'s [`MeshVertexBufferLayout`], used in `SpecializedMeshPipeline`.859///860/// # Panics861/// Panics when the mesh data has already been extracted to `RenderWorld`.862pub fn get_mesh_vertex_buffer_layout(863&self,864mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,865) -> MeshVertexBufferLayoutRef {866let mesh_attributes = self.attributes.as_ref().expect(MESH_EXTRACTED_ERROR);867868let mut attributes = Vec::with_capacity(mesh_attributes.len());869let mut attribute_ids = Vec::with_capacity(mesh_attributes.len());870let mut accumulated_offset = 0;871for (index, data) in mesh_attributes.values().enumerate() {872attribute_ids.push(data.attribute.id);873attributes.push(VertexAttribute {874offset: accumulated_offset,875format: data.attribute.format,876shader_location: index as u32,877});878accumulated_offset += data.attribute.format.size();879}880881let layout = MeshVertexBufferLayout {882layout: VertexBufferLayout {883array_stride: accumulated_offset,884step_mode: VertexStepMode::Vertex,885attributes,886},887attribute_ids,888};889mesh_vertex_buffer_layouts.insert(layout)890}891892/// Counts all vertices of the mesh.893///894/// If the attributes have different vertex counts, the smallest is returned.895///896/// # Panics897/// Panics when the mesh data has already been extracted to `RenderWorld`.898pub fn count_vertices(&self) -> usize {899let mut vertex_count: Option<usize> = None;900let mesh_attributes = self.attributes.as_ref().expect(MESH_EXTRACTED_ERROR);901902for (attribute_id, attribute_data) in mesh_attributes {903let attribute_len = attribute_data.values.len();904if let Some(previous_vertex_count) = vertex_count {905if previous_vertex_count != attribute_len {906let name = mesh_attributes907.get(attribute_id)908.map(|data| data.attribute.name.to_string())909.unwrap_or_else(|| format!("{attribute_id:?}"));910911warn!("{name} has a different vertex count ({attribute_len}) than other attributes ({previous_vertex_count}) in this mesh, \912all attributes will be truncated to match the smallest.");913vertex_count = Some(core::cmp::min(previous_vertex_count, attribute_len));914}915} else {916vertex_count = Some(attribute_len);917}918}919920vertex_count.unwrap_or(0)921}922923/// Computes and returns the vertex data of the mesh as bytes.924/// Therefore the attributes are located in the order of their [`MeshVertexAttribute::id`].925/// This is used to transform the vertex data into a GPU friendly format.926///927/// If the vertex attributes have different lengths, they are all truncated to928/// the length of the smallest.929///930/// This is a convenience method which allocates a Vec.931/// Prefer pre-allocating and using [`Mesh::write_packed_vertex_buffer_data`] when possible.932///933/// # Panics934/// Panics when the mesh data has already been extracted to `RenderWorld`.935pub fn create_packed_vertex_buffer_data(&self) -> Vec<u8> {936let mut attributes_interleaved_buffer = vec![0; self.get_vertex_buffer_size()];937self.write_packed_vertex_buffer_data(&mut attributes_interleaved_buffer);938attributes_interleaved_buffer939}940941/// Computes and write the vertex data of the mesh into a mutable byte slice.942/// The attributes are located in the order of their [`MeshVertexAttribute::id`].943/// This is used to transform the vertex data into a GPU friendly format.944///945/// If the vertex attributes have different lengths, they are all truncated to946/// the length of the smallest.947///948/// # Panics949/// Panics when the mesh data has already been extracted to `RenderWorld`.950pub fn write_packed_vertex_buffer_data(&self, slice: &mut [u8]) {951let mesh_attributes = self.attributes.as_ref().expect(MESH_EXTRACTED_ERROR);952953let vertex_size = self.get_vertex_size() as usize;954let vertex_count = self.count_vertices();955// bundle into interleaved buffers956let mut attribute_offset = 0;957for attribute_data in mesh_attributes.values() {958let attribute_size = attribute_data.attribute.format.size() as usize;959let attributes_bytes = attribute_data.values.get_bytes();960for (vertex_index, attribute_bytes) in attributes_bytes961.chunks_exact(attribute_size)962.take(vertex_count)963.enumerate()964{965let offset = vertex_index * vertex_size + attribute_offset;966slice[offset..offset + attribute_size].copy_from_slice(attribute_bytes);967}968969attribute_offset += attribute_size;970}971}972973/// Duplicates the vertex attributes so that no vertices are shared.974///975/// This can dramatically increase the vertex count, so make sure this is what you want.976/// Does nothing if no [Indices] are set.977///978/// # Panics979/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle980/// this as an error use [`Mesh::try_duplicate_vertices`]981pub fn duplicate_vertices(&mut self) {982self.try_duplicate_vertices().expect(MESH_EXTRACTED_ERROR);983}984985/// Duplicates the vertex attributes so that no vertices are shared.986///987/// This can dramatically increase the vertex count, so make sure this is what you want.988/// Does nothing if no [Indices] are set.989///990/// Returns an error if the mesh data has been extracted to `RenderWorld`.991pub fn try_duplicate_vertices(&mut self) -> Result<(), MeshAccessError> {992fn duplicate<T: Copy>(values: &[T], indices: impl Iterator<Item = usize>) -> Vec<T> {993indices.map(|i| values[i]).collect()994}995996let Some(indices) = self.indices.replace(None)? else {997return Ok(());998};9991000let mesh_attributes = self.attributes.as_mut()?;10011002for attributes in mesh_attributes.values_mut() {1003let indices = indices.iter();1004#[expect(1005clippy::match_same_arms,1006reason = "Although the `vec` binding 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."1007)]1008match &mut attributes.values {1009VertexAttributeValues::Float32(vec) => *vec = duplicate(vec, indices),1010VertexAttributeValues::Sint32(vec) => *vec = duplicate(vec, indices),1011VertexAttributeValues::Uint32(vec) => *vec = duplicate(vec, indices),1012VertexAttributeValues::Float32x2(vec) => *vec = duplicate(vec, indices),1013VertexAttributeValues::Sint32x2(vec) => *vec = duplicate(vec, indices),1014VertexAttributeValues::Uint32x2(vec) => *vec = duplicate(vec, indices),1015VertexAttributeValues::Float32x3(vec) => *vec = duplicate(vec, indices),1016VertexAttributeValues::Sint32x3(vec) => *vec = duplicate(vec, indices),1017VertexAttributeValues::Uint32x3(vec) => *vec = duplicate(vec, indices),1018VertexAttributeValues::Sint32x4(vec) => *vec = duplicate(vec, indices),1019VertexAttributeValues::Uint32x4(vec) => *vec = duplicate(vec, indices),1020VertexAttributeValues::Float32x4(vec) => *vec = duplicate(vec, indices),1021VertexAttributeValues::Sint16x2(vec) => *vec = duplicate(vec, indices),1022VertexAttributeValues::Snorm16x2(vec) => *vec = duplicate(vec, indices),1023VertexAttributeValues::Uint16x2(vec) => *vec = duplicate(vec, indices),1024VertexAttributeValues::Unorm16x2(vec) => *vec = duplicate(vec, indices),1025VertexAttributeValues::Sint16x4(vec) => *vec = duplicate(vec, indices),1026VertexAttributeValues::Snorm16x4(vec) => *vec = duplicate(vec, indices),1027VertexAttributeValues::Uint16x4(vec) => *vec = duplicate(vec, indices),1028VertexAttributeValues::Unorm16x4(vec) => *vec = duplicate(vec, indices),1029VertexAttributeValues::Sint8x2(vec) => *vec = duplicate(vec, indices),1030VertexAttributeValues::Snorm8x2(vec) => *vec = duplicate(vec, indices),1031VertexAttributeValues::Uint8x2(vec) => *vec = duplicate(vec, indices),1032VertexAttributeValues::Unorm8x2(vec) => *vec = duplicate(vec, indices),1033VertexAttributeValues::Sint8x4(vec) => *vec = duplicate(vec, indices),1034VertexAttributeValues::Snorm8x4(vec) => *vec = duplicate(vec, indices),1035VertexAttributeValues::Uint8x4(vec) => *vec = duplicate(vec, indices),1036VertexAttributeValues::Unorm8x4(vec) => *vec = duplicate(vec, indices),1037}1038}10391040Ok(())1041}10421043/// Consumes the mesh and returns a mesh with no shared vertices.1044///1045/// This can dramatically increase the vertex count, so make sure this is what you want.1046/// Does nothing if no [`Indices`] are set.1047///1048/// (Alternatively, you can use [`Mesh::duplicate_vertices`] to mutate an existing mesh in-place)1049///1050/// # Panics1051/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1052/// this as an error use [`Mesh::try_with_duplicated_vertices`]1053#[must_use]1054pub fn with_duplicated_vertices(mut self) -> Self {1055self.duplicate_vertices();1056self1057}10581059/// Consumes the mesh and returns a mesh with no shared vertices.1060///1061/// This can dramatically increase the vertex count, so make sure this is what you want.1062/// Does nothing if no [`Indices`] are set.1063///1064/// (Alternatively, you can use [`Mesh::try_duplicate_vertices`] to mutate an existing mesh in-place)1065///1066/// Returns an error if the mesh data has been extracted to `RenderWorld`.1067pub fn try_with_duplicated_vertices(mut self) -> Result<Self, MeshAccessError> {1068self.try_duplicate_vertices()?;1069Ok(self)1070}10711072/// Remove duplicate vertices and create the index pointing to the unique vertices.1073///1074/// Returns an error if the mesh data has been extracted to `RenderWorld`.1075/// Returns an error if the mesh already has [`Indices`] set, even if there1076/// are duplicate vertices. If deduplication is needed with indices already set,1077/// consider calling [`Mesh::duplicate_vertices`] and then this function.1078pub fn merge_duplicate_vertices(&mut self) -> Result<(), MeshMergeDuplicateVerticesError> {1079match self.try_indices() {1080Ok(_) => return Err(MeshMergeDuplicateVerticesError::IndicesAlreadySet),1081Err(err) => match err {1082MeshAccessError::ExtractedToRenderWorld => return Err(err.into()),1083MeshAccessError::NotFound => (),1084},1085}10861087#[derive(Copy, Clone)]1088struct VertexRef<'a> {1089mesh_attributes: &'a BTreeMap<MeshVertexAttributeId, MeshAttributeData>,1090i: usize,1091}1092impl<'a> VertexRef<'a> {1093fn push_to(&self, target: &mut BTreeMap<MeshVertexAttributeId, MeshAttributeData>) {1094for (key, this_attribute_data) in self.mesh_attributes.iter() {1095let target_attribute_data = target.get_mut(key).unwrap(); // ok to unwrap, all keys added to new_attributes below1096target_attribute_data1097.values1098.push_from(&this_attribute_data.values, self.i);1099}1100}1101}1102impl<'a> PartialEq for VertexRef<'a> {1103fn eq(&self, other: &Self) -> bool {1104assert!(ptr::eq(self.mesh_attributes, other.mesh_attributes));1105for values in self.mesh_attributes.values() {1106if values.values.get_bytes_at(self.i) != values.values.get_bytes_at(other.i) {1107return false;1108}1109}1110true1111}1112}1113impl<'a> Eq for VertexRef<'a> {}1114impl<'a> Hash for VertexRef<'a> {1115fn hash<H: Hasher>(&self, state: &mut H) {1116for values in self.mesh_attributes.values() {1117values.values.get_bytes_at(self.i).hash(state);1118}1119}1120}11211122let old_attributes = self.attributes.as_ref()?;11231124let mut new_attributes: BTreeMap<MeshVertexAttributeId, MeshAttributeData> = self1125.attributes1126.as_ref()?1127.iter()1128.map(|(k, v)| {1129(1130*k,1131MeshAttributeData {1132attribute: v.attribute,1133values: VertexAttributeValues::new(VertexFormat::from(&v.values)),1134},1135)1136})1137.collect();11381139let mut vertex_to_new_index: HashMap<VertexRef, u32> = HashMap::new();1140let mut indices = Vec::with_capacity(self.count_vertices());1141for i in 0..self.count_vertices() {1142let len: u32 = vertex_to_new_index1143.len()1144.try_into()1145.expect("The number of vertices exceeds u32::MAX");1146let vertex_ref = VertexRef {1147mesh_attributes: old_attributes,1148i,1149};1150let j = match vertex_to_new_index.entry(vertex_ref) {1151hash_map::Entry::Occupied(e) => *e.get(),1152hash_map::Entry::Vacant(e) => {1153e.insert(len);1154vertex_ref.push_to(&mut new_attributes);1155len1156}1157};1158indices.push(j);1159}1160drop(vertex_to_new_index);11611162for v in new_attributes.values_mut() {1163v.values.shrink_to_fit();1164}11651166self.attributes = MeshExtractableData::Data(new_attributes);1167self.indices = MeshExtractableData::Data(Indices::U32(indices));11681169Ok(())1170}11711172/// Consumes the mesh and returns a mesh with merged vertices.1173///1174/// (Alternatively, you can use [`Mesh::merge_duplicate_vertices`] to mutate an existing mesh in-place)1175///1176/// Returns an error if the mesh data has been extracted to `RenderWorld`.1177/// Returns an error if the mesh already has [`Indices`] set, even if there1178/// are duplicate vertices. If deduplication is needed with indices already set,1179/// consider calling [`Mesh::duplicate_vertices`] and then this function.1180pub fn with_merge_duplicate_vertices(1181mut self,1182) -> Result<Self, MeshMergeDuplicateVerticesError> {1183self.merge_duplicate_vertices()?;1184Ok(self)1185}11861187/// Inverts the winding of the indices such that all counter-clockwise triangles are now1188/// clockwise and vice versa.1189/// For lines, their start and end indices are flipped.1190///1191/// Does nothing if no [`Indices`] are set.1192/// If this operation succeeded, an [`Ok`] result is returned.1193pub fn invert_winding(&mut self) -> Result<(), MeshWindingInvertError> {1194fn invert<I>(1195indices: &mut [I],1196topology: PrimitiveTopology,1197) -> Result<(), MeshWindingInvertError> {1198match topology {1199PrimitiveTopology::TriangleList => {1200// Early return if the index count doesn't match1201if !indices.len().is_multiple_of(3) {1202return Err(MeshWindingInvertError::AbruptIndicesEnd);1203}1204for chunk in indices.chunks_mut(3) {1205// This currently can only be optimized away with unsafe, rework this when `feature(slice_as_chunks)` gets stable.1206let [_, b, c] = chunk else {1207return Err(MeshWindingInvertError::AbruptIndicesEnd);1208};1209core::mem::swap(b, c);1210}1211Ok(())1212}1213PrimitiveTopology::LineList => {1214// Early return if the index count doesn't match1215if !indices.len().is_multiple_of(2) {1216return Err(MeshWindingInvertError::AbruptIndicesEnd);1217}1218indices.reverse();1219Ok(())1220}1221PrimitiveTopology::TriangleStrip | PrimitiveTopology::LineStrip => {1222indices.reverse();1223Ok(())1224}1225_ => Err(MeshWindingInvertError::WrongTopology),1226}1227}12281229let mesh_indices = self.indices.as_mut_option()?;12301231match mesh_indices {1232Some(Indices::U16(vec)) => invert(vec, self.primitive_topology),1233Some(Indices::U32(vec)) => invert(vec, self.primitive_topology),1234None => Ok(()),1235}1236}12371238/// Consumes the mesh and returns a mesh with inverted winding of the indices such1239/// that all counter-clockwise triangles are now clockwise and vice versa.1240///1241/// Does nothing if no [`Indices`] are set.1242pub fn with_inverted_winding(mut self) -> Result<Self, MeshWindingInvertError> {1243self.invert_winding().map(|_| self)1244}12451246/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.1247/// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat1248/// normals.1249///1250/// # Panics1251/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1252/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].=1253/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1254/// this as an error use [`Mesh::try_compute_normals`]1255pub fn compute_normals(&mut self) {1256self.try_compute_normals().expect(MESH_EXTRACTED_ERROR);1257}12581259/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.1260/// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat1261/// normals.1262///1263/// # Panics1264/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1265/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].=1266pub fn try_compute_normals(&mut self) -> Result<(), MeshAccessError> {1267assert!(1268matches!(self.primitive_topology, PrimitiveTopology::TriangleList),1269"`compute_normals` can only work on `TriangleList`s"1270);1271if self.try_indices_option()?.is_none() {1272self.try_compute_flat_normals()1273} else {1274self.try_compute_smooth_normals()1275}1276}12771278/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.1279///1280/// # Panics1281/// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1282/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1283/// Consider calling [`Mesh::duplicate_vertices`] or exporting your mesh with normal1284/// attributes.1285/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1286/// this as an error use [`Mesh::try_compute_flat_normals`]1287///1288/// FIXME: This should handle more cases since this is called as a part of gltf1289/// mesh loading where we can't really blame users for loading meshes that might1290/// not conform to the limitations here!1291pub fn compute_flat_normals(&mut self) {1292self.try_compute_flat_normals().expect(MESH_EXTRACTED_ERROR);1293}12941295/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.1296///1297/// # Panics1298/// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1299/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1300/// Consider calling [`Mesh::duplicate_vertices`] or exporting your mesh with normal1301/// attributes.1302///1303/// FIXME: This should handle more cases since this is called as a part of gltf1304/// mesh loading where we can't really blame users for loading meshes that might1305/// not conform to the limitations here!1306pub fn try_compute_flat_normals(&mut self) -> Result<(), MeshAccessError> {1307assert!(1308self.try_indices_option()?.is_none(),1309"`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`."1310);1311assert!(1312matches!(self.primitive_topology, PrimitiveTopology::TriangleList),1313"`compute_flat_normals` can only work on `TriangleList`s"1314);13151316let positions = self1317.try_attribute(Mesh::ATTRIBUTE_POSITION)?1318.as_float3()1319.expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");13201321let normals: Vec<_> = positions1322.chunks_exact(3)1323.map(|p| triangle_normal(p[0], p[1], p[2]))1324.flat_map(|normal| [normal; 3])1325.collect();13261327self.try_insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals)1328}13291330/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared1331/// vertices.1332///1333/// This method weights normals by the angles of the corners of connected triangles, thus1334/// eliminating triangle area and count as factors in the final normal. This does make it1335/// somewhat slower than [`Mesh::compute_area_weighted_normals`] which does not need to1336/// greedily normalize each triangle's normal or calculate corner angles.1337///1338/// If you would rather have the computed normals be weighted by triangle area, see1339/// [`Mesh::compute_area_weighted_normals`] instead. If you need to weight them in some other1340/// way, see [`Mesh::compute_custom_smooth_normals`].1341///1342/// # Panics1343/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1344/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1345/// Panics if the mesh does not have indices defined.1346/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1347/// this as an error use [`Mesh::try_compute_smooth_normals`]1348pub fn compute_smooth_normals(&mut self) {1349self.try_compute_smooth_normals()1350.expect(MESH_EXTRACTED_ERROR);1351}13521353/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared1354/// vertices.1355///1356/// This method weights normals by the angles of the corners of connected triangles, thus1357/// eliminating triangle area and count as factors in the final normal. This does make it1358/// somewhat slower than [`Mesh::compute_area_weighted_normals`] which does not need to1359/// greedily normalize each triangle's normal or calculate corner angles.1360///1361/// If you would rather have the computed normals be weighted by triangle area, see1362/// [`Mesh::compute_area_weighted_normals`] instead. If you need to weight them in some other1363/// way, see [`Mesh::compute_custom_smooth_normals`].1364///1365/// # Panics1366/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1367/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1368/// Panics if the mesh does not have indices defined.1369pub fn try_compute_smooth_normals(&mut self) -> Result<(), MeshAccessError> {1370self.try_compute_custom_smooth_normals(|[a, b, c], positions, normals| {1371let pa = Vec3::from(positions[a]);1372let pb = Vec3::from(positions[b]);1373let pc = Vec3::from(positions[c]);13741375let ab = pb - pa;1376let ba = pa - pb;1377let bc = pc - pb;1378let cb = pb - pc;1379let ca = pa - pc;1380let ac = pc - pa;13811382const EPS: f32 = f32::EPSILON;1383let weight_a = if ab.length_squared() * ac.length_squared() > EPS {1384ab.angle_between(ac)1385} else {13860.01387};1388let weight_b = if ba.length_squared() * bc.length_squared() > EPS {1389ba.angle_between(bc)1390} else {13910.01392};1393let weight_c = if ca.length_squared() * cb.length_squared() > EPS {1394ca.angle_between(cb)1395} else {13960.01397};13981399let normal = Vec3::from(triangle_normal(positions[a], positions[b], positions[c]));14001401normals[a] += normal * weight_a;1402normals[b] += normal * weight_b;1403normals[c] += normal * weight_c;1404})1405}14061407/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared1408/// vertices.1409///1410/// This method weights normals by the area of each triangle containing the vertex. Thus,1411/// larger triangles will skew the normals of their vertices towards their own normal more1412/// than smaller triangles will.1413///1414/// This method is actually somewhat faster than [`Mesh::compute_smooth_normals`] because an1415/// intermediate result of triangle normal calculation is already scaled by the triangle's area.1416///1417/// If you would rather have the computed normals be influenced only by the angles of connected1418/// edges, see [`Mesh::compute_smooth_normals`] instead. If you need to weight them in some1419/// other way, see [`Mesh::compute_custom_smooth_normals`].1420///1421/// # Panics1422/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1423/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1424/// Panics if the mesh does not have indices defined.1425/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1426/// this as an error use [`Mesh::try_compute_area_weighted_normals`]1427pub fn compute_area_weighted_normals(&mut self) {1428self.try_compute_area_weighted_normals()1429.expect(MESH_EXTRACTED_ERROR);1430}14311432/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared1433/// vertices.1434///1435/// This method weights normals by the area of each triangle containing the vertex. Thus,1436/// larger triangles will skew the normals of their vertices towards their own normal more1437/// than smaller triangles will.1438///1439/// This method is actually somewhat faster than [`Mesh::compute_smooth_normals`] because an1440/// intermediate result of triangle normal calculation is already scaled by the triangle's area.1441///1442/// If you would rather have the computed normals be influenced only by the angles of connected1443/// edges, see [`Mesh::compute_smooth_normals`] instead. If you need to weight them in some1444/// other way, see [`Mesh::compute_custom_smooth_normals`].1445///1446/// # Panics1447/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1448/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1449/// Panics if the mesh does not have indices defined.1450pub fn try_compute_area_weighted_normals(&mut self) -> Result<(), MeshAccessError> {1451self.try_compute_custom_smooth_normals(|[a, b, c], positions, normals| {1452let normal = Vec3::from(triangle_area_normal(1453positions[a],1454positions[b],1455positions[c],1456));1457[a, b, c].into_iter().for_each(|pos| {1458normals[pos] += normal;1459});1460})1461}14621463/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared1464/// vertices.1465///1466/// This method allows you to customize how normals are weighted via the `per_triangle` parameter,1467/// which must be a function or closure that accepts 3 parameters:1468/// - The indices of the three vertices of the triangle as a `[usize; 3]`.1469/// - A reference to the values of the [`Mesh::ATTRIBUTE_POSITION`] of the mesh (`&[[f32; 3]]`).1470/// - A mutable reference to the sums of all normals so far.1471///1472/// See also the standard methods included in Bevy for calculating smooth normals:1473/// - [`Mesh::compute_smooth_normals`]1474/// - [`Mesh::compute_area_weighted_normals`]1475///1476/// An example that would weight each connected triangle's normal equally, thus skewing normals1477/// towards the planes divided into the most triangles:1478/// ```1479/// # use bevy_asset::RenderAssetUsages;1480/// # use bevy_mesh::{Mesh, PrimitiveTopology, Meshable, MeshBuilder};1481/// # use bevy_math::{Vec3, primitives::Cuboid};1482/// # let mut mesh = Cuboid::default().mesh().build();1483/// mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| {1484/// let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c]));1485/// for idx in [a, b, c] {1486/// normals[idx] += normal;1487/// }1488/// });1489/// ```1490///1491/// # Panics1492/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1493/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1494/// Panics if the mesh does not have indices defined.1495/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1496/// this as an error use [`Mesh::try_compute_custom_smooth_normals`]1497//1498// FIXME: This should handle more cases since this is called as a part of gltf1499// mesh loading where we can't really blame users for loading meshes that might1500// not conform to the limitations here!1501//1502// When fixed, also update "Panics" sections of1503// - [Mesh::compute_smooth_normals]1504// - [Mesh::with_computed_smooth_normals]1505// - [Mesh::compute_area_weighted_normals]1506// - [Mesh::with_computed_area_weighted_normals]1507pub fn compute_custom_smooth_normals(1508&mut self,1509per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]),1510) {1511self.try_compute_custom_smooth_normals(per_triangle)1512.expect(MESH_EXTRACTED_ERROR);1513}15141515/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared1516/// vertices.1517///1518/// This method allows you to customize how normals are weighted via the `per_triangle` parameter,1519/// which must be a function or closure that accepts 3 parameters:1520/// - The indices of the three vertices of the triangle as a `[usize; 3]`.1521/// - A reference to the values of the [`Mesh::ATTRIBUTE_POSITION`] of the mesh (`&[[f32; 3]]`).1522/// - A mutable reference to the sums of all normals so far.1523///1524/// See also the standard methods included in Bevy for calculating smooth normals:1525/// - [`Mesh::compute_smooth_normals`]1526/// - [`Mesh::compute_area_weighted_normals`]1527///1528/// An example that would weight each connected triangle's normal equally, thus skewing normals1529/// towards the planes divided into the most triangles:1530/// ```1531/// # use bevy_asset::RenderAssetUsages;1532/// # use bevy_mesh::{Mesh, PrimitiveTopology, Meshable, MeshBuilder};1533/// # use bevy_math::{Vec3, primitives::Cuboid};1534/// # let mut mesh = Cuboid::default().mesh().build();1535/// mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| {1536/// let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c]));1537/// for idx in [a, b, c] {1538/// normals[idx] += normal;1539/// }1540/// });1541/// ```1542///1543/// # Panics1544/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1545/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1546/// Panics if the mesh does not have indices defined.1547//1548// FIXME: This should handle more cases since this is called as a part of gltf1549// mesh loading where we can't really blame users for loading meshes that might1550// not conform to the limitations here!1551//1552// When fixed, also update "Panics" sections of1553// - [Mesh::compute_smooth_normals]1554// - [Mesh::with_computed_smooth_normals]1555// - [Mesh::compute_area_weighted_normals]1556// - [Mesh::with_computed_area_weighted_normals]1557pub fn try_compute_custom_smooth_normals(1558&mut self,1559mut per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]),1560) -> Result<(), MeshAccessError> {1561assert!(1562matches!(self.primitive_topology, PrimitiveTopology::TriangleList),1563"smooth normals can only be computed on `TriangleList`s"1564);1565assert!(1566self.try_indices_option()?.is_some(),1567"smooth normals can only be computed on indexed meshes"1568);15691570let positions = self1571.try_attribute(Mesh::ATTRIBUTE_POSITION)?1572.as_float3()1573.expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");15741575let mut normals = vec![Vec3::ZERO; positions.len()];15761577self.try_indices()?1578.iter()1579.collect::<Vec<usize>>()1580.chunks_exact(3)1581.for_each(|face| per_triangle([face[0], face[1], face[2]], positions, &mut normals));15821583for normal in &mut normals {1584*normal = normal.try_normalize().unwrap_or(Vec3::ZERO);1585}15861587self.try_insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals)1588}15891590/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].1591/// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat1592/// normals.1593///1594/// (Alternatively, you can use [`Mesh::compute_normals`] to mutate an existing mesh in-place)1595///1596/// # Panics1597/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1598/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1599/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1600/// this as an error use [`Mesh::try_with_computed_normals`]1601#[must_use]1602pub fn with_computed_normals(self) -> Self {1603self.try_with_computed_normals()1604.expect(MESH_EXTRACTED_ERROR)1605}16061607/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].1608/// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat1609/// normals.1610///1611/// (Alternatively, you can use [`Mesh::compute_normals`] to mutate an existing mesh in-place)1612///1613/// # Panics1614/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1615/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1616pub fn try_with_computed_normals(mut self) -> Result<Self, MeshAccessError> {1617self.try_compute_normals()?;1618Ok(self)1619}16201621/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].1622///1623/// (Alternatively, you can use [`Mesh::compute_flat_normals`] to mutate an existing mesh in-place)1624///1625/// # Panics1626/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1627/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1628/// Panics if the mesh has indices defined1629/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1630/// this as an error use [`Mesh::try_with_computed_flat_normals`]1631pub fn with_computed_flat_normals(mut self) -> Self {1632self.compute_flat_normals();1633self1634}16351636/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].1637///1638/// (Alternatively, you can use [`Mesh::compute_flat_normals`] to mutate an existing mesh in-place)1639///1640/// # Panics1641/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1642/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1643/// Panics if the mesh has indices defined1644pub fn try_with_computed_flat_normals(mut self) -> Result<Self, MeshAccessError> {1645self.try_compute_flat_normals()?;1646Ok(self)1647}16481649/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].1650///1651/// (Alternatively, you can use [`Mesh::compute_smooth_normals`] to mutate an existing mesh in-place)1652///1653/// This method weights normals by the angles of triangle corners connected to each vertex. If1654/// you would rather have the computed normals be weighted by triangle area, see1655/// [`Mesh::with_computed_area_weighted_normals`] instead.1656///1657/// # Panics1658/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1659/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1660/// Panics if the mesh does not have indices defined.1661/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1662/// this as an error use [`Mesh::try_with_computed_smooth_normals`]1663pub fn with_computed_smooth_normals(mut self) -> Self {1664self.compute_smooth_normals();1665self1666}1667/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].1668///1669/// (Alternatively, you can use [`Mesh::compute_smooth_normals`] to mutate an existing mesh in-place)1670///1671/// This method weights normals by the angles of triangle corners connected to each vertex. If1672/// you would rather have the computed normals be weighted by triangle area, see1673/// [`Mesh::with_computed_area_weighted_normals`] instead.1674///1675/// # Panics1676/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1677/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1678/// Panics if the mesh does not have indices defined.1679pub fn try_with_computed_smooth_normals(mut self) -> Result<Self, MeshAccessError> {1680self.try_compute_smooth_normals()?;1681Ok(self)1682}16831684/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].1685///1686/// (Alternatively, you can use [`Mesh::compute_area_weighted_normals`] to mutate an existing mesh in-place)1687///1688/// This method weights normals by the area of each triangle containing the vertex. Thus,1689/// larger triangles will skew the normals of their vertices towards their own normal more1690/// than smaller triangles will. If you would rather have the computed normals be influenced1691/// only by the angles of connected edges, see [`Mesh::with_computed_smooth_normals`] instead.1692///1693/// # Panics1694/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1695/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1696/// Panics if the mesh does not have indices defined.1697/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1698/// this as an error use [`Mesh::try_with_computed_area_weighted_normals`]1699pub fn with_computed_area_weighted_normals(mut self) -> Self {1700self.compute_area_weighted_normals();1701self1702}17031704/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].1705///1706/// (Alternatively, you can use [`Mesh::compute_area_weighted_normals`] to mutate an existing mesh in-place)1707///1708/// This method weights normals by the area of each triangle containing the vertex. Thus,1709/// larger triangles will skew the normals of their vertices towards their own normal more1710/// than smaller triangles will. If you would rather have the computed normals be influenced1711/// only by the angles of connected edges, see [`Mesh::with_computed_smooth_normals`] instead.1712///1713/// # Panics1714/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.1715/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].1716/// Panics if the mesh does not have indices defined.1717pub fn try_with_computed_area_weighted_normals(mut self) -> Result<Self, MeshAccessError> {1718self.try_compute_area_weighted_normals()?;1719Ok(self)1720}17211722/// Generate tangents for the mesh using the `mikktspace` algorithm.1723///1724/// Sets the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful.1725/// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set.1726#[cfg(feature = "bevy_mikktspace")]1727pub fn generate_tangents(&mut self) -> Result<(), super::GenerateTangentsError> {1728let tangents = super::generate_tangents_for_mesh(self)?;1729self.try_insert_attribute(Mesh::ATTRIBUTE_TANGENT, tangents)?;1730Ok(())1731}17321733/// Consumes the mesh and returns a mesh with tangents generated using the `mikktspace` algorithm.1734///1735/// The resulting mesh will have the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful.1736///1737/// (Alternatively, you can use [`Mesh::generate_tangents`] to mutate an existing mesh in-place)1738///1739/// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set.1740#[cfg(feature = "bevy_mikktspace")]1741pub fn with_generated_tangents(mut self) -> Result<Mesh, super::GenerateTangentsError> {1742self.generate_tangents()?;1743Ok(self)1744}17451746/// Merges the [`Mesh`] data of `other` with `self`. The attributes and indices of `other` will be appended to `self`.1747///1748/// Note that attributes of `other` that don't exist on `self` will be ignored.1749///1750/// `Aabb` of entities with modified mesh are not updated automatically.1751///1752/// # Errors1753///1754/// If any of the following conditions are not met, this function errors:1755/// * All of the vertex attributes that have the same attribute id, must also1756/// have the same attribute type.1757/// For example two attributes with the same id, but where one is a1758/// [`VertexAttributeValues::Float32`] and the other is a1759/// [`VertexAttributeValues::Float32x3`], would be invalid.1760/// * Both meshes must have the same primitive topology.1761pub fn merge(&mut self, other: &Mesh) -> Result<(), MeshMergeError> {1762use VertexAttributeValues::*;17631764// Check if the meshes `primitive_topology` field is the same,1765// as if that is not the case, the resulting mesh could (and most likely would)1766// be invalid.1767if self.primitive_topology != other.primitive_topology {1768return Err(MeshMergeError::IncompatiblePrimitiveTopology {1769self_primitive_topology: self.primitive_topology,1770other_primitive_topology: other.primitive_topology,1771});1772}17731774// The indices of `other` should start after the last vertex of `self`.1775let index_offset = self.count_vertices();17761777// Extend attributes of `self` with attributes of `other`.1778for (attribute, values) in self.try_attributes_mut()? {1779if let Some(other_values) = other.try_attribute_option(attribute.id)? {1780#[expect(1781clippy::match_same_arms,1782reason = "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."1783)]1784match (values, other_values) {1785(Float32(vec1), Float32(vec2)) => vec1.extend(vec2),1786(Sint32(vec1), Sint32(vec2)) => vec1.extend(vec2),1787(Uint32(vec1), Uint32(vec2)) => vec1.extend(vec2),1788(Float32x2(vec1), Float32x2(vec2)) => vec1.extend(vec2),1789(Sint32x2(vec1), Sint32x2(vec2)) => vec1.extend(vec2),1790(Uint32x2(vec1), Uint32x2(vec2)) => vec1.extend(vec2),1791(Float32x3(vec1), Float32x3(vec2)) => vec1.extend(vec2),1792(Sint32x3(vec1), Sint32x3(vec2)) => vec1.extend(vec2),1793(Uint32x3(vec1), Uint32x3(vec2)) => vec1.extend(vec2),1794(Sint32x4(vec1), Sint32x4(vec2)) => vec1.extend(vec2),1795(Uint32x4(vec1), Uint32x4(vec2)) => vec1.extend(vec2),1796(Float32x4(vec1), Float32x4(vec2)) => vec1.extend(vec2),1797(Sint16x2(vec1), Sint16x2(vec2)) => vec1.extend(vec2),1798(Snorm16x2(vec1), Snorm16x2(vec2)) => vec1.extend(vec2),1799(Uint16x2(vec1), Uint16x2(vec2)) => vec1.extend(vec2),1800(Unorm16x2(vec1), Unorm16x2(vec2)) => vec1.extend(vec2),1801(Sint16x4(vec1), Sint16x4(vec2)) => vec1.extend(vec2),1802(Snorm16x4(vec1), Snorm16x4(vec2)) => vec1.extend(vec2),1803(Uint16x4(vec1), Uint16x4(vec2)) => vec1.extend(vec2),1804(Unorm16x4(vec1), Unorm16x4(vec2)) => vec1.extend(vec2),1805(Sint8x2(vec1), Sint8x2(vec2)) => vec1.extend(vec2),1806(Snorm8x2(vec1), Snorm8x2(vec2)) => vec1.extend(vec2),1807(Uint8x2(vec1), Uint8x2(vec2)) => vec1.extend(vec2),1808(Unorm8x2(vec1), Unorm8x2(vec2)) => vec1.extend(vec2),1809(Sint8x4(vec1), Sint8x4(vec2)) => vec1.extend(vec2),1810(Snorm8x4(vec1), Snorm8x4(vec2)) => vec1.extend(vec2),1811(Uint8x4(vec1), Uint8x4(vec2)) => vec1.extend(vec2),1812(Unorm8x4(vec1), Unorm8x4(vec2)) => vec1.extend(vec2),1813_ => {1814return Err(MeshMergeError::IncompatibleVertexAttributes {1815self_attribute: *attribute,1816other_attribute: other1817.try_attribute_data(attribute.id)?1818.map(|data| data.attribute),1819})1820}1821}1822}1823}18241825// Extend indices of `self` with indices of `other`.1826if let (Some(indices), Some(other_indices)) =1827(self.try_indices_mut_option()?, other.try_indices_option()?)1828{1829indices.extend(other_indices.iter().map(|i| (i + index_offset) as u32));1830}1831Ok(())1832}18331834/// Transforms the vertex positions, normals, and tangents of the mesh by the given [`Transform`].1835///1836/// `Aabb` of entities with modified mesh are not updated automatically.1837///1838/// # Panics1839/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1840/// this as an error use [`Mesh::try_transformed_by`]1841pub fn transformed_by(mut self, transform: Transform) -> Self {1842self.transform_by(transform);1843self1844}18451846/// Transforms the vertex positions, normals, and tangents of the mesh by the given [`Transform`].1847///1848/// `Aabb` of entities with modified mesh are not updated automatically.1849pub fn try_transformed_by(mut self, transform: Transform) -> Result<Self, MeshAccessError> {1850self.try_transform_by(transform)?;1851Ok(self)1852}18531854/// Transforms the vertex positions, normals, and tangents of the mesh in place by the given [`Transform`].1855///1856/// `Aabb` of entities with modified mesh are not updated automatically.1857///1858/// # Panics1859/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1860/// this as an error use [`Mesh::try_transform_by`]1861pub fn transform_by(&mut self, transform: Transform) {1862self.try_transform_by(transform)1863.expect(MESH_EXTRACTED_ERROR);1864}18651866/// Transforms the vertex positions, normals, and tangents of the mesh in place by the given [`Transform`].1867///1868/// `Aabb` of entities with modified mesh are not updated automatically.1869pub fn try_transform_by(&mut self, transform: Transform) -> Result<(), MeshAccessError> {1870// Needed when transforming normals and tangents1871let scale_recip = 1. / transform.scale;1872debug_assert!(1873transform.scale.yzx() * transform.scale.zxy() != Vec3::ZERO,1874"mesh transform scale cannot be zero on more than one axis"1875);18761877if let Some(VertexAttributeValues::Float32x3(positions)) =1878self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?1879{1880// Apply scale, rotation, and translation to vertex positions1881positions1882.iter_mut()1883.for_each(|pos| *pos = transform.transform_point(Vec3::from_slice(pos)).to_array());1884}18851886// No need to transform normals or tangents if rotation is near identity and scale is uniform1887if transform.rotation.is_near_identity()1888&& transform.scale.x == transform.scale.y1889&& transform.scale.y == transform.scale.z1890{1891return Ok(());1892}18931894if let Some(VertexAttributeValues::Float32x3(normals)) =1895self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)?1896{1897// Transform normals, taking into account non-uniform scaling and rotation1898normals.iter_mut().for_each(|normal| {1899*normal = (transform.rotation1900* scale_normal(Vec3::from_array(*normal), scale_recip))1901.to_array();1902});1903}19041905if let Some(VertexAttributeValues::Float32x4(tangents)) =1906self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)?1907{1908// Transform tangents, taking into account non-uniform scaling and rotation1909tangents.iter_mut().for_each(|tangent| {1910let handedness = tangent[3];1911let scaled_tangent = Vec3::from_slice(tangent) * transform.scale;1912*tangent = (transform.rotation * scaled_tangent.normalize_or_zero())1913.extend(handedness)1914.to_array();1915});1916}19171918Ok(())1919}19201921/// Translates the vertex positions of the mesh by the given [`Vec3`].1922///1923/// `Aabb` of entities with modified mesh are not updated automatically.1924///1925/// # Panics1926/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1927/// this as an error use [`Mesh::try_translated_by`]1928pub fn translated_by(mut self, translation: Vec3) -> Self {1929self.translate_by(translation);1930self1931}19321933/// Translates the vertex positions of the mesh by the given [`Vec3`].1934///1935/// `Aabb` of entities with modified mesh are not updated automatically.1936pub fn try_translated_by(mut self, translation: Vec3) -> Result<Self, MeshAccessError> {1937self.try_translate_by(translation)?;1938Ok(self)1939}19401941/// Translates the vertex positions of the mesh in place by the given [`Vec3`].1942///1943/// `Aabb` of entities with modified mesh are not updated automatically.1944///1945/// # Panics1946/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1947/// this as an error use [`Mesh::try_translate_by`]1948pub fn translate_by(&mut self, translation: Vec3) {1949self.try_translate_by(translation)1950.expect(MESH_EXTRACTED_ERROR);1951}19521953/// Translates the vertex positions of the mesh in place by the given [`Vec3`].1954///1955/// `Aabb` of entities with modified mesh are not updated automatically.1956pub fn try_translate_by(&mut self, translation: Vec3) -> Result<(), MeshAccessError> {1957if translation == Vec3::ZERO {1958return Ok(());1959}19601961if let Some(VertexAttributeValues::Float32x3(positions)) =1962self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?1963{1964// Apply translation to vertex positions1965positions1966.iter_mut()1967.for_each(|pos| *pos = (Vec3::from_slice(pos) + translation).to_array());1968}19691970Ok(())1971}19721973/// Rotates the vertex positions, normals, and tangents of the mesh by the given [`Quat`].1974///1975/// `Aabb` of entities with modified mesh are not updated automatically.1976///1977/// # Panics1978/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1979/// this as an error use [`Mesh::try_rotated_by`]1980pub fn rotated_by(mut self, rotation: Quat) -> Self {1981self.try_rotate_by(rotation).expect(MESH_EXTRACTED_ERROR);1982self1983}19841985/// Rotates the vertex positions, normals, and tangents of the mesh by the given [`Quat`].1986///1987/// `Aabb` of entities with modified mesh are not updated automatically.1988pub fn try_rotated_by(mut self, rotation: Quat) -> Result<Self, MeshAccessError> {1989self.try_rotate_by(rotation)?;1990Ok(self)1991}19921993/// Rotates the vertex positions, normals, and tangents of the mesh in place by the given [`Quat`].1994///1995/// `Aabb` of entities with modified mesh are not updated automatically.1996///1997/// # Panics1998/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle1999/// this as an error use [`Mesh::try_rotate_by`]2000pub fn rotate_by(&mut self, rotation: Quat) {2001self.try_rotate_by(rotation).expect(MESH_EXTRACTED_ERROR);2002}20032004/// Rotates the vertex positions, normals, and tangents of the mesh in place by the given [`Quat`].2005///2006/// `Aabb` of entities with modified mesh are not updated automatically.2007pub fn try_rotate_by(&mut self, rotation: Quat) -> Result<(), MeshAccessError> {2008if let Some(VertexAttributeValues::Float32x3(positions)) =2009self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?2010{2011// Apply rotation to vertex positions2012positions2013.iter_mut()2014.for_each(|pos| *pos = (rotation * Vec3::from_slice(pos)).to_array());2015}20162017// No need to transform normals or tangents if rotation is near identity2018if rotation.is_near_identity() {2019return Ok(());2020}20212022if let Some(VertexAttributeValues::Float32x3(normals)) =2023self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)?2024{2025// Transform normals2026normals.iter_mut().for_each(|normal| {2027*normal = (rotation * Vec3::from_slice(normal).normalize_or_zero()).to_array();2028});2029}20302031if let Some(VertexAttributeValues::Float32x4(tangents)) =2032self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)?2033{2034// Transform tangents2035tangents.iter_mut().for_each(|tangent| {2036let handedness = tangent[3];2037*tangent = (rotation * Vec3::from_slice(tangent).normalize_or_zero())2038.extend(handedness)2039.to_array();2040});2041}20422043Ok(())2044}20452046/// Scales the vertex positions, normals, and tangents of the mesh by the given [`Vec3`].2047///2048/// `Aabb` of entities with modified mesh are not updated automatically.2049///2050/// # Panics2051/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle2052/// this as an error use [`Mesh::try_scaled_by`]2053pub fn scaled_by(mut self, scale: Vec3) -> Self {2054self.scale_by(scale);2055self2056}20572058/// Scales the vertex positions, normals, and tangents of the mesh by the given [`Vec3`].2059///2060/// `Aabb` of entities with modified mesh are not updated automatically.2061pub fn try_scaled_by(mut self, scale: Vec3) -> Result<Self, MeshAccessError> {2062self.try_scale_by(scale)?;2063Ok(self)2064}20652066/// Scales the vertex positions, normals, and tangents of the mesh in place by the given [`Vec3`].2067///2068/// `Aabb` of entities with modified mesh are not updated automatically.2069///2070/// # Panics2071/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle2072/// this as an error use [`Mesh::try_scale_by`]2073pub fn scale_by(&mut self, scale: Vec3) {2074self.try_scale_by(scale).expect(MESH_EXTRACTED_ERROR);2075}20762077/// Scales the vertex positions, normals, and tangents of the mesh in place by the given [`Vec3`].2078///2079/// `Aabb` of entities with modified mesh are not updated automatically.2080pub fn try_scale_by(&mut self, scale: Vec3) -> Result<(), MeshAccessError> {2081// Needed when transforming normals and tangents2082let scale_recip = 1. / scale;2083debug_assert!(2084scale.yzx() * scale.zxy() != Vec3::ZERO,2085"mesh transform scale cannot be zero on more than one axis"2086);20872088if let Some(VertexAttributeValues::Float32x3(positions)) =2089self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?2090{2091// Apply scale to vertex positions2092positions2093.iter_mut()2094.for_each(|pos| *pos = (scale * Vec3::from_slice(pos)).to_array());2095}20962097// No need to transform normals or tangents if scale is uniform2098if scale.x == scale.y && scale.y == scale.z {2099return Ok(());2100}21012102if let Some(VertexAttributeValues::Float32x3(normals)) =2103self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)?2104{2105// Transform normals, taking into account non-uniform scaling2106normals.iter_mut().for_each(|normal| {2107*normal = scale_normal(Vec3::from_array(*normal), scale_recip).to_array();2108});2109}21102111if let Some(VertexAttributeValues::Float32x4(tangents)) =2112self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)?2113{2114// Transform tangents, taking into account non-uniform scaling2115tangents.iter_mut().for_each(|tangent| {2116let handedness = tangent[3];2117let scaled_tangent = Vec3::from_slice(tangent) * scale;2118*tangent = scaled_tangent2119.normalize_or_zero()2120.extend(handedness)2121.to_array();2122});2123}21242125Ok(())2126}21272128/// Normalize joint weights so they sum to 1.2129///2130/// # Panics2131/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle2132/// this as an error use [`Mesh::try_normalize_joint_weights`]2133pub fn normalize_joint_weights(&mut self) {2134self.try_normalize_joint_weights()2135.expect(MESH_EXTRACTED_ERROR);2136}21372138/// Normalize joint weights so they sum to 1.2139pub fn try_normalize_joint_weights(&mut self) -> Result<(), MeshAccessError> {2140if let Some(VertexAttributeValues::Float32x4(joints)) =2141self.try_attribute_mut_option(Self::ATTRIBUTE_JOINT_WEIGHT)?2142{2143for weights in joints.iter_mut() {2144// force negative weights to zero2145weights.iter_mut().for_each(|w| *w = w.max(0.0));21462147let sum: f32 = weights.iter().sum();2148if sum == 0.0 {2149// all-zero weights are invalid2150weights[0] = 1.0;2151} else {2152let recip = sum.recip();2153for weight in weights.iter_mut() {2154*weight *= recip;2155}2156}2157}2158}21592160Ok(())2161}21622163/// Get a list of this Mesh's [triangles] as an iterator if possible.2164///2165/// Returns an error if any of the following conditions are met (see [`MeshTrianglesError`]):2166/// * The Mesh's [primitive topology] is not `TriangleList` or `TriangleStrip`.2167/// * The Mesh is missing position or index data.2168/// * The Mesh's position data has the wrong format (not `Float32x3`).2169///2170/// [primitive topology]: PrimitiveTopology2171/// [triangles]: Triangle3d2172pub fn triangles(&self) -> Result<impl Iterator<Item = Triangle3d> + '_, MeshTrianglesError> {2173let position_data = self.try_attribute(Mesh::ATTRIBUTE_POSITION)?;21742175let Some(vertices) = position_data.as_float3() else {2176return Err(MeshTrianglesError::PositionsFormat);2177};21782179let indices = self.try_indices()?;21802181match self.primitive_topology {2182PrimitiveTopology::TriangleList => {2183// When indices reference out-of-bounds vertex data, the triangle is omitted.2184// This implicitly truncates the indices to a multiple of 3.2185let iterator = match indices {2186Indices::U16(vec) => FourIterators::First(2187vec.as_slice()2188.chunks_exact(3)2189.flat_map(move |indices| indices_to_triangle(vertices, indices)),2190),2191Indices::U32(vec) => FourIterators::Second(2192vec.as_slice()2193.chunks_exact(3)2194.flat_map(move |indices| indices_to_triangle(vertices, indices)),2195),2196};21972198return Ok(iterator);2199}22002201PrimitiveTopology::TriangleStrip => {2202// When indices reference out-of-bounds vertex data, the triangle is omitted.2203// If there aren't enough indices to make a triangle, then an empty vector will be2204// returned.2205let iterator = match indices {2206Indices::U16(vec) => {2207FourIterators::Third(vec.as_slice().windows(3).enumerate().flat_map(2208move |(i, indices)| {2209if i % 2 == 0 {2210indices_to_triangle(vertices, indices)2211} else {2212indices_to_triangle(2213vertices,2214&[indices[1], indices[0], indices[2]],2215)2216}2217},2218))2219}2220Indices::U32(vec) => {2221FourIterators::Fourth(vec.as_slice().windows(3).enumerate().flat_map(2222move |(i, indices)| {2223if i % 2 == 0 {2224indices_to_triangle(vertices, indices)2225} else {2226indices_to_triangle(2227vertices,2228&[indices[1], indices[0], indices[2]],2229)2230}2231},2232))2233}2234};22352236return Ok(iterator);2237}22382239_ => {2240return Err(MeshTrianglesError::WrongTopology);2241}2242};22432244fn indices_to_triangle<T: TryInto<usize> + Copy>(2245vertices: &[[f32; 3]],2246indices: &[T],2247) -> Option<Triangle3d> {2248let vert0: Vec3 = Vec3::from(*vertices.get(indices[0].try_into().ok()?)?);2249let vert1: Vec3 = Vec3::from(*vertices.get(indices[1].try_into().ok()?)?);2250let vert2: Vec3 = Vec3::from(*vertices.get(indices[2].try_into().ok()?)?);2251Some(Triangle3d {2252vertices: [vert0, vert1, vert2],2253})2254}2255}22562257/// Extracts the mesh vertex, index and morph target data for GPU upload.2258/// This function is called internally in render world extraction, it is2259/// unlikely to be useful outside of that context.2260///2261/// Returns an error if the mesh data has been extracted to `RenderWorld`.2262pub fn take_gpu_data(&mut self) -> Result<Self, MeshAccessError> {2263let attributes = self.attributes.extract()?;2264let indices = self.indices.extract()?;2265#[cfg(feature = "morph")]2266let morph_targets = self.morph_targets.extract()?;2267#[cfg(feature = "morph")]2268let morph_target_names = self.morph_target_names.extract()?;22692270// store the aabb extents as they cannot be computed after extraction2271if let Some(MeshAttributeData {2272values: VertexAttributeValues::Float32x3(position_values),2273..2274}) = attributes2275.as_ref_option()?2276.and_then(|attrs| attrs.get(&Self::ATTRIBUTE_POSITION.id))2277&& !position_values.is_empty()2278{2279let mut iter = position_values.iter().map(|p| Vec3::from_slice(p));2280let mut min = iter.next().unwrap();2281let mut max = min;2282for v in iter {2283min = Vec3::min(min, v);2284max = Vec3::max(max, v);2285}2286self.final_aabb = Some(Aabb3d::from_min_max(min, max));2287}22882289Ok(Self {2290attributes,2291indices,2292#[cfg(feature = "morph")]2293morph_targets,2294#[cfg(feature = "morph")]2295morph_target_names,2296..self.clone()2297})2298}22992300/// Get this mesh's [`SkinnedMeshBounds`].2301pub fn skinned_mesh_bounds(&self) -> Option<&SkinnedMeshBounds> {2302self.skinned_mesh_bounds.as_ref()2303}23042305/// Set this mesh's [`SkinnedMeshBounds`].2306pub fn set_skinned_mesh_bounds(&mut self, skinned_mesh_bounds: Option<SkinnedMeshBounds>) {2307self.skinned_mesh_bounds = skinned_mesh_bounds;2308}23092310/// Consumes the mesh and returns a mesh with the given [`SkinnedMeshBounds`].2311pub fn with_skinned_mesh_bounds(2312mut self,2313skinned_mesh_bounds: Option<SkinnedMeshBounds>,2314) -> Self {2315self.set_skinned_mesh_bounds(skinned_mesh_bounds);2316self2317}23182319/// Generate [`SkinnedMeshBounds`] for this mesh.2320pub fn generate_skinned_mesh_bounds(&mut self) -> Result<(), SkinnedMeshBoundsError> {2321self.skinned_mesh_bounds = Some(SkinnedMeshBounds::from_mesh(self)?);2322Ok(())2323}23242325/// Consumes the mesh and returns a mesh with generated [`SkinnedMeshBounds`].2326pub fn with_generated_skinned_mesh_bounds(mut self) -> Result<Self, SkinnedMeshBoundsError> {2327self.generate_skinned_mesh_bounds()?;2328Ok(self)2329}2330}23312332#[cfg(feature = "morph")]2333impl Mesh {2334/// Whether this mesh has morph targets.2335///2336/// # Panics2337/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle2338/// this as an error use [`Mesh::try_has_morph_targets`]2339pub fn has_morph_targets(&self) -> bool {2340self.try_has_morph_targets().expect(MESH_EXTRACTED_ERROR)2341}23422343/// Whether this mesh has morph targets.2344pub fn try_has_morph_targets(&self) -> Result<bool, MeshAccessError> {2345Ok(self.morph_targets.as_ref_option()?.is_some())2346}23472348/// Set [morph targets] image for this mesh. This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.2349///2350/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation2351///2352/// # Panics2353/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle2354/// this as an error use [`Mesh::try_set_morph_targets`]2355pub fn set_morph_targets(&mut self, morph_targets: Handle<Image>) {2356self.try_set_morph_targets(morph_targets)2357.expect(MESH_EXTRACTED_ERROR);2358}23592360/// Set [morph targets] image for this mesh. This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.2361///2362/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation2363pub fn try_set_morph_targets(2364&mut self,2365morph_targets: Handle<Image>,2366) -> Result<(), MeshAccessError> {2367self.morph_targets.replace(Some(morph_targets))?;2368Ok(())2369}23702371/// Retrieve the morph targets for this mesh, or None if there are no morph targets.2372/// # Panics2373/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle2374/// this as an error use [`Mesh::try_morph_targets`]2375pub fn morph_targets(&self) -> Option<&Handle<Image>> {2376self.morph_targets2377.as_ref_option()2378.expect(MESH_EXTRACTED_ERROR)2379}23802381/// Retrieve the morph targets for this mesh, or None if there are no morph targets.2382///2383/// Returns an error if the mesh data has been extracted to `RenderWorld`or2384/// if the morph targets do not exist.2385pub fn try_morph_targets(&self) -> Result<&Handle<Image>, MeshAccessError> {2386self.morph_targets.as_ref()2387}23882389/// Consumes the mesh and returns a mesh with the given [morph targets].2390///2391/// This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.2392///2393/// (Alternatively, you can use [`Mesh::set_morph_targets`] to mutate an existing mesh in-place)2394///2395/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation2396///2397/// # Panics2398/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle2399/// this as an error use [`Mesh::try_with_morph_targets`]2400#[must_use]2401pub fn with_morph_targets(mut self, morph_targets: Handle<Image>) -> Self {2402self.set_morph_targets(morph_targets);2403self2404}24052406/// Consumes the mesh and returns a mesh with the given [morph targets].2407///2408/// This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.2409///2410/// (Alternatively, you can use [`Mesh::set_morph_targets`] to mutate an existing mesh in-place)2411///2412/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation2413///2414/// Returns an error if the mesh data has been extracted to `RenderWorld`.2415pub fn try_with_morph_targets(2416mut self,2417morph_targets: Handle<Image>,2418) -> Result<Self, MeshAccessError> {2419self.try_set_morph_targets(morph_targets)?;2420Ok(self)2421}24222423/// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`.2424///2425/// # Panics2426/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle2427/// this as an error use [`Mesh::try_set_morph_target_names`]2428pub fn set_morph_target_names(&mut self, names: Vec<String>) {2429self.try_set_morph_target_names(names)2430.expect(MESH_EXTRACTED_ERROR);2431}24322433/// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`.2434///2435/// Returns an error if the mesh data has been extracted to `RenderWorld`.2436pub fn try_set_morph_target_names(2437&mut self,2438names: Vec<String>,2439) -> Result<(), MeshAccessError> {2440self.morph_target_names.replace(Some(names))?;2441Ok(())2442}24432444/// Consumes the mesh and returns a mesh with morph target names.2445/// Names should correspond to the order of the morph targets in `set_morph_targets`.2446///2447/// (Alternatively, you can use [`Mesh::set_morph_target_names`] to mutate an existing mesh in-place)2448///2449/// # Panics2450/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle2451/// this as an error use [`Mesh::try_set_morph_target_names`]2452#[must_use]2453pub fn with_morph_target_names(self, names: Vec<String>) -> Self {2454self.try_with_morph_target_names(names)2455.expect(MESH_EXTRACTED_ERROR)2456}24572458/// Consumes the mesh and returns a mesh with morph target names.2459/// Names should correspond to the order of the morph targets in `set_morph_targets`.2460///2461/// (Alternatively, you can use [`Mesh::set_morph_target_names`] to mutate an existing mesh in-place)2462///2463/// Returns an error if the mesh data has been extracted to `RenderWorld`.2464pub fn try_with_morph_target_names(2465mut self,2466names: Vec<String>,2467) -> Result<Self, MeshAccessError> {2468self.try_set_morph_target_names(names)?;2469Ok(self)2470}24712472/// Gets a list of all morph target names, if they exist.2473///2474/// # Panics2475/// Panics when the mesh data has already been extracted to `RenderWorld`. To handle2476/// this as an error use [`Mesh::try_morph_target_names`]2477pub fn morph_target_names(&self) -> Option<&[String]> {2478self.try_morph_target_names().expect(MESH_EXTRACTED_ERROR)2479}24802481/// Gets a list of all morph target names, if they exist.2482///2483/// Returns an error if the mesh data has been extracted to `RenderWorld`or2484/// if the morph targets do not exist.2485pub fn try_morph_target_names(&self) -> Result<Option<&[String]>, MeshAccessError> {2486Ok(self2487.morph_target_names2488.as_ref_option()?2489.map(core::ops::Deref::deref))2490}2491}24922493/// An enum to define which UV attribute to use for a texture.2494///2495/// It only supports two UV attributes, [`Mesh::ATTRIBUTE_UV_0`] and2496/// [`Mesh::ATTRIBUTE_UV_1`].2497/// The default is [`UvChannel::Uv0`].2498#[derive(Reflect, Default, Debug, Clone, PartialEq, Eq)]2499#[reflect(Default, Debug, Clone, PartialEq)]2500pub enum UvChannel {2501#[default]2502Uv0,2503Uv1,2504}25052506/// Correctly scales and renormalizes an already normalized `normal` by the scale determined by its reciprocal `scale_recip`2507pub(crate) fn scale_normal(normal: Vec3, scale_recip: Vec3) -> Vec3 {2508// This is basically just `normal * scale_recip` but with the added rule that `0. * anything == 0.`2509// This is necessary because components of `scale_recip` may be infinities, which do not multiply to zero2510let n = Vec3::select(normal.cmpeq(Vec3::ZERO), Vec3::ZERO, normal * scale_recip);25112512// If n is finite, no component of `scale_recip` was infinite or the normal was perpendicular to the scale2513// else the scale had at least one zero-component and the normal needs to point along the direction of that component2514if n.is_finite() {2515n.normalize_or_zero()2516} else {2517Vec3::select(n.abs().cmpeq(Vec3::INFINITY), n.signum(), Vec3::ZERO).normalize()2518}2519}25202521impl core::ops::Mul<Mesh> for Transform {2522type Output = Mesh;25232524fn mul(self, rhs: Mesh) -> Self::Output {2525rhs.transformed_by(self)2526}2527}25282529/// A version of [`Mesh`] suitable for serializing for short-term transfer.2530///2531/// [`Mesh`] does not implement [`Serialize`] / [`Deserialize`] because it is made with the renderer in mind.2532/// It is not a general-purpose mesh implementation, and its internals are subject to frequent change.2533/// As such, storing a [`Mesh`] on disk is highly discouraged.2534///2535/// But there are still some valid use cases for serializing a [`Mesh`], namely transferring meshes between processes.2536/// To support this, you can create a [`SerializedMesh`] from a [`Mesh`] with [`SerializedMesh::from_mesh`],2537/// and then deserialize it with [`SerializedMesh::deserialize`]. The caveats are:2538/// - The mesh representation is not valid across different versions of Bevy.2539/// - This conversion is lossy. Only the following information is preserved:2540/// - Primitive topology2541/// - Vertex attributes2542/// - Indices2543/// - Custom attributes that were not specified with [`MeshDeserializer::add_custom_vertex_attribute`] will be ignored while deserializing.2544#[cfg(feature = "serialize")]2545#[derive(Debug, Clone, Serialize, Deserialize)]2546pub struct SerializedMesh {2547primitive_topology: PrimitiveTopology,2548attributes: Vec<(MeshVertexAttributeId, SerializedMeshAttributeData)>,2549indices: Option<Indices>,2550}25512552#[cfg(feature = "serialize")]2553impl SerializedMesh {2554/// Create a [`SerializedMesh`] from a [`Mesh`]. See the documentation for [`SerializedMesh`] for caveats.2555pub fn from_mesh(mut mesh: Mesh) -> Self {2556Self {2557primitive_topology: mesh.primitive_topology,2558attributes: mesh2559.attributes2560.replace(None)2561.expect(MESH_EXTRACTED_ERROR)2562.unwrap()2563.into_iter()2564.map(|(id, data)| {2565(2566id,2567SerializedMeshAttributeData::from_mesh_attribute_data(data),2568)2569})2570.collect(),2571indices: mesh.indices.replace(None).expect(MESH_EXTRACTED_ERROR),2572}2573}25742575/// Create a [`Mesh`] from a [`SerializedMesh`]. See the documentation for [`SerializedMesh`] for caveats.2576///2577/// Use [`MeshDeserializer`] if you need to pass extra options to the deserialization process, such as specifying custom vertex attributes.2578pub fn into_mesh(self) -> Mesh {2579MeshDeserializer::default().deserialize(self)2580}2581}25822583/// Use to specify extra options when deserializing a [`SerializedMesh`] into a [`Mesh`].2584#[cfg(feature = "serialize")]2585pub struct MeshDeserializer {2586custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,2587}25882589#[cfg(feature = "serialize")]2590impl Default for MeshDeserializer {2591fn default() -> Self {2592// Written like this so that the compiler can validate that we use all the built-in attributes.2593// If you just added a new attribute and got a compile error, please add it to this list :)2594const BUILTINS: [MeshVertexAttribute; Mesh::FIRST_AVAILABLE_CUSTOM_ATTRIBUTE as usize] = [2595Mesh::ATTRIBUTE_POSITION,2596Mesh::ATTRIBUTE_NORMAL,2597Mesh::ATTRIBUTE_UV_0,2598Mesh::ATTRIBUTE_UV_1,2599Mesh::ATTRIBUTE_TANGENT,2600Mesh::ATTRIBUTE_COLOR,2601Mesh::ATTRIBUTE_JOINT_WEIGHT,2602Mesh::ATTRIBUTE_JOINT_INDEX,2603];2604Self {2605custom_vertex_attributes: BUILTINS2606.into_iter()2607.map(|attribute| (attribute.name.into(), attribute))2608.collect(),2609}2610}2611}26122613#[cfg(feature = "serialize")]2614impl MeshDeserializer {2615/// Create a new [`MeshDeserializer`].2616pub fn new() -> Self {2617Self::default()2618}26192620/// Register a custom vertex attribute to the deserializer. Custom vertex attributes that were not added with this method will be ignored while deserializing.2621pub fn add_custom_vertex_attribute(2622&mut self,2623name: &str,2624attribute: MeshVertexAttribute,2625) -> &mut Self {2626self.custom_vertex_attributes.insert(name.into(), attribute);2627self2628}26292630/// Deserialize a [`SerializedMesh`] into a [`Mesh`].2631///2632/// See the documentation for [`SerializedMesh`] for caveats.2633pub fn deserialize(&self, serialized_mesh: SerializedMesh) -> Mesh {2634Mesh {2635attributes: MeshExtractableData::Data(2636serialized_mesh2637.attributes2638.into_iter()2639.filter_map(|(id, data)| {2640let attribute = data.attribute.clone();2641let Some(data) =2642data.try_into_mesh_attribute_data(&self.custom_vertex_attributes)2643else {2644warn!(2645"Deserialized mesh contains custom vertex attribute {attribute:?} that \2646was not specified with `MeshDeserializer::add_custom_vertex_attribute`. Ignoring."2647);2648return None;2649};2650Some((id, data))2651})2652.collect()),2653indices: serialized_mesh.indices.into(),2654..Mesh::new(serialized_mesh.primitive_topology, RenderAssetUsages::default())2655}2656}2657}26582659/// Error that can occur when calling [`Mesh::merge_duplicate_vertices`]2660#[derive(Error, Debug, Clone)]2661pub enum MeshMergeDuplicateVerticesError {2662#[error("Index attribute already set.")]2663IndicesAlreadySet,2664#[error("Mesh access error: {0}")]2665MeshAccessError(#[from] MeshAccessError),2666}26672668/// Error that can occur when calling [`Mesh::merge`].2669#[derive(Error, Debug, Clone)]2670pub enum MeshMergeError {2671#[error("Incompatible vertex attribute types: {} and {}", self_attribute.name, other_attribute.map(|a| a.name).unwrap_or("None"))]2672IncompatibleVertexAttributes {2673self_attribute: MeshVertexAttribute,2674other_attribute: Option<MeshVertexAttribute>,2675},2676#[error(2677"Incompatible primitive topologies: {:?} and {:?}",2678self_primitive_topology,2679other_primitive_topology2680)]2681IncompatiblePrimitiveTopology {2682self_primitive_topology: PrimitiveTopology,2683other_primitive_topology: PrimitiveTopology,2684},2685#[error("Mesh access error: {0}")]2686MeshAccessError(#[from] MeshAccessError),2687}26882689#[cfg(test)]2690mod tests {2691use super::Mesh;2692#[cfg(feature = "serialize")]2693use super::SerializedMesh;2694use crate::mesh::{Indices, MeshWindingInvertError, VertexAttributeValues};2695use crate::PrimitiveTopology;2696use bevy_asset::RenderAssetUsages;2697use bevy_math::bounding::Aabb3d;2698use bevy_math::primitives::Triangle3d;2699use bevy_math::Vec3;2700use bevy_transform::components::Transform;27012702#[test]2703#[should_panic]2704fn panic_invalid_format() {2705let _mesh = Mesh::new(2706PrimitiveTopology::TriangleList,2707RenderAssetUsages::default(),2708)2709.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0, 0.0]]);2710}27112712#[test]2713fn transform_mesh() {2714let mesh = Mesh::new(2715PrimitiveTopology::TriangleList,2716RenderAssetUsages::default(),2717)2718.with_inserted_attribute(2719Mesh::ATTRIBUTE_POSITION,2720vec![[-1., -1., 2.], [1., -1., 2.], [0., 1., 2.]],2721)2722.with_inserted_attribute(2723Mesh::ATTRIBUTE_NORMAL,2724vec![2725Vec3::new(-1., -1., 1.).normalize().to_array(),2726Vec3::new(1., -1., 1.).normalize().to_array(),2727[0., 0., 1.],2728],2729)2730.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0., 0.], [1., 0.], [0.5, 1.]]);27312732let mesh = mesh.transformed_by(2733Transform::from_translation(Vec3::splat(-2.)).with_scale(Vec3::new(2., 0., -1.)),2734);27352736if let Some(VertexAttributeValues::Float32x3(positions)) =2737mesh.attribute(Mesh::ATTRIBUTE_POSITION)2738{2739// All positions are first scaled resulting in `vec![[-2, 0., -2.], [2., 0., -2.], [0., 0., -2.]]`2740// and then shifted by `-2.` along each axis2741assert_eq!(2742positions,2743&vec![[-4.0, -2.0, -4.0], [0.0, -2.0, -4.0], [-2.0, -2.0, -4.0]]2744);2745} else {2746panic!("Mesh does not have a position attribute");2747}27482749if let Some(VertexAttributeValues::Float32x3(normals)) =2750mesh.attribute(Mesh::ATTRIBUTE_NORMAL)2751{2752assert_eq!(normals, &vec![[0., -1., 0.], [0., -1., 0.], [0., 0., -1.]]);2753} else {2754panic!("Mesh does not have a normal attribute");2755}27562757if let Some(VertexAttributeValues::Float32x2(uvs)) = mesh.attribute(Mesh::ATTRIBUTE_UV_0) {2758assert_eq!(uvs, &vec![[0., 0.], [1., 0.], [0.5, 1.]]);2759} else {2760panic!("Mesh does not have a uv attribute");2761}2762}27632764#[test]2765fn point_list_mesh_invert_winding() {2766let mesh = Mesh::new(PrimitiveTopology::PointList, RenderAssetUsages::default())2767.with_inserted_indices(Indices::U32(vec![]));2768assert!(matches!(2769mesh.with_inverted_winding(),2770Err(MeshWindingInvertError::WrongTopology)2771));2772}27732774#[test]2775fn line_list_mesh_invert_winding() {2776let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())2777.with_inserted_indices(Indices::U32(vec![0, 1, 1, 2, 2, 3]));2778let mesh = mesh.with_inverted_winding().unwrap();2779assert_eq!(2780mesh.indices().unwrap().iter().collect::<Vec<usize>>(),2781vec![3, 2, 2, 1, 1, 0]2782);2783}27842785#[test]2786fn line_list_mesh_invert_winding_fail() {2787let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())2788.with_inserted_indices(Indices::U32(vec![0, 1, 1]));2789assert!(matches!(2790mesh.with_inverted_winding(),2791Err(MeshWindingInvertError::AbruptIndicesEnd)2792));2793}27942795#[test]2796fn line_strip_mesh_invert_winding() {2797let mesh = Mesh::new(PrimitiveTopology::LineStrip, RenderAssetUsages::default())2798.with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));2799let mesh = mesh.with_inverted_winding().unwrap();2800assert_eq!(2801mesh.indices().unwrap().iter().collect::<Vec<usize>>(),2802vec![3, 2, 1, 0]2803);2804}28052806#[test]2807fn triangle_list_mesh_invert_winding() {2808let mesh = Mesh::new(2809PrimitiveTopology::TriangleList,2810RenderAssetUsages::default(),2811)2812.with_inserted_indices(Indices::U32(vec![28130, 3, 1, // First triangle28141, 3, 2, // Second triangle2815]));2816let mesh = mesh.with_inverted_winding().unwrap();2817assert_eq!(2818mesh.indices().unwrap().iter().collect::<Vec<usize>>(),2819vec![28200, 1, 3, // First triangle28211, 2, 3, // Second triangle2822]2823);2824}28252826#[test]2827fn triangle_list_mesh_invert_winding_fail() {2828let mesh = Mesh::new(2829PrimitiveTopology::TriangleList,2830RenderAssetUsages::default(),2831)2832.with_inserted_indices(Indices::U32(vec![0, 3, 1, 2]));2833assert!(matches!(2834mesh.with_inverted_winding(),2835Err(MeshWindingInvertError::AbruptIndicesEnd)2836));2837}28382839#[test]2840fn triangle_strip_mesh_invert_winding() {2841let mesh = Mesh::new(2842PrimitiveTopology::TriangleStrip,2843RenderAssetUsages::default(),2844)2845.with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));2846let mesh = mesh.with_inverted_winding().unwrap();2847assert_eq!(2848mesh.indices().unwrap().iter().collect::<Vec<usize>>(),2849vec![3, 2, 1, 0]2850);2851}28522853#[test]2854fn compute_area_weighted_normals() {2855let mut mesh = Mesh::new(2856PrimitiveTopology::TriangleList,2857RenderAssetUsages::default(),2858);28592860// z y2861// | /2862// 3---22863// | / \2864// 0-----1--x28652866mesh.insert_attribute(2867Mesh::ATTRIBUTE_POSITION,2868vec![[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]],2869);2870mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));2871mesh.compute_area_weighted_normals();2872let normals = mesh2873.attribute(Mesh::ATTRIBUTE_NORMAL)2874.unwrap()2875.as_float3()2876.unwrap();2877assert_eq!(4, normals.len());2878// 02879assert_eq!(Vec3::new(1., 0., 1.).normalize().to_array(), normals[0]);2880// 12881assert_eq!([0., 0., 1.], normals[1]);2882// 22883assert_eq!(Vec3::new(1., 0., 1.).normalize().to_array(), normals[2]);2884// 32885assert_eq!([1., 0., 0.], normals[3]);2886}28872888#[test]2889fn compute_area_weighted_normals_proportionate() {2890let mut mesh = Mesh::new(2891PrimitiveTopology::TriangleList,2892RenderAssetUsages::default(),2893);28942895// z y2896// | /2897// 3---2..2898// | / \2899// 0-------1---x29002901mesh.insert_attribute(2902Mesh::ATTRIBUTE_POSITION,2903vec![[0., 0., 0.], [2., 0., 0.], [0., 1., 0.], [0., 0., 1.]],2904);2905mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));2906mesh.compute_area_weighted_normals();2907let normals = mesh2908.attribute(Mesh::ATTRIBUTE_NORMAL)2909.unwrap()2910.as_float3()2911.unwrap();2912assert_eq!(4, normals.len());2913// 02914assert_eq!(Vec3::new(1., 0., 2.).normalize().to_array(), normals[0]);2915// 12916assert_eq!([0., 0., 1.], normals[1]);2917// 22918assert_eq!(Vec3::new(1., 0., 2.).normalize().to_array(), normals[2]);2919// 32920assert_eq!([1., 0., 0.], normals[3]);2921}29222923#[test]2924fn compute_angle_weighted_normals() {2925// CuboidMeshBuilder duplicates vertices (even though it is indexed)29262927// 5---------42928// /| /|2929// 1-+-------0 |2930// | 6-------|-72931// |/ |/2932// 2---------32933let verts = vec![2934[1.0, 1.0, 1.0],2935[-1.0, 1.0, 1.0],2936[-1.0, -1.0, 1.0],2937[1.0, -1.0, 1.0],2938[1.0, 1.0, -1.0],2939[-1.0, 1.0, -1.0],2940[-1.0, -1.0, -1.0],2941[1.0, -1.0, -1.0],2942];29432944let indices = Indices::U16(vec![29450, 1, 2, 2, 3, 0, // front29465, 4, 7, 7, 6, 5, // back29471, 5, 6, 6, 2, 1, // left29484, 0, 3, 3, 7, 4, // right29494, 5, 1, 1, 0, 4, // top29503, 2, 6, 6, 7, 3, // bottom2951]);2952let mut mesh = Mesh::new(2953PrimitiveTopology::TriangleList,2954RenderAssetUsages::default(),2955);2956mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, verts);2957mesh.insert_indices(indices);2958mesh.compute_smooth_normals();29592960let normals = mesh2961.attribute(Mesh::ATTRIBUTE_NORMAL)2962.unwrap()2963.as_float3()2964.unwrap();29652966for new in normals.iter().copied().flatten() {2967// std impl is unstable2968const FRAC_1_SQRT_3: f32 = 0.57735026;2969const MIN: f32 = FRAC_1_SQRT_3 - f32::EPSILON;2970const MAX: f32 = FRAC_1_SQRT_3 + f32::EPSILON;2971assert!(new.abs() >= MIN, "{new} < {MIN}");2972assert!(new.abs() <= MAX, "{new} > {MAX}");2973}2974}29752976#[test]2977fn triangles_from_triangle_list() {2978let mut mesh = Mesh::new(2979PrimitiveTopology::TriangleList,2980RenderAssetUsages::default(),2981);2982mesh.insert_attribute(2983Mesh::ATTRIBUTE_POSITION,2984vec![[0., 0., 0.], [1., 0., 0.], [1., 1., 0.], [0., 1., 0.]],2985);2986mesh.insert_indices(Indices::U32(vec![0, 1, 2, 2, 3, 0]));2987assert_eq!(2988vec![2989Triangle3d {2990vertices: [2991Vec3::new(0., 0., 0.),2992Vec3::new(1., 0., 0.),2993Vec3::new(1., 1., 0.),2994]2995},2996Triangle3d {2997vertices: [2998Vec3::new(1., 1., 0.),2999Vec3::new(0., 1., 0.),3000Vec3::new(0., 0., 0.),3001]3002}3003],3004mesh.triangles().unwrap().collect::<Vec<Triangle3d>>()3005);3006}30073008#[test]3009fn triangles_from_triangle_strip() {3010let mut mesh = Mesh::new(3011PrimitiveTopology::TriangleStrip,3012RenderAssetUsages::default(),3013);3014// Triangles: (0, 1, 2), (2, 1, 3), (2, 3, 4), (4, 3, 5)3015//3016// 4 - 53017// | \ |3018// 2 - 33019// | \ |3020// 0 - 13021let positions: Vec<Vec3> = [3022[0., 0., 0.],3023[1., 0., 0.],3024[0., 1., 0.],3025[1., 1., 0.],3026[0., 2., 0.],3027[1., 2., 0.],3028]3029.into_iter()3030.map(Vec3::from_array)3031.collect();3032mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions.clone());3033mesh.insert_indices(Indices::U32(vec![0, 1, 2, 3, 4, 5]));3034assert_eq!(3035vec![3036Triangle3d {3037vertices: [positions[0], positions[1], positions[2]]3038},3039Triangle3d {3040vertices: [positions[2], positions[1], positions[3]]3041},3042Triangle3d {3043vertices: [positions[2], positions[3], positions[4]]3044},3045Triangle3d {3046vertices: [positions[4], positions[3], positions[5]]3047},3048],3049mesh.triangles().unwrap().collect::<Vec<Triangle3d>>()3050);3051}30523053#[test]3054fn take_gpu_data_calculates_aabb() {3055let mut mesh = Mesh::new(3056PrimitiveTopology::TriangleList,3057RenderAssetUsages::default(),3058);3059mesh.insert_attribute(3060Mesh::ATTRIBUTE_POSITION,3061vec![3062[-0.5, 0., 0.],3063[-1., 0., 0.],3064[-1., -1., 0.],3065[-0.5, -1., 0.],3066],3067);3068mesh.insert_indices(Indices::U32(vec![0, 1, 2, 2, 3, 0]));3069mesh = mesh.take_gpu_data().unwrap();3070assert_eq!(3071mesh.final_aabb,3072Some(Aabb3d::from_min_max([-1., -1., 0.], [-0.5, 0., 0.]))3073);3074}30753076#[cfg(feature = "serialize")]3077#[test]3078fn serialize_deserialize_mesh() {3079let mut mesh = Mesh::new(3080PrimitiveTopology::TriangleList,3081RenderAssetUsages::default(),3082);30833084mesh.insert_attribute(3085Mesh::ATTRIBUTE_POSITION,3086vec![[0., 0., 0.], [2., 0., 0.], [0., 1., 0.], [0., 0., 1.]],3087);3088mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));30893090let serialized_mesh = SerializedMesh::from_mesh(mesh.clone());3091let serialized_string = serde_json::to_string(&serialized_mesh).unwrap();3092let serialized_mesh_from_string: SerializedMesh =3093serde_json::from_str(&serialized_string).unwrap();3094let deserialized_mesh = serialized_mesh_from_string.into_mesh();3095assert_eq!(mesh, deserialized_mesh);3096}30973098#[test]3099fn merge_duplicate_vertices() {3100let mut mesh = Mesh::new(3101PrimitiveTopology::TriangleList,3102RenderAssetUsages::default(),3103);3104// Quad made of two triangles.3105let positions = vec![3106[0.0, 0.0, 0.0],3107[1.0, 0.0, 0.0],3108[1.0, 1.0, 0.0],3109// This will be deduplicated.3110[1.0, 1.0, 0.0],3111[0.0, 1.0, 0.0],3112// Position is equal to the first one but UV is different so it won't be deduplicated.3113[0.0, 0.0, 0.0],3114];3115let uvs = vec![3116[0.0, 0.0],3117[1.0, 0.0],3118[1.0, 1.0],3119// This will be deduplicated.3120[1.0, 1.0],3121[0.0, 1.0],3122// Use different UV here so it won't be deduplicated.3123[0.0, 0.5],3124];3125mesh.insert_attribute(3126Mesh::ATTRIBUTE_POSITION,3127VertexAttributeValues::Float32x3(positions.clone()),3128);3129mesh.insert_attribute(3130Mesh::ATTRIBUTE_UV_0,3131VertexAttributeValues::Float32x2(uvs.clone()),3132);31333134let res = mesh.merge_duplicate_vertices();3135assert!(res.is_ok());3136assert_eq!(6, mesh.indices().unwrap().len());3137// Note we have 5 unique vertices, not 6.3138assert_eq!(5, mesh.attribute(Mesh::ATTRIBUTE_POSITION).unwrap().len());3139assert_eq!(5, mesh.attribute(Mesh::ATTRIBUTE_UV_0).unwrap().len());31403141// Duplicate back.3142mesh.duplicate_vertices();3143assert!(mesh.indices().is_none());3144let VertexAttributeValues::Float32x3(new_positions) =3145mesh.attribute(Mesh::ATTRIBUTE_POSITION).unwrap()3146else {3147panic!("Unexpected attribute type")3148};3149let VertexAttributeValues::Float32x2(new_uvs) =3150mesh.attribute(Mesh::ATTRIBUTE_UV_0).unwrap()3151else {3152panic!("Unexpected attribute type")3153};3154assert_eq!(&positions, new_positions);3155assert_eq!(&uvs, new_uvs);3156}3157}315831593160