use bevy_transform::components::Transform;1pub use wgpu_types::PrimitiveTopology;23use super::{4generate_tangents_for_mesh, scale_normal, triangle_area_normal, triangle_normal, FourIterators,5GenerateTangentsError, Indices, MeshAttributeData, MeshTrianglesError, MeshVertexAttribute,6MeshVertexAttributeId, MeshVertexBufferLayout, MeshVertexBufferLayoutRef,7MeshVertexBufferLayouts, MeshWindingInvertError, VertexAttributeValues, VertexBufferLayout,8};9#[cfg(feature = "serialize")]10use crate::SerializedMeshAttributeData;11use alloc::collections::BTreeMap;12use bevy_asset::{Asset, Handle, RenderAssetUsages};13use bevy_image::Image;14use bevy_math::{primitives::Triangle3d, *};15#[cfg(feature = "serialize")]16use bevy_platform::collections::HashMap;17use bevy_reflect::Reflect;18use bytemuck::cast_slice;19#[cfg(feature = "serialize")]20use serde::{Deserialize, Serialize};21use thiserror::Error;22use tracing::warn;23use wgpu_types::{VertexAttribute, VertexFormat, VertexStepMode};2425pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;26pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;2728/// A 3D object made out of vertices representing triangles, lines, or points,29/// with "attribute" values for each vertex.30///31/// Meshes can be automatically generated by a bevy `AssetLoader` (generally by loading a `Gltf` file),32/// or by converting a [primitive](bevy_math::primitives) using [`into`](Into).33/// It is also possible to create one manually. They can be edited after creation.34///35/// Meshes can be rendered with a [`Mesh2d`](crate::Mesh2d) and `MeshMaterial2d`36/// or [`Mesh3d`](crate::Mesh3d) and `MeshMaterial3d` for 2D and 3D respectively.37///38/// A [`Mesh`] in Bevy is equivalent to a "primitive" in the glTF format, for a39/// glTF Mesh representation, see `GltfMesh`.40///41/// ## Manual creation42///43/// The following function will construct a flat mesh, to be rendered with a44/// `StandardMaterial` or `ColorMaterial`:45///46/// ```47/// # use bevy_mesh::{Mesh, Indices, PrimitiveTopology};48/// # use bevy_asset::RenderAssetUsages;49/// fn create_simple_parallelogram() -> Mesh {50/// // Create a new mesh using a triangle list topology, where each set of 3 vertices composes a triangle.51/// Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default())52/// // Add 4 vertices, each with its own position attribute (coordinate in53/// // 3D space), for each of the corners of the parallelogram.54/// .with_inserted_attribute(55/// Mesh::ATTRIBUTE_POSITION,56/// 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]]57/// )58/// // Assign a UV coordinate to each vertex.59/// .with_inserted_attribute(60/// Mesh::ATTRIBUTE_UV_0,61/// vec![[0.0, 1.0], [0.5, 0.0], [1.0, 0.0], [0.5, 1.0]]62/// )63/// // Assign normals (everything points outwards)64/// .with_inserted_attribute(65/// Mesh::ATTRIBUTE_NORMAL,66/// 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]]67/// )68/// // After defining all the vertices and their attributes, build each triangle using the69/// // indices of the vertices that make it up in a counter-clockwise order.70/// .with_inserted_indices(Indices::U32(vec![71/// // First triangle72/// 0, 3, 1,73/// // Second triangle74/// 1, 3, 275/// ]))76/// }77/// ```78///79/// You can see how it looks like [here](https://github.com/bevyengine/bevy/blob/main/assets/docs/Mesh.png),80/// used in a [`Mesh3d`](crate::Mesh3d) with a square bevy logo texture, with added axis, points,81/// lines and text for clarity.82///83/// ## Other examples84///85/// For further visualization, explanation, and examples, see the built-in Bevy examples,86/// and the [implementation of the built-in shapes](https://github.com/bevyengine/bevy/tree/main/crates/bevy_mesh/src/primitives).87/// In particular, [generate_custom_mesh](https://github.com/bevyengine/bevy/blob/main/examples/3d/generate_custom_mesh.rs)88/// teaches you to access and modify the attributes of a [`Mesh`] after creating it.89///90/// ## Common points of confusion91///92/// - UV maps in Bevy start at the top-left, see [`ATTRIBUTE_UV_0`](Mesh::ATTRIBUTE_UV_0),93/// other APIs can have other conventions, `OpenGL` starts at bottom-left.94/// - It is possible and sometimes useful for multiple vertices to have the same95/// [position attribute](Mesh::ATTRIBUTE_POSITION) value,96/// it's a common technique in 3D modeling for complex UV mapping or other calculations.97/// - Bevy performs frustum culling based on the `Aabb` of meshes, which is calculated98/// and added automatically for new meshes only. If a mesh is modified, the entity's `Aabb`99/// needs to be updated manually or deleted so that it is re-calculated.100///101/// ## Use with `StandardMaterial`102///103/// To render correctly with `StandardMaterial`, a mesh needs to have properly defined:104/// - [`UVs`](Mesh::ATTRIBUTE_UV_0): Bevy needs to know how to map a texture onto the mesh105/// (also true for `ColorMaterial`).106/// - [`Normals`](Mesh::ATTRIBUTE_NORMAL): Bevy needs to know how light interacts with your mesh.107/// [0.0, 0.0, 1.0] is very common for simple flat meshes on the XY plane,108/// because simple meshes are smooth and they don't require complex light calculations.109/// - Vertex winding order: by default, `StandardMaterial.cull_mode` is `Some(Face::Back)`,110/// which means that Bevy would *only* render the "front" of each triangle, which111/// is the side of the triangle from where the vertices appear in a *counter-clockwise* order.112///113/// ## Remote Inspection114///115/// To transmit a [`Mesh`] between two running Bevy apps, e.g. through BRP, use [`SerializedMesh`].116/// This type is only meant for short-term transmission between same versions and should not be stored anywhere.117#[derive(Asset, Debug, Clone, Reflect, PartialEq)]118#[reflect(Clone)]119pub struct Mesh {120#[reflect(ignore, clone)]121primitive_topology: PrimitiveTopology,122/// `std::collections::BTreeMap` with all defined vertex attributes (Positions, Normals, ...)123/// for this mesh. Attribute ids to attribute values.124/// Uses a [`BTreeMap`] because, unlike `HashMap`, it has a defined iteration order,125/// which allows easy stable `VertexBuffers` (i.e. same buffer order)126#[reflect(ignore, clone)]127attributes: BTreeMap<MeshVertexAttributeId, MeshAttributeData>,128indices: Option<Indices>,129morph_targets: Option<Handle<Image>>,130morph_target_names: Option<Vec<String>>,131pub asset_usage: RenderAssetUsages,132/// Whether or not to build a BLAS for use with `bevy_solari` raytracing.133///134/// Note that this is _not_ whether the mesh is _compatible_ with `bevy_solari` raytracing.135/// This field just controls whether or not a BLAS gets built for this mesh, assuming that136/// the mesh is compatible.137///138/// The use case for this field is using lower-resolution proxy meshes for raytracing (to save on BLAS memory usage),139/// while using higher-resolution meshes for raster. You can set this field to true for the lower-resolution proxy mesh,140/// and to false for the high-resolution raster mesh.141///142/// Alternatively, you can use the same mesh for both raster and raytracing, with this field set to true.143///144/// Does nothing if not used with `bevy_solari`, or if the mesh is not compatible145/// with `bevy_solari` (see `bevy_solari`'s docs).146pub enable_raytracing: bool,147}148149impl Mesh {150/// Where the vertex is located in space. Use in conjunction with [`Mesh::insert_attribute`]151/// or [`Mesh::with_inserted_attribute`].152///153/// The format of this attribute is [`VertexFormat::Float32x3`].154pub const ATTRIBUTE_POSITION: MeshVertexAttribute =155MeshVertexAttribute::new("Vertex_Position", 0, VertexFormat::Float32x3);156157/// The direction the vertex normal is facing in.158/// Use in conjunction with [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`].159///160/// The format of this attribute is [`VertexFormat::Float32x3`].161pub const ATTRIBUTE_NORMAL: MeshVertexAttribute =162MeshVertexAttribute::new("Vertex_Normal", 1, VertexFormat::Float32x3);163164/// Texture coordinates for the vertex. Use in conjunction with [`Mesh::insert_attribute`]165/// or [`Mesh::with_inserted_attribute`].166///167/// Generally `[0.,0.]` is mapped to the top left of the texture, and `[1.,1.]` to the bottom-right.168///169/// By default values outside will be clamped per pixel not for the vertex,170/// "stretching" the borders of the texture.171/// This behavior can be useful in some cases, usually when the borders have only172/// one color, for example a logo, and you want to "extend" those borders.173///174/// For different mapping outside of `0..=1` range,175/// see [`ImageAddressMode`](bevy_image::ImageAddressMode).176///177/// The format of this attribute is [`VertexFormat::Float32x2`].178pub const ATTRIBUTE_UV_0: MeshVertexAttribute =179MeshVertexAttribute::new("Vertex_Uv", 2, VertexFormat::Float32x2);180181/// Alternate texture coordinates for the vertex. Use in conjunction with182/// [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`].183///184/// Typically, these are used for lightmaps, textures that provide185/// precomputed illumination.186///187/// The format of this attribute is [`VertexFormat::Float32x2`].188pub const ATTRIBUTE_UV_1: MeshVertexAttribute =189MeshVertexAttribute::new("Vertex_Uv_1", 3, VertexFormat::Float32x2);190191/// The direction of the vertex tangent. Used for normal mapping.192/// Usually generated with [`generate_tangents`](Mesh::generate_tangents) or193/// [`with_generated_tangents`](Mesh::with_generated_tangents).194///195/// The format of this attribute is [`VertexFormat::Float32x4`].196pub const ATTRIBUTE_TANGENT: MeshVertexAttribute =197MeshVertexAttribute::new("Vertex_Tangent", 4, VertexFormat::Float32x4);198199/// Per vertex coloring. Use in conjunction with [`Mesh::insert_attribute`]200/// or [`Mesh::with_inserted_attribute`].201///202/// The format of this attribute is [`VertexFormat::Float32x4`].203pub const ATTRIBUTE_COLOR: MeshVertexAttribute =204MeshVertexAttribute::new("Vertex_Color", 5, VertexFormat::Float32x4);205206/// Per vertex joint transform matrix weight. Use in conjunction with [`Mesh::insert_attribute`]207/// or [`Mesh::with_inserted_attribute`].208///209/// The format of this attribute is [`VertexFormat::Float32x4`].210pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute =211MeshVertexAttribute::new("Vertex_JointWeight", 6, VertexFormat::Float32x4);212213/// Per vertex joint transform matrix index. Use in conjunction with [`Mesh::insert_attribute`]214/// or [`Mesh::with_inserted_attribute`].215///216/// The format of this attribute is [`VertexFormat::Uint16x4`].217pub const ATTRIBUTE_JOINT_INDEX: MeshVertexAttribute =218MeshVertexAttribute::new("Vertex_JointIndex", 7, VertexFormat::Uint16x4);219220/// The first index that can be used for custom vertex attributes.221/// Only the attributes with an index below this are used by Bevy.222pub const FIRST_AVAILABLE_CUSTOM_ATTRIBUTE: u64 = 8;223224/// Construct a new mesh. You need to provide a [`PrimitiveTopology`] so that the225/// renderer knows how to treat the vertex data. Most of the time this will be226/// [`PrimitiveTopology::TriangleList`].227pub fn new(primitive_topology: PrimitiveTopology, asset_usage: RenderAssetUsages) -> Self {228Mesh {229primitive_topology,230attributes: Default::default(),231indices: None,232morph_targets: None,233morph_target_names: None,234asset_usage,235enable_raytracing: true,236}237}238239/// Returns the topology of the mesh.240pub fn primitive_topology(&self) -> PrimitiveTopology {241self.primitive_topology242}243244/// Sets the data for a vertex attribute (position, normal, etc.). The name will245/// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].246///247/// `Aabb` of entities with modified mesh are not updated automatically.248///249/// # Panics250/// Panics when the format of the values does not match the attribute's format.251#[inline]252pub fn insert_attribute(253&mut self,254attribute: MeshVertexAttribute,255values: impl Into<VertexAttributeValues>,256) {257let values = values.into();258let values_format = VertexFormat::from(&values);259if values_format != attribute.format {260panic!(261"Failed to insert attribute. Invalid attribute format for {}. Given format is {values_format:?} but expected {:?}",262attribute.name, attribute.format263);264}265266self.attributes267.insert(attribute.id, MeshAttributeData { attribute, values });268}269270/// Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.).271/// The name will often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].272///273/// (Alternatively, you can use [`Mesh::insert_attribute`] to mutate an existing mesh in-place)274///275/// `Aabb` of entities with modified mesh are not updated automatically.276///277/// # Panics278/// Panics when the format of the values does not match the attribute's format.279#[must_use]280#[inline]281pub fn with_inserted_attribute(282mut self,283attribute: MeshVertexAttribute,284values: impl Into<VertexAttributeValues>,285) -> Self {286self.insert_attribute(attribute, values);287self288}289290/// Removes the data for a vertex attribute291pub fn remove_attribute(292&mut self,293attribute: impl Into<MeshVertexAttributeId>,294) -> Option<VertexAttributeValues> {295self.attributes296.remove(&attribute.into())297.map(|data| data.values)298}299300/// Consumes the mesh and returns a mesh without the data for a vertex attribute301///302/// (Alternatively, you can use [`Mesh::remove_attribute`] to mutate an existing mesh in-place)303#[must_use]304pub fn with_removed_attribute(mut self, attribute: impl Into<MeshVertexAttributeId>) -> Self {305self.remove_attribute(attribute);306self307}308309#[inline]310pub fn contains_attribute(&self, id: impl Into<MeshVertexAttributeId>) -> bool {311self.attributes.contains_key(&id.into())312}313314/// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].315#[inline]316pub fn attribute(317&self,318id: impl Into<MeshVertexAttributeId>,319) -> Option<&VertexAttributeValues> {320self.attributes.get(&id.into()).map(|data| &data.values)321}322323/// Retrieves the full data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].324#[inline]325pub(crate) fn attribute_data(326&self,327id: impl Into<MeshVertexAttributeId>,328) -> Option<&MeshAttributeData> {329self.attributes.get(&id.into())330}331332/// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.333#[inline]334pub fn attribute_mut(335&mut self,336id: impl Into<MeshVertexAttributeId>,337) -> Option<&mut VertexAttributeValues> {338self.attributes339.get_mut(&id.into())340.map(|data| &mut data.values)341}342343/// Returns an iterator that yields references to the data of each vertex attribute.344pub fn attributes(345&self,346) -> impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)> {347self.attributes348.values()349.map(|data| (&data.attribute, &data.values))350}351352/// Returns an iterator that yields mutable references to the data of each vertex attribute.353pub fn attributes_mut(354&mut self,355) -> impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)> {356self.attributes357.values_mut()358.map(|data| (&data.attribute, &mut data.values))359}360361/// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the362/// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants363/// that use triangles.364#[inline]365pub fn insert_indices(&mut self, indices: Indices) {366self.indices = Some(indices);367}368369/// Consumes the mesh and returns a mesh with the given vertex indices. They describe how triangles370/// are constructed out of the vertex attributes and are therefore only useful for the371/// [`PrimitiveTopology`] variants that use triangles.372///373/// (Alternatively, you can use [`Mesh::insert_indices`] to mutate an existing mesh in-place)374#[must_use]375#[inline]376pub fn with_inserted_indices(mut self, indices: Indices) -> Self {377self.insert_indices(indices);378self379}380381/// Retrieves the vertex `indices` of the mesh.382#[inline]383pub fn indices(&self) -> Option<&Indices> {384self.indices.as_ref()385}386387/// Retrieves the vertex `indices` of the mesh mutably.388#[inline]389pub fn indices_mut(&mut self) -> Option<&mut Indices> {390self.indices.as_mut()391}392393/// Removes the vertex `indices` from the mesh and returns them.394#[inline]395pub fn remove_indices(&mut self) -> Option<Indices> {396core::mem::take(&mut self.indices)397}398399/// Consumes the mesh and returns a mesh without the vertex `indices` of the mesh.400///401/// (Alternatively, you can use [`Mesh::remove_indices`] to mutate an existing mesh in-place)402#[must_use]403pub fn with_removed_indices(mut self) -> Self {404self.remove_indices();405self406}407408/// Returns the size of a vertex in bytes.409pub fn get_vertex_size(&self) -> u64 {410self.attributes411.values()412.map(|data| data.attribute.format.size())413.sum()414}415416/// Returns the size required for the vertex buffer in bytes.417pub fn get_vertex_buffer_size(&self) -> usize {418let vertex_size = self.get_vertex_size() as usize;419let vertex_count = self.count_vertices();420vertex_count * vertex_size421}422423/// Computes and returns the index data of the mesh as bytes.424/// This is used to transform the index data into a GPU friendly format.425pub fn get_index_buffer_bytes(&self) -> Option<&[u8]> {426self.indices.as_ref().map(|indices| match &indices {427Indices::U16(indices) => cast_slice(&indices[..]),428Indices::U32(indices) => cast_slice(&indices[..]),429})430}431432/// Get this `Mesh`'s [`MeshVertexBufferLayout`], used in `SpecializedMeshPipeline`.433pub fn get_mesh_vertex_buffer_layout(434&self,435mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,436) -> MeshVertexBufferLayoutRef {437let mut attributes = Vec::with_capacity(self.attributes.len());438let mut attribute_ids = Vec::with_capacity(self.attributes.len());439let mut accumulated_offset = 0;440for (index, data) in self.attributes.values().enumerate() {441attribute_ids.push(data.attribute.id);442attributes.push(VertexAttribute {443offset: accumulated_offset,444format: data.attribute.format,445shader_location: index as u32,446});447accumulated_offset += data.attribute.format.size();448}449450let layout = MeshVertexBufferLayout {451layout: VertexBufferLayout {452array_stride: accumulated_offset,453step_mode: VertexStepMode::Vertex,454attributes,455},456attribute_ids,457};458mesh_vertex_buffer_layouts.insert(layout)459}460461/// Counts all vertices of the mesh.462///463/// If the attributes have different vertex counts, the smallest is returned.464pub fn count_vertices(&self) -> usize {465let mut vertex_count: Option<usize> = None;466for (attribute_id, attribute_data) in &self.attributes {467let attribute_len = attribute_data.values.len();468if let Some(previous_vertex_count) = vertex_count {469if previous_vertex_count != attribute_len {470let name = self471.attributes472.get(attribute_id)473.map(|data| data.attribute.name.to_string())474.unwrap_or_else(|| format!("{attribute_id:?}"));475476warn!("{name} has a different vertex count ({attribute_len}) than other attributes ({previous_vertex_count}) in this mesh, \477all attributes will be truncated to match the smallest.");478vertex_count = Some(core::cmp::min(previous_vertex_count, attribute_len));479}480} else {481vertex_count = Some(attribute_len);482}483}484485vertex_count.unwrap_or(0)486}487488/// Computes and returns the vertex data of the mesh as bytes.489/// Therefore the attributes are located in the order of their [`MeshVertexAttribute::id`].490/// This is used to transform the vertex data into a GPU friendly format.491///492/// If the vertex attributes have different lengths, they are all truncated to493/// the length of the smallest.494///495/// This is a convenience method which allocates a Vec.496/// Prefer pre-allocating and using [`Mesh::write_packed_vertex_buffer_data`] when possible.497pub fn create_packed_vertex_buffer_data(&self) -> Vec<u8> {498let mut attributes_interleaved_buffer = vec![0; self.get_vertex_buffer_size()];499self.write_packed_vertex_buffer_data(&mut attributes_interleaved_buffer);500attributes_interleaved_buffer501}502503/// Computes and write the vertex data of the mesh into a mutable byte slice.504/// The attributes are located in the order of their [`MeshVertexAttribute::id`].505/// This is used to transform the vertex data into a GPU friendly format.506///507/// If the vertex attributes have different lengths, they are all truncated to508/// the length of the smallest.509pub fn write_packed_vertex_buffer_data(&self, slice: &mut [u8]) {510let vertex_size = self.get_vertex_size() as usize;511let vertex_count = self.count_vertices();512// bundle into interleaved buffers513let mut attribute_offset = 0;514for attribute_data in self.attributes.values() {515let attribute_size = attribute_data.attribute.format.size() as usize;516let attributes_bytes = attribute_data.values.get_bytes();517for (vertex_index, attribute_bytes) in attributes_bytes518.chunks_exact(attribute_size)519.take(vertex_count)520.enumerate()521{522let offset = vertex_index * vertex_size + attribute_offset;523slice[offset..offset + attribute_size].copy_from_slice(attribute_bytes);524}525526attribute_offset += attribute_size;527}528}529530/// Duplicates the vertex attributes so that no vertices are shared.531///532/// This can dramatically increase the vertex count, so make sure this is what you want.533/// Does nothing if no [Indices] are set.534pub fn duplicate_vertices(&mut self) {535fn duplicate<T: Copy>(values: &[T], indices: impl Iterator<Item = usize>) -> Vec<T> {536indices.map(|i| values[i]).collect()537}538539let Some(indices) = self.indices.take() else {540return;541};542543for attributes in self.attributes.values_mut() {544let indices = indices.iter();545#[expect(546clippy::match_same_arms,547reason = "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."548)]549match &mut attributes.values {550VertexAttributeValues::Float32(vec) => *vec = duplicate(vec, indices),551VertexAttributeValues::Sint32(vec) => *vec = duplicate(vec, indices),552VertexAttributeValues::Uint32(vec) => *vec = duplicate(vec, indices),553VertexAttributeValues::Float32x2(vec) => *vec = duplicate(vec, indices),554VertexAttributeValues::Sint32x2(vec) => *vec = duplicate(vec, indices),555VertexAttributeValues::Uint32x2(vec) => *vec = duplicate(vec, indices),556VertexAttributeValues::Float32x3(vec) => *vec = duplicate(vec, indices),557VertexAttributeValues::Sint32x3(vec) => *vec = duplicate(vec, indices),558VertexAttributeValues::Uint32x3(vec) => *vec = duplicate(vec, indices),559VertexAttributeValues::Sint32x4(vec) => *vec = duplicate(vec, indices),560VertexAttributeValues::Uint32x4(vec) => *vec = duplicate(vec, indices),561VertexAttributeValues::Float32x4(vec) => *vec = duplicate(vec, indices),562VertexAttributeValues::Sint16x2(vec) => *vec = duplicate(vec, indices),563VertexAttributeValues::Snorm16x2(vec) => *vec = duplicate(vec, indices),564VertexAttributeValues::Uint16x2(vec) => *vec = duplicate(vec, indices),565VertexAttributeValues::Unorm16x2(vec) => *vec = duplicate(vec, indices),566VertexAttributeValues::Sint16x4(vec) => *vec = duplicate(vec, indices),567VertexAttributeValues::Snorm16x4(vec) => *vec = duplicate(vec, indices),568VertexAttributeValues::Uint16x4(vec) => *vec = duplicate(vec, indices),569VertexAttributeValues::Unorm16x4(vec) => *vec = duplicate(vec, indices),570VertexAttributeValues::Sint8x2(vec) => *vec = duplicate(vec, indices),571VertexAttributeValues::Snorm8x2(vec) => *vec = duplicate(vec, indices),572VertexAttributeValues::Uint8x2(vec) => *vec = duplicate(vec, indices),573VertexAttributeValues::Unorm8x2(vec) => *vec = duplicate(vec, indices),574VertexAttributeValues::Sint8x4(vec) => *vec = duplicate(vec, indices),575VertexAttributeValues::Snorm8x4(vec) => *vec = duplicate(vec, indices),576VertexAttributeValues::Uint8x4(vec) => *vec = duplicate(vec, indices),577VertexAttributeValues::Unorm8x4(vec) => *vec = duplicate(vec, indices),578}579}580}581582/// Consumes the mesh and returns a mesh with no shared vertices.583///584/// This can dramatically increase the vertex count, so make sure this is what you want.585/// Does nothing if no [`Indices`] are set.586///587/// (Alternatively, you can use [`Mesh::duplicate_vertices`] to mutate an existing mesh in-place)588#[must_use]589pub fn with_duplicated_vertices(mut self) -> Self {590self.duplicate_vertices();591self592}593594/// Inverts the winding of the indices such that all counter-clockwise triangles are now595/// clockwise and vice versa.596/// For lines, their start and end indices are flipped.597///598/// Does nothing if no [`Indices`] are set.599/// If this operation succeeded, an [`Ok`] result is returned.600pub fn invert_winding(&mut self) -> Result<(), MeshWindingInvertError> {601fn invert<I>(602indices: &mut [I],603topology: PrimitiveTopology,604) -> Result<(), MeshWindingInvertError> {605match topology {606PrimitiveTopology::TriangleList => {607// Early return if the index count doesn't match608if !indices.len().is_multiple_of(3) {609return Err(MeshWindingInvertError::AbruptIndicesEnd);610}611for chunk in indices.chunks_mut(3) {612// This currently can only be optimized away with unsafe, rework this when `feature(slice_as_chunks)` gets stable.613let [_, b, c] = chunk else {614return Err(MeshWindingInvertError::AbruptIndicesEnd);615};616core::mem::swap(b, c);617}618Ok(())619}620PrimitiveTopology::LineList => {621// Early return if the index count doesn't match622if !indices.len().is_multiple_of(2) {623return Err(MeshWindingInvertError::AbruptIndicesEnd);624}625indices.reverse();626Ok(())627}628PrimitiveTopology::TriangleStrip | PrimitiveTopology::LineStrip => {629indices.reverse();630Ok(())631}632_ => Err(MeshWindingInvertError::WrongTopology),633}634}635match &mut self.indices {636Some(Indices::U16(vec)) => invert(vec, self.primitive_topology),637Some(Indices::U32(vec)) => invert(vec, self.primitive_topology),638None => Ok(()),639}640}641642/// Consumes the mesh and returns a mesh with inverted winding of the indices such643/// that all counter-clockwise triangles are now clockwise and vice versa.644///645/// Does nothing if no [`Indices`] are set.646pub fn with_inverted_winding(mut self) -> Result<Self, MeshWindingInvertError> {647self.invert_winding().map(|_| self)648}649650/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.651/// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat652/// normals.653///654/// # Panics655/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.656/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].=657pub fn compute_normals(&mut self) {658assert!(659matches!(self.primitive_topology, PrimitiveTopology::TriangleList),660"`compute_normals` can only work on `TriangleList`s"661);662if self.indices().is_none() {663self.compute_flat_normals();664} else {665self.compute_smooth_normals();666}667}668669/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.670///671/// # Panics672/// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.673/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].674/// Consider calling [`Mesh::duplicate_vertices`] or exporting your mesh with normal675/// attributes.676///677/// FIXME: This should handle more cases since this is called as a part of gltf678/// mesh loading where we can't really blame users for loading meshes that might679/// not conform to the limitations here!680pub fn compute_flat_normals(&mut self) {681assert!(682self.indices().is_none(),683"`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`."684);685assert!(686matches!(self.primitive_topology, PrimitiveTopology::TriangleList),687"`compute_flat_normals` can only work on `TriangleList`s"688);689690let positions = self691.attribute(Mesh::ATTRIBUTE_POSITION)692.unwrap()693.as_float3()694.expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");695696let normals: Vec<_> = positions697.chunks_exact(3)698.map(|p| triangle_normal(p[0], p[1], p[2]))699.flat_map(|normal| [normal; 3])700.collect();701702self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);703}704705/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared706/// vertices.707///708/// This method weights normals by the angles of the corners of connected triangles, thus709/// eliminating triangle area and count as factors in the final normal. This does make it710/// somewhat slower than [`Mesh::compute_area_weighted_normals`] which does not need to711/// greedily normalize each triangle's normal or calculate corner angles.712///713/// If you would rather have the computed normals be weighted by triangle area, see714/// [`Mesh::compute_area_weighted_normals`] instead. If you need to weight them in some other715/// way, see [`Mesh::compute_custom_smooth_normals`].716///717/// # Panics718/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.719/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].720/// Panics if the mesh does not have indices defined.721pub fn compute_smooth_normals(&mut self) {722self.compute_custom_smooth_normals(|[a, b, c], positions, normals| {723let pa = Vec3::from(positions[a]);724let pb = Vec3::from(positions[b]);725let pc = Vec3::from(positions[c]);726727let ab = pb - pa;728let ba = pa - pb;729let bc = pc - pb;730let cb = pb - pc;731let ca = pa - pc;732let ac = pc - pa;733734const EPS: f32 = f32::EPSILON;735let weight_a = if ab.length_squared() * ac.length_squared() > EPS {736ab.angle_between(ac)737} else {7380.0739};740let weight_b = if ba.length_squared() * bc.length_squared() > EPS {741ba.angle_between(bc)742} else {7430.0744};745let weight_c = if ca.length_squared() * cb.length_squared() > EPS {746ca.angle_between(cb)747} else {7480.0749};750751let normal = Vec3::from(triangle_normal(positions[a], positions[b], positions[c]));752753normals[a] += normal * weight_a;754normals[b] += normal * weight_b;755normals[c] += normal * weight_c;756});757}758759/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared760/// vertices.761///762/// This method weights normals by the area of each triangle containing the vertex. Thus,763/// larger triangles will skew the normals of their vertices towards their own normal more764/// than smaller triangles will.765///766/// This method is actually somewhat faster than [`Mesh::compute_smooth_normals`] because an767/// intermediate result of triangle normal calculation is already scaled by the triangle's area.768///769/// If you would rather have the computed normals be influenced only by the angles of connected770/// edges, see [`Mesh::compute_smooth_normals`] instead. If you need to weight them in some771/// other way, see [`Mesh::compute_custom_smooth_normals`].772///773/// # Panics774/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.775/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].776/// Panics if the mesh does not have indices defined.777pub fn compute_area_weighted_normals(&mut self) {778self.compute_custom_smooth_normals(|[a, b, c], positions, normals| {779let normal = Vec3::from(triangle_area_normal(780positions[a],781positions[b],782positions[c],783));784[a, b, c].into_iter().for_each(|pos| {785normals[pos] += normal;786});787});788}789790/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared791/// vertices.792///793/// This method allows you to customize how normals are weighted via the `per_triangle` parameter,794/// which must be a function or closure that accepts 3 parameters:795/// - The indices of the three vertices of the triangle as a `[usize; 3]`.796/// - A reference to the values of the [`Mesh::ATTRIBUTE_POSITION`] of the mesh (`&[[f32; 3]]`).797/// - A mutable reference to the sums of all normals so far.798///799/// See also the standard methods included in Bevy for calculating smooth normals:800/// - [`Mesh::compute_smooth_normals`]801/// - [`Mesh::compute_area_weighted_normals`]802///803/// An example that would weight each connected triangle's normal equally, thus skewing normals804/// towards the planes divided into the most triangles:805/// ```806/// # use bevy_asset::RenderAssetUsages;807/// # use bevy_mesh::{Mesh, PrimitiveTopology, Meshable, MeshBuilder};808/// # use bevy_math::{Vec3, primitives::Cuboid};809/// # let mut mesh = Cuboid::default().mesh().build();810/// mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| {811/// let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c]));812/// for idx in [a, b, c] {813/// normals[idx] += normal;814/// }815/// });816/// ```817///818/// # Panics819/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.820/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].821/// Panics if the mesh does not have indices defined.822//823// FIXME: This should handle more cases since this is called as a part of gltf824// mesh loading where we can't really blame users for loading meshes that might825// not conform to the limitations here!826//827// When fixed, also update "Panics" sections of828// - [Mesh::compute_smooth_normals]829// - [Mesh::with_computed_smooth_normals]830// - [Mesh::compute_area_weighted_normals]831// - [Mesh::with_computed_area_weighted_normals]832pub fn compute_custom_smooth_normals(833&mut self,834mut per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]),835) {836assert!(837matches!(self.primitive_topology, PrimitiveTopology::TriangleList),838"smooth normals can only be computed on `TriangleList`s"839);840assert!(841self.indices().is_some(),842"smooth normals can only be computed on indexed meshes"843);844845let positions = self846.attribute(Mesh::ATTRIBUTE_POSITION)847.unwrap()848.as_float3()849.expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");850851let mut normals = vec![Vec3::ZERO; positions.len()];852853self.indices()854.unwrap()855.iter()856.collect::<Vec<usize>>()857.chunks_exact(3)858.for_each(|face| per_triangle([face[0], face[1], face[2]], positions, &mut normals));859860for normal in &mut normals {861*normal = normal.try_normalize().unwrap_or(Vec3::ZERO);862}863864self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);865}866867/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].868/// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat869/// normals.870///871/// (Alternatively, you can use [`Mesh::compute_normals`] to mutate an existing mesh in-place)872///873/// # Panics874/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.875/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].876#[must_use]877pub fn with_computed_normals(mut self) -> Self {878self.compute_normals();879self880}881882/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].883///884/// (Alternatively, you can use [`Mesh::compute_flat_normals`] to mutate an existing mesh in-place)885///886/// # Panics887/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.888/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].889/// Panics if the mesh has indices defined890#[must_use]891pub fn with_computed_flat_normals(mut self) -> Self {892self.compute_flat_normals();893self894}895896/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].897///898/// (Alternatively, you can use [`Mesh::compute_smooth_normals`] to mutate an existing mesh in-place)899///900/// This method weights normals by the angles of triangle corners connected to each vertex. If901/// you would rather have the computed normals be weighted by triangle area, see902/// [`Mesh::with_computed_area_weighted_normals`] instead.903///904/// # Panics905/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.906/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].907/// Panics if the mesh does not have indices defined.908#[must_use]909pub fn with_computed_smooth_normals(mut self) -> Self {910self.compute_smooth_normals();911self912}913914/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].915///916/// (Alternatively, you can use [`Mesh::compute_area_weighted_normals`] to mutate an existing mesh in-place)917///918/// This method weights normals by the area of each triangle containing the vertex. Thus,919/// larger triangles will skew the normals of their vertices towards their own normal more920/// than smaller triangles will. If you would rather have the computed normals be influenced921/// only by the angles of connected edges, see [`Mesh::with_computed_smooth_normals`] instead.922///923/// # Panics924/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.925/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].926/// Panics if the mesh does not have indices defined.927#[must_use]928pub fn with_computed_area_weighted_normals(mut self) -> Self {929self.compute_area_weighted_normals();930self931}932933/// Generate tangents for the mesh using the `mikktspace` algorithm.934///935/// Sets the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful.936/// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set.937pub fn generate_tangents(&mut self) -> Result<(), GenerateTangentsError> {938let tangents = generate_tangents_for_mesh(self)?;939self.insert_attribute(Mesh::ATTRIBUTE_TANGENT, tangents);940Ok(())941}942943/// Consumes the mesh and returns a mesh with tangents generated using the `mikktspace` algorithm.944///945/// The resulting mesh will have the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful.946///947/// (Alternatively, you can use [`Mesh::generate_tangents`] to mutate an existing mesh in-place)948///949/// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set.950pub fn with_generated_tangents(mut self) -> Result<Mesh, GenerateTangentsError> {951self.generate_tangents()?;952Ok(self)953}954955/// Merges the [`Mesh`] data of `other` with `self`. The attributes and indices of `other` will be appended to `self`.956///957/// Note that attributes of `other` that don't exist on `self` will be ignored.958///959/// `Aabb` of entities with modified mesh are not updated automatically.960///961/// # Errors962///963/// If any of the following conditions are not met, this function errors:964/// * All of the vertex attributes that have the same attribute id, must also965/// have the same attribute type.966/// For example two attributes with the same id, but where one is a967/// [`VertexAttributeValues::Float32`] and the other is a968/// [`VertexAttributeValues::Float32x3`], would be invalid.969/// * Both meshes must have the same primitive topology.970pub fn merge(&mut self, other: &Mesh) -> Result<(), MeshMergeError> {971use VertexAttributeValues::*;972973// Check if the meshes `primitive_topology` field is the same,974// as if that is not the case, the resulting mesh could (and most likely would)975// be invalid.976if self.primitive_topology != other.primitive_topology {977return Err(MeshMergeError::IncompatiblePrimitiveTopology {978self_primitive_topology: self.primitive_topology,979other_primitive_topology: other.primitive_topology,980});981}982983// The indices of `other` should start after the last vertex of `self`.984let index_offset = self.count_vertices();985986// Extend attributes of `self` with attributes of `other`.987for (attribute, values) in self.attributes_mut() {988if let Some(other_values) = other.attribute(attribute.id) {989#[expect(990clippy::match_same_arms,991reason = "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."992)]993match (values, other_values) {994(Float32(vec1), Float32(vec2)) => vec1.extend(vec2),995(Sint32(vec1), Sint32(vec2)) => vec1.extend(vec2),996(Uint32(vec1), Uint32(vec2)) => vec1.extend(vec2),997(Float32x2(vec1), Float32x2(vec2)) => vec1.extend(vec2),998(Sint32x2(vec1), Sint32x2(vec2)) => vec1.extend(vec2),999(Uint32x2(vec1), Uint32x2(vec2)) => vec1.extend(vec2),1000(Float32x3(vec1), Float32x3(vec2)) => vec1.extend(vec2),1001(Sint32x3(vec1), Sint32x3(vec2)) => vec1.extend(vec2),1002(Uint32x3(vec1), Uint32x3(vec2)) => vec1.extend(vec2),1003(Sint32x4(vec1), Sint32x4(vec2)) => vec1.extend(vec2),1004(Uint32x4(vec1), Uint32x4(vec2)) => vec1.extend(vec2),1005(Float32x4(vec1), Float32x4(vec2)) => vec1.extend(vec2),1006(Sint16x2(vec1), Sint16x2(vec2)) => vec1.extend(vec2),1007(Snorm16x2(vec1), Snorm16x2(vec2)) => vec1.extend(vec2),1008(Uint16x2(vec1), Uint16x2(vec2)) => vec1.extend(vec2),1009(Unorm16x2(vec1), Unorm16x2(vec2)) => vec1.extend(vec2),1010(Sint16x4(vec1), Sint16x4(vec2)) => vec1.extend(vec2),1011(Snorm16x4(vec1), Snorm16x4(vec2)) => vec1.extend(vec2),1012(Uint16x4(vec1), Uint16x4(vec2)) => vec1.extend(vec2),1013(Unorm16x4(vec1), Unorm16x4(vec2)) => vec1.extend(vec2),1014(Sint8x2(vec1), Sint8x2(vec2)) => vec1.extend(vec2),1015(Snorm8x2(vec1), Snorm8x2(vec2)) => vec1.extend(vec2),1016(Uint8x2(vec1), Uint8x2(vec2)) => vec1.extend(vec2),1017(Unorm8x2(vec1), Unorm8x2(vec2)) => vec1.extend(vec2),1018(Sint8x4(vec1), Sint8x4(vec2)) => vec1.extend(vec2),1019(Snorm8x4(vec1), Snorm8x4(vec2)) => vec1.extend(vec2),1020(Uint8x4(vec1), Uint8x4(vec2)) => vec1.extend(vec2),1021(Unorm8x4(vec1), Unorm8x4(vec2)) => vec1.extend(vec2),1022_ => {1023return Err(MeshMergeError::IncompatibleVertexAttributes {1024self_attribute: *attribute,1025other_attribute: other1026.attribute_data(attribute.id)1027.map(|data| data.attribute),1028})1029}1030}1031}1032}10331034// Extend indices of `self` with indices of `other`.1035if let (Some(indices), Some(other_indices)) = (self.indices_mut(), other.indices()) {1036indices.extend(other_indices.iter().map(|i| (i + index_offset) as u32));1037}1038Ok(())1039}10401041/// Transforms the vertex positions, normals, and tangents of the mesh by the given [`Transform`].1042///1043/// `Aabb` of entities with modified mesh are not updated automatically.1044pub fn transformed_by(mut self, transform: Transform) -> Self {1045self.transform_by(transform);1046self1047}10481049/// Transforms the vertex positions, normals, and tangents of the mesh in place by the given [`Transform`].1050///1051/// `Aabb` of entities with modified mesh are not updated automatically.1052pub fn transform_by(&mut self, transform: Transform) {1053// Needed when transforming normals and tangents1054let scale_recip = 1. / transform.scale;1055debug_assert!(1056transform.scale.yzx() * transform.scale.zxy() != Vec3::ZERO,1057"mesh transform scale cannot be zero on more than one axis"1058);10591060if let Some(VertexAttributeValues::Float32x3(positions)) =1061self.attribute_mut(Mesh::ATTRIBUTE_POSITION)1062{1063// Apply scale, rotation, and translation to vertex positions1064positions1065.iter_mut()1066.for_each(|pos| *pos = transform.transform_point(Vec3::from_slice(pos)).to_array());1067}10681069// No need to transform normals or tangents if rotation is near identity and scale is uniform1070if transform.rotation.is_near_identity()1071&& transform.scale.x == transform.scale.y1072&& transform.scale.y == transform.scale.z1073{1074return;1075}10761077if let Some(VertexAttributeValues::Float32x3(normals)) =1078self.attribute_mut(Mesh::ATTRIBUTE_NORMAL)1079{1080// Transform normals, taking into account non-uniform scaling and rotation1081normals.iter_mut().for_each(|normal| {1082*normal = (transform.rotation1083* scale_normal(Vec3::from_array(*normal), scale_recip))1084.to_array();1085});1086}10871088if let Some(VertexAttributeValues::Float32x4(tangents)) =1089self.attribute_mut(Mesh::ATTRIBUTE_TANGENT)1090{1091// Transform tangents, taking into account non-uniform scaling and rotation1092tangents.iter_mut().for_each(|tangent| {1093let handedness = tangent[3];1094let scaled_tangent = Vec3::from_slice(tangent) * transform.scale;1095*tangent = (transform.rotation * scaled_tangent.normalize_or_zero())1096.extend(handedness)1097.to_array();1098});1099}1100}11011102/// Translates the vertex positions of the mesh by the given [`Vec3`].1103///1104/// `Aabb` of entities with modified mesh are not updated automatically.1105pub fn translated_by(mut self, translation: Vec3) -> Self {1106self.translate_by(translation);1107self1108}11091110/// Translates the vertex positions of the mesh in place by the given [`Vec3`].1111///1112/// `Aabb` of entities with modified mesh are not updated automatically.1113pub fn translate_by(&mut self, translation: Vec3) {1114if translation == Vec3::ZERO {1115return;1116}11171118if let Some(VertexAttributeValues::Float32x3(positions)) =1119self.attribute_mut(Mesh::ATTRIBUTE_POSITION)1120{1121// Apply translation to vertex positions1122positions1123.iter_mut()1124.for_each(|pos| *pos = (Vec3::from_slice(pos) + translation).to_array());1125}1126}11271128/// Rotates the vertex positions, normals, and tangents of the mesh by the given [`Quat`].1129///1130/// `Aabb` of entities with modified mesh are not updated automatically.1131pub fn rotated_by(mut self, rotation: Quat) -> Self {1132self.rotate_by(rotation);1133self1134}11351136/// Rotates the vertex positions, normals, and tangents of the mesh in place by the given [`Quat`].1137///1138/// `Aabb` of entities with modified mesh are not updated automatically.1139pub fn rotate_by(&mut self, rotation: Quat) {1140if let Some(VertexAttributeValues::Float32x3(positions)) =1141self.attribute_mut(Mesh::ATTRIBUTE_POSITION)1142{1143// Apply rotation to vertex positions1144positions1145.iter_mut()1146.for_each(|pos| *pos = (rotation * Vec3::from_slice(pos)).to_array());1147}11481149// No need to transform normals or tangents if rotation is near identity1150if rotation.is_near_identity() {1151return;1152}11531154if let Some(VertexAttributeValues::Float32x3(normals)) =1155self.attribute_mut(Mesh::ATTRIBUTE_NORMAL)1156{1157// Transform normals1158normals.iter_mut().for_each(|normal| {1159*normal = (rotation * Vec3::from_slice(normal).normalize_or_zero()).to_array();1160});1161}11621163if let Some(VertexAttributeValues::Float32x4(tangents)) =1164self.attribute_mut(Mesh::ATTRIBUTE_TANGENT)1165{1166// Transform tangents1167tangents.iter_mut().for_each(|tangent| {1168let handedness = tangent[3];1169*tangent = (rotation * Vec3::from_slice(tangent).normalize_or_zero())1170.extend(handedness)1171.to_array();1172});1173}1174}11751176/// Scales the vertex positions, normals, and tangents of the mesh by the given [`Vec3`].1177///1178/// `Aabb` of entities with modified mesh are not updated automatically.1179pub fn scaled_by(mut self, scale: Vec3) -> Self {1180self.scale_by(scale);1181self1182}11831184/// Scales the vertex positions, normals, and tangents of the mesh in place by the given [`Vec3`].1185///1186/// `Aabb` of entities with modified mesh are not updated automatically.1187pub fn scale_by(&mut self, scale: Vec3) {1188// Needed when transforming normals and tangents1189let scale_recip = 1. / scale;1190debug_assert!(1191scale.yzx() * scale.zxy() != Vec3::ZERO,1192"mesh transform scale cannot be zero on more than one axis"1193);11941195if let Some(VertexAttributeValues::Float32x3(positions)) =1196self.attribute_mut(Mesh::ATTRIBUTE_POSITION)1197{1198// Apply scale to vertex positions1199positions1200.iter_mut()1201.for_each(|pos| *pos = (scale * Vec3::from_slice(pos)).to_array());1202}12031204// No need to transform normals or tangents if scale is uniform1205if scale.x == scale.y && scale.y == scale.z {1206return;1207}12081209if let Some(VertexAttributeValues::Float32x3(normals)) =1210self.attribute_mut(Mesh::ATTRIBUTE_NORMAL)1211{1212// Transform normals, taking into account non-uniform scaling1213normals.iter_mut().for_each(|normal| {1214*normal = scale_normal(Vec3::from_array(*normal), scale_recip).to_array();1215});1216}12171218if let Some(VertexAttributeValues::Float32x4(tangents)) =1219self.attribute_mut(Mesh::ATTRIBUTE_TANGENT)1220{1221// Transform tangents, taking into account non-uniform scaling1222tangents.iter_mut().for_each(|tangent| {1223let handedness = tangent[3];1224let scaled_tangent = Vec3::from_slice(tangent) * scale;1225*tangent = scaled_tangent1226.normalize_or_zero()1227.extend(handedness)1228.to_array();1229});1230}1231}12321233/// Whether this mesh has morph targets.1234pub fn has_morph_targets(&self) -> bool {1235self.morph_targets.is_some()1236}12371238/// Set [morph targets] image for this mesh. This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.1239///1240/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation1241pub fn set_morph_targets(&mut self, morph_targets: Handle<Image>) {1242self.morph_targets = Some(morph_targets);1243}12441245pub fn morph_targets(&self) -> Option<&Handle<Image>> {1246self.morph_targets.as_ref()1247}12481249/// Consumes the mesh and returns a mesh with the given [morph targets].1250///1251/// This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.1252///1253/// (Alternatively, you can use [`Mesh::set_morph_targets`] to mutate an existing mesh in-place)1254///1255/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation1256#[must_use]1257pub fn with_morph_targets(mut self, morph_targets: Handle<Image>) -> Self {1258self.set_morph_targets(morph_targets);1259self1260}12611262/// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`.1263pub fn set_morph_target_names(&mut self, names: Vec<String>) {1264self.morph_target_names = Some(names);1265}12661267/// Consumes the mesh and returns a mesh with morph target names.1268/// Names should correspond to the order of the morph targets in `set_morph_targets`.1269///1270/// (Alternatively, you can use [`Mesh::set_morph_target_names`] to mutate an existing mesh in-place)1271#[must_use]1272pub fn with_morph_target_names(mut self, names: Vec<String>) -> Self {1273self.set_morph_target_names(names);1274self1275}12761277/// Gets a list of all morph target names, if they exist.1278pub fn morph_target_names(&self) -> Option<&[String]> {1279self.morph_target_names.as_deref()1280}12811282/// Normalize joint weights so they sum to 1.1283pub fn normalize_joint_weights(&mut self) {1284if let Some(joints) = self.attribute_mut(Self::ATTRIBUTE_JOINT_WEIGHT) {1285let VertexAttributeValues::Float32x4(joints) = joints else {1286panic!("unexpected joint weight format");1287};12881289for weights in joints.iter_mut() {1290// force negative weights to zero1291weights.iter_mut().for_each(|w| *w = w.max(0.0));12921293let sum: f32 = weights.iter().sum();1294if sum == 0.0 {1295// all-zero weights are invalid1296weights[0] = 1.0;1297} else {1298let recip = sum.recip();1299for weight in weights.iter_mut() {1300*weight *= recip;1301}1302}1303}1304}1305}13061307/// Get a list of this Mesh's [triangles] as an iterator if possible.1308///1309/// Returns an error if any of the following conditions are met (see [`MeshTrianglesError`]):1310/// * The Mesh's [primitive topology] is not `TriangleList` or `TriangleStrip`.1311/// * The Mesh is missing position or index data.1312/// * The Mesh's position data has the wrong format (not `Float32x3`).1313///1314/// [primitive topology]: PrimitiveTopology1315/// [triangles]: Triangle3d1316pub fn triangles(&self) -> Result<impl Iterator<Item = Triangle3d> + '_, MeshTrianglesError> {1317let Some(position_data) = self.attribute(Mesh::ATTRIBUTE_POSITION) else {1318return Err(MeshTrianglesError::MissingPositions);1319};13201321let Some(vertices) = position_data.as_float3() else {1322return Err(MeshTrianglesError::PositionsFormat);1323};13241325let Some(indices) = self.indices() else {1326return Err(MeshTrianglesError::MissingIndices);1327};13281329match self.primitive_topology {1330PrimitiveTopology::TriangleList => {1331// When indices reference out-of-bounds vertex data, the triangle is omitted.1332// This implicitly truncates the indices to a multiple of 3.1333let iterator = match indices {1334Indices::U16(vec) => FourIterators::First(1335vec.as_slice()1336.chunks_exact(3)1337.flat_map(move |indices| indices_to_triangle(vertices, indices)),1338),1339Indices::U32(vec) => FourIterators::Second(1340vec.as_slice()1341.chunks_exact(3)1342.flat_map(move |indices| indices_to_triangle(vertices, indices)),1343),1344};13451346return Ok(iterator);1347}13481349PrimitiveTopology::TriangleStrip => {1350// When indices reference out-of-bounds vertex data, the triangle is omitted.1351// If there aren't enough indices to make a triangle, then an empty vector will be1352// returned.1353let iterator = match indices {1354Indices::U16(vec) => {1355FourIterators::Third(vec.as_slice().windows(3).enumerate().flat_map(1356move |(i, indices)| {1357if i % 2 == 0 {1358indices_to_triangle(vertices, indices)1359} else {1360indices_to_triangle(1361vertices,1362&[indices[1], indices[0], indices[2]],1363)1364}1365},1366))1367}1368Indices::U32(vec) => {1369FourIterators::Fourth(vec.as_slice().windows(3).enumerate().flat_map(1370move |(i, indices)| {1371if i % 2 == 0 {1372indices_to_triangle(vertices, indices)1373} else {1374indices_to_triangle(1375vertices,1376&[indices[1], indices[0], indices[2]],1377)1378}1379},1380))1381}1382};13831384return Ok(iterator);1385}13861387_ => {1388return Err(MeshTrianglesError::WrongTopology);1389}1390};13911392fn indices_to_triangle<T: TryInto<usize> + Copy>(1393vertices: &[[f32; 3]],1394indices: &[T],1395) -> Option<Triangle3d> {1396let vert0: Vec3 = Vec3::from(*vertices.get(indices[0].try_into().ok()?)?);1397let vert1: Vec3 = Vec3::from(*vertices.get(indices[1].try_into().ok()?)?);1398let vert2: Vec3 = Vec3::from(*vertices.get(indices[2].try_into().ok()?)?);1399Some(Triangle3d {1400vertices: [vert0, vert1, vert2],1401})1402}1403}1404}14051406impl core::ops::Mul<Mesh> for Transform {1407type Output = Mesh;14081409fn mul(self, rhs: Mesh) -> Self::Output {1410rhs.transformed_by(self)1411}1412}14131414/// A version of [`Mesh`] suitable for serializing for short-term transfer.1415///1416/// [`Mesh`] does not implement [`Serialize`] / [`Deserialize`] because it is made with the renderer in mind.1417/// It is not a general-purpose mesh implementation, and its internals are subject to frequent change.1418/// As such, storing a [`Mesh`] on disk is highly discouraged.1419///1420/// But there are still some valid use cases for serializing a [`Mesh`], namely transferring meshes between processes.1421/// To support this, you can create a [`SerializedMesh`] from a [`Mesh`] with [`SerializedMesh::from_mesh`],1422/// and then deserialize it with [`SerializedMesh::deserialize`]. The caveats are:1423/// - The mesh representation is not valid across different versions of Bevy.1424/// - This conversion is lossy. Only the following information is preserved:1425/// - Primitive topology1426/// - Vertex attributes1427/// - Indices1428/// - Custom attributes that were not specified with [`MeshDeserializer::add_custom_vertex_attribute`] will be ignored while deserializing.1429#[cfg(feature = "serialize")]1430#[derive(Debug, Clone, Serialize, Deserialize)]1431pub struct SerializedMesh {1432primitive_topology: PrimitiveTopology,1433attributes: Vec<(MeshVertexAttributeId, SerializedMeshAttributeData)>,1434indices: Option<Indices>,1435}14361437#[cfg(feature = "serialize")]1438impl SerializedMesh {1439/// Create a [`SerializedMesh`] from a [`Mesh`]. See the documentation for [`SerializedMesh`] for caveats.1440pub fn from_mesh(mesh: Mesh) -> Self {1441Self {1442primitive_topology: mesh.primitive_topology,1443attributes: mesh1444.attributes1445.into_iter()1446.map(|(id, data)| {1447(1448id,1449SerializedMeshAttributeData::from_mesh_attribute_data(data),1450)1451})1452.collect(),1453indices: mesh.indices,1454}1455}14561457/// Create a [`Mesh`] from a [`SerializedMesh`]. See the documentation for [`SerializedMesh`] for caveats.1458///1459/// Use [`MeshDeserializer`] if you need to pass extra options to the deserialization process, such as specifying custom vertex attributes.1460pub fn into_mesh(self) -> Mesh {1461MeshDeserializer::default().deserialize(self)1462}1463}14641465/// Use to specify extra options when deserializing a [`SerializedMesh`] into a [`Mesh`].1466#[cfg(feature = "serialize")]1467pub struct MeshDeserializer {1468custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,1469}14701471#[cfg(feature = "serialize")]1472impl Default for MeshDeserializer {1473fn default() -> Self {1474// Written like this so that the compiler can validate that we use all the built-in attributes.1475// If you just added a new attribute and got a compile error, please add it to this list :)1476const BUILTINS: [MeshVertexAttribute; Mesh::FIRST_AVAILABLE_CUSTOM_ATTRIBUTE as usize] = [1477Mesh::ATTRIBUTE_POSITION,1478Mesh::ATTRIBUTE_NORMAL,1479Mesh::ATTRIBUTE_UV_0,1480Mesh::ATTRIBUTE_UV_1,1481Mesh::ATTRIBUTE_TANGENT,1482Mesh::ATTRIBUTE_COLOR,1483Mesh::ATTRIBUTE_JOINT_WEIGHT,1484Mesh::ATTRIBUTE_JOINT_INDEX,1485];1486Self {1487custom_vertex_attributes: BUILTINS1488.into_iter()1489.map(|attribute| (attribute.name.into(), attribute))1490.collect(),1491}1492}1493}14941495#[cfg(feature = "serialize")]1496impl MeshDeserializer {1497/// Create a new [`MeshDeserializer`].1498pub fn new() -> Self {1499Self::default()1500}15011502/// Register a custom vertex attribute to the deserializer. Custom vertex attributes that were not added with this method will be ignored while deserializing.1503pub fn add_custom_vertex_attribute(1504&mut self,1505name: &str,1506attribute: MeshVertexAttribute,1507) -> &mut Self {1508self.custom_vertex_attributes.insert(name.into(), attribute);1509self1510}15111512/// Deserialize a [`SerializedMesh`] into a [`Mesh`].1513///1514/// See the documentation for [`SerializedMesh`] for caveats.1515pub fn deserialize(&self, serialized_mesh: SerializedMesh) -> Mesh {1516Mesh {1517attributes:1518serialized_mesh1519.attributes1520.into_iter()1521.filter_map(|(id, data)| {1522let attribute = data.attribute.clone();1523let Some(data) =1524data.try_into_mesh_attribute_data(&self.custom_vertex_attributes)1525else {1526warn!(1527"Deserialized mesh contains custom vertex attribute {attribute:?} that \1528was not specified with `MeshDeserializer::add_custom_vertex_attribute`. Ignoring."1529);1530return None;1531};1532Some((id, data))1533})1534.collect(),1535indices: serialized_mesh.indices,1536..Mesh::new(serialized_mesh.primitive_topology, RenderAssetUsages::default())1537}1538}1539}15401541/// Error that can occur when calling [`Mesh::merge`].1542#[derive(Error, Debug, Clone)]1543pub enum MeshMergeError {1544#[error("Incompatible vertex attribute types: {} and {}", self_attribute.name, other_attribute.map(|a| a.name).unwrap_or("None"))]1545IncompatibleVertexAttributes {1546self_attribute: MeshVertexAttribute,1547other_attribute: Option<MeshVertexAttribute>,1548},1549#[error(1550"Incompatible primitive topologies: {:?} and {:?}",1551self_primitive_topology,1552other_primitive_topology1553)]1554IncompatiblePrimitiveTopology {1555self_primitive_topology: PrimitiveTopology,1556other_primitive_topology: PrimitiveTopology,1557},1558}15591560#[cfg(test)]1561mod tests {1562use super::Mesh;1563#[cfg(feature = "serialize")]1564use super::SerializedMesh;1565use crate::mesh::{Indices, MeshWindingInvertError, VertexAttributeValues};1566use crate::PrimitiveTopology;1567use bevy_asset::RenderAssetUsages;1568use bevy_math::primitives::Triangle3d;1569use bevy_math::Vec3;1570use bevy_transform::components::Transform;15711572#[test]1573#[should_panic]1574fn panic_invalid_format() {1575let _mesh = Mesh::new(1576PrimitiveTopology::TriangleList,1577RenderAssetUsages::default(),1578)1579.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0, 0.0]]);1580}15811582#[test]1583fn transform_mesh() {1584let mesh = Mesh::new(1585PrimitiveTopology::TriangleList,1586RenderAssetUsages::default(),1587)1588.with_inserted_attribute(1589Mesh::ATTRIBUTE_POSITION,1590vec![[-1., -1., 2.], [1., -1., 2.], [0., 1., 2.]],1591)1592.with_inserted_attribute(1593Mesh::ATTRIBUTE_NORMAL,1594vec![1595Vec3::new(-1., -1., 1.).normalize().to_array(),1596Vec3::new(1., -1., 1.).normalize().to_array(),1597[0., 0., 1.],1598],1599)1600.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0., 0.], [1., 0.], [0.5, 1.]]);16011602let mesh = mesh.transformed_by(1603Transform::from_translation(Vec3::splat(-2.)).with_scale(Vec3::new(2., 0., -1.)),1604);16051606if let Some(VertexAttributeValues::Float32x3(positions)) =1607mesh.attribute(Mesh::ATTRIBUTE_POSITION)1608{1609// All positions are first scaled resulting in `vec![[-2, 0., -2.], [2., 0., -2.], [0., 0., -2.]]`1610// and then shifted by `-2.` along each axis1611assert_eq!(1612positions,1613&vec![[-4.0, -2.0, -4.0], [0.0, -2.0, -4.0], [-2.0, -2.0, -4.0]]1614);1615} else {1616panic!("Mesh does not have a position attribute");1617}16181619if let Some(VertexAttributeValues::Float32x3(normals)) =1620mesh.attribute(Mesh::ATTRIBUTE_NORMAL)1621{1622assert_eq!(normals, &vec![[0., -1., 0.], [0., -1., 0.], [0., 0., -1.]]);1623} else {1624panic!("Mesh does not have a normal attribute");1625}16261627if let Some(VertexAttributeValues::Float32x2(uvs)) = mesh.attribute(Mesh::ATTRIBUTE_UV_0) {1628assert_eq!(uvs, &vec![[0., 0.], [1., 0.], [0.5, 1.]]);1629} else {1630panic!("Mesh does not have a uv attribute");1631}1632}16331634#[test]1635fn point_list_mesh_invert_winding() {1636let mesh = Mesh::new(PrimitiveTopology::PointList, RenderAssetUsages::default())1637.with_inserted_indices(Indices::U32(vec![]));1638assert!(matches!(1639mesh.with_inverted_winding(),1640Err(MeshWindingInvertError::WrongTopology)1641));1642}16431644#[test]1645fn line_list_mesh_invert_winding() {1646let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())1647.with_inserted_indices(Indices::U32(vec![0, 1, 1, 2, 2, 3]));1648let mesh = mesh.with_inverted_winding().unwrap();1649assert_eq!(1650mesh.indices().unwrap().iter().collect::<Vec<usize>>(),1651vec![3, 2, 2, 1, 1, 0]1652);1653}16541655#[test]1656fn line_list_mesh_invert_winding_fail() {1657let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())1658.with_inserted_indices(Indices::U32(vec![0, 1, 1]));1659assert!(matches!(1660mesh.with_inverted_winding(),1661Err(MeshWindingInvertError::AbruptIndicesEnd)1662));1663}16641665#[test]1666fn line_strip_mesh_invert_winding() {1667let mesh = Mesh::new(PrimitiveTopology::LineStrip, RenderAssetUsages::default())1668.with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));1669let mesh = mesh.with_inverted_winding().unwrap();1670assert_eq!(1671mesh.indices().unwrap().iter().collect::<Vec<usize>>(),1672vec![3, 2, 1, 0]1673);1674}16751676#[test]1677fn triangle_list_mesh_invert_winding() {1678let mesh = Mesh::new(1679PrimitiveTopology::TriangleList,1680RenderAssetUsages::default(),1681)1682.with_inserted_indices(Indices::U32(vec![16830, 3, 1, // First triangle16841, 3, 2, // Second triangle1685]));1686let mesh = mesh.with_inverted_winding().unwrap();1687assert_eq!(1688mesh.indices().unwrap().iter().collect::<Vec<usize>>(),1689vec![16900, 1, 3, // First triangle16911, 2, 3, // Second triangle1692]1693);1694}16951696#[test]1697fn triangle_list_mesh_invert_winding_fail() {1698let mesh = Mesh::new(1699PrimitiveTopology::TriangleList,1700RenderAssetUsages::default(),1701)1702.with_inserted_indices(Indices::U32(vec![0, 3, 1, 2]));1703assert!(matches!(1704mesh.with_inverted_winding(),1705Err(MeshWindingInvertError::AbruptIndicesEnd)1706));1707}17081709#[test]1710fn triangle_strip_mesh_invert_winding() {1711let mesh = Mesh::new(1712PrimitiveTopology::TriangleStrip,1713RenderAssetUsages::default(),1714)1715.with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));1716let mesh = mesh.with_inverted_winding().unwrap();1717assert_eq!(1718mesh.indices().unwrap().iter().collect::<Vec<usize>>(),1719vec![3, 2, 1, 0]1720);1721}17221723#[test]1724fn compute_area_weighted_normals() {1725let mut mesh = Mesh::new(1726PrimitiveTopology::TriangleList,1727RenderAssetUsages::default(),1728);17291730// z y1731// | /1732// 3---21733// | / \1734// 0-----1--x17351736mesh.insert_attribute(1737Mesh::ATTRIBUTE_POSITION,1738vec![[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]],1739);1740mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));1741mesh.compute_area_weighted_normals();1742let normals = mesh1743.attribute(Mesh::ATTRIBUTE_NORMAL)1744.unwrap()1745.as_float3()1746.unwrap();1747assert_eq!(4, normals.len());1748// 01749assert_eq!(Vec3::new(1., 0., 1.).normalize().to_array(), normals[0]);1750// 11751assert_eq!([0., 0., 1.], normals[1]);1752// 21753assert_eq!(Vec3::new(1., 0., 1.).normalize().to_array(), normals[2]);1754// 31755assert_eq!([1., 0., 0.], normals[3]);1756}17571758#[test]1759fn compute_area_weighted_normals_proportionate() {1760let mut mesh = Mesh::new(1761PrimitiveTopology::TriangleList,1762RenderAssetUsages::default(),1763);17641765// z y1766// | /1767// 3---2..1768// | / \1769// 0-------1---x17701771mesh.insert_attribute(1772Mesh::ATTRIBUTE_POSITION,1773vec![[0., 0., 0.], [2., 0., 0.], [0., 1., 0.], [0., 0., 1.]],1774);1775mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));1776mesh.compute_area_weighted_normals();1777let normals = mesh1778.attribute(Mesh::ATTRIBUTE_NORMAL)1779.unwrap()1780.as_float3()1781.unwrap();1782assert_eq!(4, normals.len());1783// 01784assert_eq!(Vec3::new(1., 0., 2.).normalize().to_array(), normals[0]);1785// 11786assert_eq!([0., 0., 1.], normals[1]);1787// 21788assert_eq!(Vec3::new(1., 0., 2.).normalize().to_array(), normals[2]);1789// 31790assert_eq!([1., 0., 0.], normals[3]);1791}17921793#[test]1794fn compute_angle_weighted_normals() {1795// CuboidMeshBuilder duplicates vertices (even though it is indexed)17961797// 5---------41798// /| /|1799// 1-+-------0 |1800// | 6-------|-71801// |/ |/1802// 2---------31803let verts = vec![1804[1.0, 1.0, 1.0],1805[-1.0, 1.0, 1.0],1806[-1.0, -1.0, 1.0],1807[1.0, -1.0, 1.0],1808[1.0, 1.0, -1.0],1809[-1.0, 1.0, -1.0],1810[-1.0, -1.0, -1.0],1811[1.0, -1.0, -1.0],1812];18131814let indices = Indices::U16(vec![18150, 1, 2, 2, 3, 0, // front18165, 4, 7, 7, 6, 5, // back18171, 5, 6, 6, 2, 1, // left18184, 0, 3, 3, 7, 4, // right18194, 5, 1, 1, 0, 4, // top18203, 2, 6, 6, 7, 3, // bottom1821]);1822let mut mesh = Mesh::new(1823PrimitiveTopology::TriangleList,1824RenderAssetUsages::default(),1825);1826mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, verts);1827mesh.insert_indices(indices);1828mesh.compute_smooth_normals();18291830let normals = mesh1831.attribute(Mesh::ATTRIBUTE_NORMAL)1832.unwrap()1833.as_float3()1834.unwrap();18351836for new in normals.iter().copied().flatten() {1837// std impl is unstable1838const FRAC_1_SQRT_3: f32 = 0.57735026;1839const MIN: f32 = FRAC_1_SQRT_3 - f32::EPSILON;1840const MAX: f32 = FRAC_1_SQRT_3 + f32::EPSILON;1841assert!(new.abs() >= MIN, "{new} < {MIN}");1842assert!(new.abs() <= MAX, "{new} > {MAX}");1843}1844}18451846#[test]1847fn triangles_from_triangle_list() {1848let mut mesh = Mesh::new(1849PrimitiveTopology::TriangleList,1850RenderAssetUsages::default(),1851);1852mesh.insert_attribute(1853Mesh::ATTRIBUTE_POSITION,1854vec![[0., 0., 0.], [1., 0., 0.], [1., 1., 0.], [0., 1., 0.]],1855);1856mesh.insert_indices(Indices::U32(vec![0, 1, 2, 2, 3, 0]));1857assert_eq!(1858vec![1859Triangle3d {1860vertices: [1861Vec3::new(0., 0., 0.),1862Vec3::new(1., 0., 0.),1863Vec3::new(1., 1., 0.),1864]1865},1866Triangle3d {1867vertices: [1868Vec3::new(1., 1., 0.),1869Vec3::new(0., 1., 0.),1870Vec3::new(0., 0., 0.),1871]1872}1873],1874mesh.triangles().unwrap().collect::<Vec<Triangle3d>>()1875);1876}18771878#[test]1879fn triangles_from_triangle_strip() {1880let mut mesh = Mesh::new(1881PrimitiveTopology::TriangleStrip,1882RenderAssetUsages::default(),1883);1884// Triangles: (0, 1, 2), (2, 1, 3), (2, 3, 4), (4, 3, 5)1885//1886// 4 - 51887// | \ |1888// 2 - 31889// | \ |1890// 0 - 11891let positions: Vec<Vec3> = [1892[0., 0., 0.],1893[1., 0., 0.],1894[0., 1., 0.],1895[1., 1., 0.],1896[0., 2., 0.],1897[1., 2., 0.],1898]1899.into_iter()1900.map(Vec3::from_array)1901.collect();1902mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions.clone());1903mesh.insert_indices(Indices::U32(vec![0, 1, 2, 3, 4, 5]));1904assert_eq!(1905vec![1906Triangle3d {1907vertices: [positions[0], positions[1], positions[2]]1908},1909Triangle3d {1910vertices: [positions[2], positions[1], positions[3]]1911},1912Triangle3d {1913vertices: [positions[2], positions[3], positions[4]]1914},1915Triangle3d {1916vertices: [positions[4], positions[3], positions[5]]1917},1918],1919mesh.triangles().unwrap().collect::<Vec<Triangle3d>>()1920);1921}19221923#[cfg(feature = "serialize")]1924#[test]1925fn serialize_deserialize_mesh() {1926let mut mesh = Mesh::new(1927PrimitiveTopology::TriangleList,1928RenderAssetUsages::default(),1929);19301931mesh.insert_attribute(1932Mesh::ATTRIBUTE_POSITION,1933vec![[0., 0., 0.], [2., 0., 0.], [0., 1., 0.], [0., 0., 1.]],1934);1935mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));19361937let serialized_mesh = SerializedMesh::from_mesh(mesh.clone());1938let serialized_string = serde_json::to_string(&serialized_mesh).unwrap();1939let serialized_mesh_from_string: SerializedMesh =1940serde_json::from_str(&serialized_string).unwrap();1941let deserialized_mesh = serialized_mesh_from_string.into_mesh();1942assert_eq!(mesh, deserialized_mesh);1943}1944}194519461947