bevy_mesh/mesh.rs
1use bevy_transform::components::Transform;
2pub use wgpu_types::PrimitiveTopology;
3
4use super::{
5 skinning::{SkinnedMeshBounds, SkinnedMeshBoundsError},
6 triangle_area_normal, triangle_normal, FourIterators, Indices, MeshAttributeData,
7 MeshTrianglesError, MeshVertexAttribute, MeshVertexAttributeId, MeshVertexBufferLayout,
8 MeshVertexBufferLayoutRef, MeshVertexBufferLayouts, MeshWindingInvertError,
9 VertexAttributeValues, VertexBufferLayout,
10};
11#[cfg(feature = "morph")]
12use crate::morph::MorphAttributes;
13use crate::AttributeQuantization;
14#[cfg(feature = "serialize")]
15use crate::SerializedMeshAttributeData;
16use alloc::borrow::Cow;
17use alloc::collections::BTreeMap;
18use bevy_asset::{Asset, RenderAssetUsages};
19use bevy_math::*;
20use bevy_platform::collections::{hash_map, HashMap};
21use bevy_reflect::{std_traits::ReflectDefault, Reflect};
22use bevy_shape::{Aabb2d, Aabb3d, Triangle3d};
23use bytemuck::cast_slice;
24use core::hash::{Hash, Hasher};
25use core::ptr;
26#[cfg(feature = "serialize")]
27use serde::{Deserialize, Serialize};
28use thiserror::Error;
29use tracing::warn;
30use wgpu_types::{VertexAttribute, VertexFormat, VertexStepMode, WriteOnly};
31
32pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
33pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
34
35/// Error from accessing mesh vertex attributes or indices
36#[derive(Error, Debug, Clone)]
37pub enum MeshAccessError {
38 #[error("The mesh vertex/index data has been extracted to the RenderWorld (via `Mesh::asset_usage`)")]
39 ExtractedToRenderWorld,
40 #[error("The requested mesh data wasn't found in this mesh")]
41 NotFound,
42}
43
44const MESH_EXTRACTED_ERROR: &str = "Mesh has been extracted to RenderWorld. To access vertex attributes, the mesh `asset_usage` must include `MAIN_WORLD`";
45
46// storage for extractable data with access methods which return errors if the
47// contents have already been extracted
48#[derive(Debug, Clone, PartialEq, Reflect, Default)]
49enum MeshExtractableData<T> {
50 Data(T),
51 #[default]
52 NoData,
53 ExtractedToRenderWorld,
54}
55
56impl<T> MeshExtractableData<T> {
57 // get a reference to internal data. returns error if data has been extracted, or if no
58 // data exists
59 fn as_ref(&self) -> Result<&T, MeshAccessError> {
60 match self {
61 MeshExtractableData::Data(data) => Ok(data),
62 MeshExtractableData::NoData => Err(MeshAccessError::NotFound),
63 MeshExtractableData::ExtractedToRenderWorld => {
64 Err(MeshAccessError::ExtractedToRenderWorld)
65 }
66 }
67 }
68
69 // get an optional reference to internal data. returns error if data has been extracted
70 fn as_ref_option(&self) -> Result<Option<&T>, MeshAccessError> {
71 match self {
72 MeshExtractableData::Data(data) => Ok(Some(data)),
73 MeshExtractableData::NoData => Ok(None),
74 MeshExtractableData::ExtractedToRenderWorld => {
75 Err(MeshAccessError::ExtractedToRenderWorld)
76 }
77 }
78 }
79
80 // get a mutable reference to internal data. returns error if data has been extracted,
81 // or if no data exists
82 fn as_mut(&mut self) -> Result<&mut T, MeshAccessError> {
83 match self {
84 MeshExtractableData::Data(data) => Ok(data),
85 MeshExtractableData::NoData => Err(MeshAccessError::NotFound),
86 MeshExtractableData::ExtractedToRenderWorld => {
87 Err(MeshAccessError::ExtractedToRenderWorld)
88 }
89 }
90 }
91
92 // get an optional mutable reference to internal data. returns error if data has been extracted
93 fn as_mut_option(&mut self) -> Result<Option<&mut T>, MeshAccessError> {
94 match self {
95 MeshExtractableData::Data(data) => Ok(Some(data)),
96 MeshExtractableData::NoData => Ok(None),
97 MeshExtractableData::ExtractedToRenderWorld => {
98 Err(MeshAccessError::ExtractedToRenderWorld)
99 }
100 }
101 }
102
103 // extract data and replace self with `ExtractedToRenderWorld`. returns error if
104 // data has been extracted
105 fn extract(&mut self) -> Result<MeshExtractableData<T>, MeshAccessError> {
106 match core::mem::replace(self, MeshExtractableData::ExtractedToRenderWorld) {
107 MeshExtractableData::ExtractedToRenderWorld => {
108 Err(MeshAccessError::ExtractedToRenderWorld)
109 }
110 not_extracted => Ok(not_extracted),
111 }
112 }
113
114 // replace internal data. returns the existing data, or an error if data has been extracted
115 fn replace(
116 &mut self,
117 data: impl Into<MeshExtractableData<T>>,
118 ) -> Result<Option<T>, MeshAccessError> {
119 match core::mem::replace(self, data.into()) {
120 MeshExtractableData::ExtractedToRenderWorld => {
121 *self = MeshExtractableData::ExtractedToRenderWorld;
122 Err(MeshAccessError::ExtractedToRenderWorld)
123 }
124 MeshExtractableData::Data(t) => Ok(Some(t)),
125 MeshExtractableData::NoData => Ok(None),
126 }
127 }
128}
129
130impl<T> From<Option<T>> for MeshExtractableData<T> {
131 fn from(value: Option<T>) -> Self {
132 match value {
133 Some(data) => MeshExtractableData::Data(data),
134 None => MeshExtractableData::NoData,
135 }
136 }
137}
138
139/// A 3D object made out of vertices representing triangles, lines, or points,
140/// with "attribute" values for each vertex.
141///
142/// Meshes can be automatically generated by a bevy `AssetLoader` (generally by loading a `Gltf` file),
143/// or by converting a [primitive](bevy_shape) using [`into`](Into).
144/// It is also possible to create one manually. They can be edited after creation.
145///
146/// Meshes can be rendered with a [`Mesh2d`](crate::Mesh2d) and `MeshMaterial2d`
147/// or [`Mesh3d`](crate::Mesh3d) and `MeshMaterial3d` for 2D and 3D respectively.
148///
149/// A [`Mesh`] in Bevy is equivalent to a "primitive" in the glTF format, for a
150/// glTF Mesh representation, see `GltfMesh`.
151///
152/// ## Manual creation
153///
154/// The following function will construct a flat mesh, to be rendered with a
155/// `StandardMaterial` or `ColorMaterial`:
156///
157/// ```
158/// # use bevy_mesh::{Mesh, Indices, PrimitiveTopology};
159/// # use bevy_asset::RenderAssetUsages;
160/// fn create_simple_parallelogram() -> Mesh {
161/// // Create a new mesh using a triangle list topology, where each set of 3 vertices composes a triangle.
162/// Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default())
163/// // Add 4 vertices, each with its own position attribute (coordinate in
164/// // 3D space), for each of the corners of the parallelogram.
165/// .with_inserted_attribute(
166/// Mesh::ATTRIBUTE_POSITION,
167/// 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]]
168/// )
169/// // Assign a UV coordinate to each vertex.
170/// .with_inserted_attribute(
171/// Mesh::ATTRIBUTE_UV_0,
172/// vec![[0.0, 1.0], [0.5, 0.0], [1.0, 0.0], [0.5, 1.0]]
173/// )
174/// // Assign normals (everything points outwards)
175/// .with_inserted_attribute(
176/// Mesh::ATTRIBUTE_NORMAL,
177/// 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]]
178/// )
179/// // After defining all the vertices and their attributes, build each triangle using the
180/// // indices of the vertices that make it up in a counter-clockwise order.
181/// .with_inserted_indices(Indices::U32(vec![
182/// // First triangle
183/// 0, 3, 1,
184/// // Second triangle
185/// 1, 3, 2
186/// ]))
187/// }
188/// ```
189///
190/// You can see how it looks like [here](https://github.com/bevyengine/bevy/blob/main/assets/docs/Mesh.png),
191/// used in a [`Mesh3d`](crate::Mesh3d) with a square bevy logo texture, with added axis, points,
192/// lines and text for clarity.
193///
194/// ## Other examples
195///
196/// For further visualization, explanation, and examples, see the built-in Bevy examples,
197/// and the [implementation of the built-in shapes](https://github.com/bevyengine/bevy/tree/main/crates/bevy_mesh/src/primitives).
198/// In particular, [generate_custom_mesh](https://github.com/bevyengine/bevy/blob/main/examples/3d/generate_custom_mesh.rs)
199/// teaches you to access and modify the attributes of a [`Mesh`] after creating it.
200///
201/// ## Common points of confusion
202///
203/// - UV maps in Bevy start at the top-left, see [`ATTRIBUTE_UV_0`](Mesh::ATTRIBUTE_UV_0),
204/// other APIs can have other conventions, `OpenGL` starts at bottom-left.
205/// - It is possible and sometimes useful for multiple vertices to have the same
206/// [position attribute](Mesh::ATTRIBUTE_POSITION) value,
207/// it's a common technique in 3D modeling for complex UV mapping or other calculations.
208/// - Bevy performs frustum culling based on the `Aabb` of meshes, which is calculated
209/// and added automatically for new meshes only. If a mesh is modified, the entity's `Aabb`
210/// needs to be updated manually or deleted so that it is re-calculated.
211///
212/// ## Use with `StandardMaterial`
213///
214/// To render correctly with `StandardMaterial`, a mesh needs to have properly defined:
215/// - [`UVs`](Mesh::ATTRIBUTE_UV_0): Bevy needs to know how to map a texture onto the mesh
216/// (also true for `ColorMaterial`).
217/// - [`Normals`](Mesh::ATTRIBUTE_NORMAL): Bevy needs to know how light interacts with your mesh.
218/// [0.0, 0.0, 1.0] is very common for simple flat meshes on the XY plane,
219/// because simple meshes are smooth and they don't require complex light calculations.
220/// - Vertex winding order: by default, `StandardMaterial.cull_mode` is `Some(Face::Back)`,
221/// which means that Bevy would *only* render the "front" of each triangle, which
222/// is the side of the triangle from where the vertices appear in a *counter-clockwise* order.
223///
224/// ## Remote Inspection
225///
226/// To transmit a [`Mesh`] between two running Bevy apps, e.g. through BRP, use [`SerializedMesh`].
227/// This type is only meant for short-term transmission between same versions and should not be stored anywhere.
228#[derive(Asset, Debug, Clone, Reflect, PartialEq)]
229#[reflect(Clone)]
230pub struct Mesh {
231 #[reflect(ignore, clone)]
232 primitive_topology: PrimitiveTopology,
233 /// `std::collections::BTreeMap` with all defined vertex attributes (Positions, Normals, ...)
234 /// for this mesh. Attribute ids to attribute values.
235 /// Uses a [`BTreeMap`] because, unlike `HashMap`, it has a defined iteration order,
236 /// which allows easy stable `VertexBuffers` (i.e. same buffer order)
237 #[reflect(ignore, clone)]
238 attributes: MeshExtractableData<BTreeMap<MeshVertexAttributeId, MeshAttributeData>>,
239 indices: MeshExtractableData<Indices>,
240 #[cfg(feature = "morph")]
241 morph_targets: MeshExtractableData<Vec<MorphAttributes>>,
242 #[cfg(feature = "morph")]
243 morph_target_names: MeshExtractableData<Vec<String>>,
244 pub asset_usage: RenderAssetUsages,
245 /// Whether or not to build a BLAS for use with `bevy_solari` raytracing.
246 ///
247 /// Note that this is _not_ whether the mesh is _compatible_ with `bevy_solari` raytracing.
248 /// This field just controls whether or not a BLAS gets built for this mesh, assuming that
249 /// the mesh is compatible.
250 ///
251 /// The use case for this field is using lower-resolution proxy meshes for raytracing (to save on BLAS memory usage),
252 /// while using higher-resolution meshes for raster. You can set this field to true for the lower-resolution proxy mesh,
253 /// and to false for the high-resolution raster mesh.
254 ///
255 /// Alternatively, you can use the same mesh for both raster and raytracing, with this field set to true.
256 ///
257 /// Does nothing if not used with `bevy_solari`, or if the mesh is not compatible
258 /// with `bevy_solari` (see `bevy_solari`'s docs).
259 pub enable_raytracing: bool,
260 /// Indicate whether vertex attributes are compressed.
261 attribute_compression: MeshAttributeCompressionFlags,
262 /// Precomputed min and max extents of the mesh position data. Used mainly for constructing `Aabb`s for frustum culling and decompressing vertex positions.
263 /// This data will be set if/when a mesh is extracted to the GPU or when compressing positions.
264 pub final_aabb: Option<Aabb3d>,
265 /// Precomputed min and max extents of the mesh UV channels data. Used mainly for decompressing vertex UVs.
266 /// This will be set when compressing UVs.
267 pub final_uv_ranges: [Option<Aabb2d>; 2],
268 skinned_mesh_bounds: Option<SkinnedMeshBounds>,
269}
270
271/// Mesh compression arguments used in [`Mesh::compressed_mesh`].
272#[derive(Debug, Clone)]
273#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
274pub struct MeshCompressionArgs {
275 /// Whether to compress indices to [`Indices::U16`] when possible.
276 pub compress_indices: bool,
277 /// Quantize Float32 to smaller types for some attributes. No change is needed in shaders.
278 pub quantize_attributes: Cow<'static, [(MeshVertexAttributeId, AttributeQuantization)]>,
279 /// Compress some attributes to smaller representation. Shaders need change to decode them correctly.
280 /// See [`MeshAttributeCompressionFlags`] for the details.
281 pub compress_attributes: MeshAttributeCompressionFlags,
282}
283
284impl MeshCompressionArgs {
285 /// None of compression/quantization is enabled.
286 pub fn none() -> Self {
287 Self {
288 compress_indices: false,
289 quantize_attributes: (&[]).into(),
290 compress_attributes: MeshAttributeCompressionFlags::empty(),
291 }
292 }
293
294 /// Compression with all attributes compressed and with colors quantized to unorm8 and joint weights quantized to unorm16.
295 pub fn regular() -> Self {
296 Self {
297 compress_indices: true,
298 quantize_attributes: (&[
299 // Unorm8 is chosen by default as glTF vertex color is clamped to [0, 1] and HDR vertex color isn't common.
300 (Mesh::ATTRIBUTE_COLOR.id, AttributeQuantization::Unorm8),
301 (
302 Mesh::ATTRIBUTE_JOINT_WEIGHT.id,
303 AttributeQuantization::Unorm16,
304 ),
305 ])
306 .into(),
307 compress_attributes: MeshAttributeCompressionFlags::all(),
308 }
309 }
310}
311
312bitflags::bitflags! {
313 /// If the corresponding attribute compression is enabled:
314 /// - Position will be Snorm16x4 relative to the mesh's AABB. The w component is unused.
315 /// - Normal and tangent will be Snorm16x2 with octahedral encoding, using [`octahedral_encode_signed`] and [`octahedral_encode_tangent`].
316 /// - UV0 and UV1 will be Unorm16x2. UVs are remapped based on their min/max values so they can go beyond [0, 1], though a larger range will reduce precision.
317 ///
318 /// [`octahedral_encode_signed`]: crate::vertex::octahedral_encode_signed
319 /// [`octahedral_encode_tangent`]: crate::vertex::octahedral_encode_tangent
320 #[repr(transparent)]
321 #[derive(Hash, Clone, Copy, PartialEq, Eq, Debug, Reflect)]
322 #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
323 #[reflect(opaque)]
324 #[reflect(Hash, Clone, PartialEq, Debug)]
325 pub struct MeshAttributeCompressionFlags: u8 {
326 const COMPRESS_POSITION = 1 << 0;
327 const COMPRESS_NORMAL = 1 << 1;
328 const COMPRESS_TANGENT = 1 << 2;
329 const COMPRESS_UV0 = 1 << 3;
330 const COMPRESS_UV1 = 1 << 4;
331 }
332}
333
334impl Mesh {
335 /// Where the vertex is located in space. Use in conjunction with [`Mesh::insert_attribute`]
336 /// or [`Mesh::with_inserted_attribute`].
337 ///
338 /// The format of this attribute is [`VertexFormat::Float32x3`].
339 pub const ATTRIBUTE_POSITION: MeshVertexAttribute =
340 MeshVertexAttribute::new("Vertex_Position", 0, VertexFormat::Float32x3);
341
342 /// The direction the vertex normal is facing in.
343 /// Use in conjunction with [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`].
344 ///
345 /// The format of this attribute is [`VertexFormat::Float32x3`].
346 pub const ATTRIBUTE_NORMAL: MeshVertexAttribute =
347 MeshVertexAttribute::new("Vertex_Normal", 1, VertexFormat::Float32x3);
348
349 /// Texture coordinates for the vertex. Use in conjunction with [`Mesh::insert_attribute`]
350 /// or [`Mesh::with_inserted_attribute`].
351 ///
352 /// Generally `[0.,0.]` is mapped to the top left of the texture, and `[1.,1.]` to the bottom-right.
353 ///
354 /// By default values outside will be clamped per pixel not for the vertex,
355 /// "stretching" the borders of the texture.
356 /// This behavior can be useful in some cases, usually when the borders have only
357 /// one color, for example a logo, and you want to "extend" those borders.
358 ///
359 /// For different mapping outside of `0..=1` range,
360 /// see [`ImageAddressMode`](https://docs.rs/bevy_image/latest/bevy_image/enum.ImageAddressMode.html).
361 ///
362 /// The format of this attribute is [`VertexFormat::Float32x2`].
363 pub const ATTRIBUTE_UV_0: MeshVertexAttribute =
364 MeshVertexAttribute::new("Vertex_Uv", 2, VertexFormat::Float32x2);
365
366 /// Alternate texture coordinates for the vertex. Use in conjunction with
367 /// [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`].
368 ///
369 /// Typically, these are used for lightmaps, textures that provide
370 /// precomputed illumination.
371 ///
372 /// The format of this attribute is [`VertexFormat::Float32x2`].
373 pub const ATTRIBUTE_UV_1: MeshVertexAttribute =
374 MeshVertexAttribute::new("Vertex_Uv_1", 3, VertexFormat::Float32x2);
375
376 /// The direction of the vertex tangent. Used for normal mapping.
377 /// Usually generated with [`generate_tangents`](Mesh::generate_tangents) or
378 /// [`with_generated_tangents`](Mesh::with_generated_tangents).
379 ///
380 /// The format of this attribute is [`VertexFormat::Float32x4`].
381 pub const ATTRIBUTE_TANGENT: MeshVertexAttribute =
382 MeshVertexAttribute::new("Vertex_Tangent", 4, VertexFormat::Float32x4);
383
384 /// Per vertex coloring. Use in conjunction with [`Mesh::insert_attribute`]
385 /// or [`Mesh::with_inserted_attribute`].
386 ///
387 /// The format of this attribute is [`VertexFormat::Float32x4`].
388 pub const ATTRIBUTE_COLOR: MeshVertexAttribute =
389 MeshVertexAttribute::new("Vertex_Color", 5, VertexFormat::Float32x4);
390
391 /// Per vertex joint transform matrix weight. Use in conjunction with [`Mesh::insert_attribute`]
392 /// or [`Mesh::with_inserted_attribute`].
393 ///
394 /// The format of this attribute is [`VertexFormat::Float32x4`].
395 pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute =
396 MeshVertexAttribute::new("Vertex_JointWeight", 6, VertexFormat::Float32x4);
397
398 /// Per vertex joint transform matrix index. Use in conjunction with [`Mesh::insert_attribute`]
399 /// or [`Mesh::with_inserted_attribute`].
400 ///
401 /// The format of this attribute is [`VertexFormat::Uint16x4`].
402 pub const ATTRIBUTE_JOINT_INDEX: MeshVertexAttribute =
403 MeshVertexAttribute::new("Vertex_JointIndex", 7, VertexFormat::Uint16x4);
404
405 /// The first index that can be used for custom vertex attributes.
406 /// Only the attributes with an index below this are used by Bevy.
407 pub const FIRST_AVAILABLE_CUSTOM_ATTRIBUTE: u64 = 8;
408
409 /// Construct a new mesh. You need to provide a [`PrimitiveTopology`] so that the
410 /// renderer knows how to treat the vertex data. Most of the time this will be
411 /// [`PrimitiveTopology::TriangleList`].
412 pub fn new(primitive_topology: PrimitiveTopology, asset_usage: RenderAssetUsages) -> Self {
413 Mesh {
414 primitive_topology,
415 attributes: MeshExtractableData::Data(Default::default()),
416 indices: MeshExtractableData::NoData,
417 #[cfg(feature = "morph")]
418 morph_targets: MeshExtractableData::NoData,
419 #[cfg(feature = "morph")]
420 morph_target_names: MeshExtractableData::NoData,
421 asset_usage,
422 enable_raytracing: true,
423 attribute_compression: MeshAttributeCompressionFlags::empty(),
424 final_aabb: None,
425 skinned_mesh_bounds: None,
426 final_uv_ranges: [None; 2],
427 }
428 }
429
430 /// Returns the topology of the mesh.
431 pub fn primitive_topology(&self) -> PrimitiveTopology {
432 self.primitive_topology
433 }
434
435 /// Sets the data for a vertex attribute (position, normal, etc.). The name will
436 /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
437 ///
438 /// `Aabb` of entities with modified mesh are not updated automatically.
439 ///
440 /// # Panics
441 /// Panics when the format of the values does not match the attribute's format.
442 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
443 /// this as an error use [`Mesh::try_insert_attribute`]
444 #[inline]
445 pub fn insert_attribute(
446 &mut self,
447 attribute: MeshVertexAttribute,
448 values: impl Into<VertexAttributeValues>,
449 ) {
450 self.try_insert_attribute(attribute, values)
451 .expect(MESH_EXTRACTED_ERROR);
452 }
453
454 /// Sets the data for a vertex attribute (position, normal, etc.). The name will
455 /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
456 ///
457 /// `Aabb` of entities with modified mesh are not updated automatically.
458 ///
459 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
460 ///
461 /// # Panics
462 /// Panics when the format of the values does not match the attribute's format.
463 #[inline]
464 pub fn try_insert_attribute(
465 &mut self,
466 attribute: MeshVertexAttribute,
467 values: impl Into<VertexAttributeValues>,
468 ) -> Result<(), MeshAccessError> {
469 let values = values.into();
470 let values_format = VertexFormat::from(&values);
471 if values_format != attribute.format {
472 panic!(
473 "Failed to insert attribute. Invalid attribute format for {}. Given format is {values_format:?} but expected {:?}",
474 attribute.name, attribute.format
475 );
476 }
477
478 self.attributes
479 .as_mut()?
480 .insert(attribute.id, MeshAttributeData { attribute, values });
481 Ok(())
482 }
483
484 /// Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.).
485 /// The name will often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
486 ///
487 /// (Alternatively, you can use [`Mesh::insert_attribute`] to mutate an existing mesh in-place)
488 ///
489 /// `Aabb` of entities with modified mesh are not updated automatically.
490 ///
491 /// # Panics
492 /// Panics when the format of the values does not match the attribute's format.
493 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
494 /// this as an error use [`Mesh::try_with_inserted_attribute`]
495 #[must_use]
496 #[inline]
497 pub fn with_inserted_attribute(
498 mut self,
499 attribute: MeshVertexAttribute,
500 values: impl Into<VertexAttributeValues>,
501 ) -> Self {
502 self.insert_attribute(attribute, values);
503 self
504 }
505
506 /// Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.).
507 /// The name will often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
508 ///
509 /// (Alternatively, you can use [`Mesh::insert_attribute`] to mutate an existing mesh in-place)
510 ///
511 /// `Aabb` of entities with modified mesh are not updated automatically.
512 ///
513 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
514 #[inline]
515 pub fn try_with_inserted_attribute(
516 mut self,
517 attribute: MeshVertexAttribute,
518 values: impl Into<VertexAttributeValues>,
519 ) -> Result<Self, MeshAccessError> {
520 self.try_insert_attribute(attribute, values)?;
521 Ok(self)
522 }
523
524 /// Removes the data for a vertex attribute
525 ///
526 /// # Panics
527 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
528 /// this as an error use [`Mesh::try_remove_attribute`]
529 pub fn remove_attribute(
530 &mut self,
531 attribute: impl Into<MeshVertexAttributeId>,
532 ) -> Option<VertexAttributeValues> {
533 self.attributes
534 .as_mut()
535 .expect(MESH_EXTRACTED_ERROR)
536 .remove(&attribute.into())
537 .map(|data| data.values)
538 }
539
540 /// Removes the data for a vertex attribute
541 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
542 /// if the attribute does not exist.
543 pub fn try_remove_attribute(
544 &mut self,
545 attribute: impl Into<MeshVertexAttributeId>,
546 ) -> Result<VertexAttributeValues, MeshAccessError> {
547 Ok(self
548 .attributes
549 .as_mut()?
550 .remove(&attribute.into())
551 .ok_or(MeshAccessError::NotFound)?
552 .values)
553 }
554
555 /// Consumes the mesh and returns a mesh without the data for a vertex attribute
556 ///
557 /// (Alternatively, you can use [`Mesh::remove_attribute`] to mutate an existing mesh in-place)
558 ///
559 /// # Panics
560 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
561 /// this as an error use [`Mesh::try_with_removed_attribute`]
562 #[must_use]
563 pub fn with_removed_attribute(mut self, attribute: impl Into<MeshVertexAttributeId>) -> Self {
564 self.remove_attribute(attribute);
565 self
566 }
567
568 /// Consumes the mesh and returns a mesh without the data for a vertex attribute
569 ///
570 /// (Alternatively, you can use [`Mesh::remove_attribute`] to mutate an existing mesh in-place)
571 ///
572 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
573 /// if the attribute does not exist.
574 pub fn try_with_removed_attribute(
575 mut self,
576 attribute: impl Into<MeshVertexAttributeId>,
577 ) -> Result<Self, MeshAccessError> {
578 self.try_remove_attribute(attribute)?;
579 Ok(self)
580 }
581
582 /// Returns a bool indicating if the attribute is present in this mesh's vertex data.
583 ///
584 /// # Panics
585 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
586 /// this as an error use [`Mesh::try_contains_attribute`]
587 #[inline]
588 pub fn contains_attribute(&self, id: impl Into<MeshVertexAttributeId>) -> bool {
589 self.attributes
590 .as_ref()
591 .expect(MESH_EXTRACTED_ERROR)
592 .contains_key(&id.into())
593 }
594
595 /// Returns a bool indicating if the attribute is present in this mesh's vertex data.
596 ///
597 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
598 #[inline]
599 pub fn try_contains_attribute(
600 &self,
601 id: impl Into<MeshVertexAttributeId>,
602 ) -> Result<bool, MeshAccessError> {
603 Ok(self.attributes.as_ref()?.contains_key(&id.into()))
604 }
605
606 /// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].
607 ///
608 /// # Panics
609 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
610 /// this as an error use [`Mesh::try_attribute`] or [`Mesh::try_attribute_option`]
611 #[inline]
612 pub fn attribute(
613 &self,
614 id: impl Into<MeshVertexAttributeId>,
615 ) -> Option<&VertexAttributeValues> {
616 self.try_attribute_option(id).expect(MESH_EXTRACTED_ERROR)
617 }
618
619 /// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].
620 ///
621 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
622 /// if the attribute does not exist.
623 #[inline]
624 pub fn try_attribute(
625 &self,
626 id: impl Into<MeshVertexAttributeId>,
627 ) -> Result<&VertexAttributeValues, MeshAccessError> {
628 self.try_attribute_option(id)?
629 .ok_or(MeshAccessError::NotFound)
630 }
631
632 /// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].
633 ///
634 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
635 #[inline]
636 pub fn try_attribute_option(
637 &self,
638 id: impl Into<MeshVertexAttributeId>,
639 ) -> Result<Option<&VertexAttributeValues>, MeshAccessError> {
640 Ok(self
641 .attributes
642 .as_ref()?
643 .get(&id.into())
644 .map(|data| &data.values))
645 }
646
647 /// Retrieves the full data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].
648 #[inline]
649 pub(crate) fn try_attribute_data(
650 &self,
651 id: impl Into<MeshVertexAttributeId>,
652 ) -> Result<Option<&MeshAttributeData>, MeshAccessError> {
653 Ok(self.attributes.as_ref()?.get(&id.into()))
654 }
655
656 /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.
657 ///
658 /// # Panics
659 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
660 /// this as an error use [`Mesh::try_attribute_mut`]
661 #[inline]
662 pub fn attribute_mut(
663 &mut self,
664 id: impl Into<MeshVertexAttributeId>,
665 ) -> Option<&mut VertexAttributeValues> {
666 self.try_attribute_mut_option(id)
667 .expect(MESH_EXTRACTED_ERROR)
668 }
669
670 /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.
671 ///
672 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
673 /// if the attribute does not exist.
674 #[inline]
675 pub fn try_attribute_mut(
676 &mut self,
677 id: impl Into<MeshVertexAttributeId>,
678 ) -> Result<&mut VertexAttributeValues, MeshAccessError> {
679 self.try_attribute_mut_option(id)?
680 .ok_or(MeshAccessError::NotFound)
681 }
682
683 /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.
684 ///
685 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
686 #[inline]
687 pub fn try_attribute_mut_option(
688 &mut self,
689 id: impl Into<MeshVertexAttributeId>,
690 ) -> Result<Option<&mut VertexAttributeValues>, MeshAccessError> {
691 Ok(self
692 .attributes
693 .as_mut()?
694 .get_mut(&id.into())
695 .map(|data| &mut data.values))
696 }
697
698 /// Returns an iterator that yields references to the data of each vertex attribute.
699 ///
700 /// # Panics
701 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
702 /// this as an error use [`Mesh::try_attributes`]
703 pub fn attributes(
704 &self,
705 ) -> impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)> {
706 self.try_attributes().expect(MESH_EXTRACTED_ERROR)
707 }
708
709 /// Returns an iterator that yields references to the data of each vertex attribute.
710 /// Returns an error if data has been extracted to `RenderWorld`
711 pub fn try_attributes(
712 &self,
713 ) -> Result<impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)>, MeshAccessError>
714 {
715 Ok(self
716 .attributes
717 .as_ref()?
718 .values()
719 .map(|data| (&data.attribute, &data.values)))
720 }
721
722 /// Returns an iterator that yields mutable references to the data of each vertex attribute.
723 ///
724 /// # Panics
725 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
726 /// this as an error use [`Mesh::try_attributes_mut`]
727 pub fn attributes_mut(
728 &mut self,
729 ) -> impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)> {
730 self.try_attributes_mut().expect(MESH_EXTRACTED_ERROR)
731 }
732
733 /// Returns an iterator that yields mutable references to the data of each vertex attribute.
734 ///
735 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
736 pub fn try_attributes_mut(
737 &mut self,
738 ) -> Result<
739 impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)>,
740 MeshAccessError,
741 > {
742 Ok(self
743 .attributes
744 .as_mut()?
745 .values_mut()
746 .map(|data| (&data.attribute, &mut data.values)))
747 }
748
749 /// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the
750 /// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants
751 /// that use triangles.
752 ///
753 /// # Panics
754 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
755 /// this as an error use [`Mesh::try_insert_indices`]
756 #[inline]
757 pub fn insert_indices(&mut self, indices: Indices) {
758 self.indices
759 .replace(Some(indices))
760 .expect(MESH_EXTRACTED_ERROR);
761 }
762
763 /// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the
764 /// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants
765 /// that use triangles.
766 ///
767 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
768 #[inline]
769 pub fn try_insert_indices(&mut self, indices: Indices) -> Result<(), MeshAccessError> {
770 self.indices.replace(Some(indices))?;
771 Ok(())
772 }
773
774 /// Consumes the mesh and returns a mesh with the given vertex indices. They describe how triangles
775 /// are constructed out of the vertex attributes and are therefore only useful for the
776 /// [`PrimitiveTopology`] variants that use triangles.
777 ///
778 /// (Alternatively, you can use [`Mesh::insert_indices`] to mutate an existing mesh in-place)
779 ///
780 /// # Panics
781 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
782 /// this as an error use [`Mesh::try_with_inserted_indices`]
783 #[must_use]
784 #[inline]
785 pub fn with_inserted_indices(mut self, indices: Indices) -> Self {
786 self.insert_indices(indices);
787 self
788 }
789
790 /// Consumes the mesh and returns a mesh with the given vertex indices. They describe how triangles
791 /// are constructed out of the vertex attributes and are therefore only useful for the
792 /// [`PrimitiveTopology`] variants that use triangles.
793 ///
794 /// (Alternatively, you can use [`Mesh::try_insert_indices`] to mutate an existing mesh in-place)
795 ///
796 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
797 #[inline]
798 pub fn try_with_inserted_indices(mut self, indices: Indices) -> Result<Self, MeshAccessError> {
799 self.try_insert_indices(indices)?;
800 Ok(self)
801 }
802
803 /// Retrieves the vertex `indices` of the mesh, returns None if not found.
804 ///
805 /// # Panics
806 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
807 /// this as an error use [`Mesh::try_indices`]
808 #[inline]
809 pub fn indices(&self) -> Option<&Indices> {
810 self.indices.as_ref_option().expect(MESH_EXTRACTED_ERROR)
811 }
812
813 /// Retrieves the vertex `indices` of the mesh.
814 ///
815 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
816 /// if the attribute does not exist.
817 #[inline]
818 pub fn try_indices(&self) -> Result<&Indices, MeshAccessError> {
819 self.indices.as_ref()
820 }
821
822 /// Retrieves the vertex `indices` of the mesh, returns None if not found.
823 ///
824 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
825 #[inline]
826 pub fn try_indices_option(&self) -> Result<Option<&Indices>, MeshAccessError> {
827 self.indices.as_ref_option()
828 }
829
830 /// Retrieves the vertex `indices` of the mesh mutably.
831 #[inline]
832 pub fn indices_mut(&mut self) -> Option<&mut Indices> {
833 self.try_indices_mut_option().expect(MESH_EXTRACTED_ERROR)
834 }
835
836 /// Retrieves the vertex `indices` of the mesh mutably.
837 ///
838 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
839 #[inline]
840 pub fn try_indices_mut(&mut self) -> Result<&mut Indices, MeshAccessError> {
841 self.indices.as_mut()
842 }
843
844 /// Retrieves the vertex `indices` of the mesh mutably.
845 ///
846 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
847 #[inline]
848 pub fn try_indices_mut_option(&mut self) -> Result<Option<&mut Indices>, MeshAccessError> {
849 self.indices.as_mut_option()
850 }
851
852 /// Removes the vertex `indices` from the mesh and returns them.
853 ///
854 /// # Panics
855 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
856 /// this as an error use [`Mesh::try_remove_indices`]
857 #[inline]
858 pub fn remove_indices(&mut self) -> Option<Indices> {
859 self.try_remove_indices().expect(MESH_EXTRACTED_ERROR)
860 }
861
862 /// Removes the vertex `indices` from the mesh and returns them.
863 ///
864 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
865 #[inline]
866 pub fn try_remove_indices(&mut self) -> Result<Option<Indices>, MeshAccessError> {
867 self.indices.replace(None)
868 }
869
870 /// Consumes the mesh and returns a mesh without the vertex `indices` of the mesh.
871 ///
872 /// (Alternatively, you can use [`Mesh::remove_indices`] to mutate an existing mesh in-place)
873 ///
874 /// # Panics
875 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
876 /// this as an error use [`Mesh::try_with_removed_indices`]
877 #[must_use]
878 pub fn with_removed_indices(mut self) -> Self {
879 self.remove_indices();
880 self
881 }
882
883 /// Consumes the mesh and returns a mesh without the vertex `indices` of the mesh.
884 ///
885 /// (Alternatively, you can use [`Mesh::try_remove_indices`] to mutate an existing mesh in-place)
886 ///
887 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
888 pub fn try_with_removed_indices(mut self) -> Result<Self, MeshAccessError> {
889 self.try_remove_indices()?;
890 Ok(self)
891 }
892
893 /// Returns the size of a vertex in bytes.
894 ///
895 /// # Panics
896 /// Panics when the mesh data has already been extracted to `RenderWorld`.
897 pub fn get_vertex_size(&self) -> u64 {
898 self.attributes
899 .as_ref()
900 .expect(MESH_EXTRACTED_ERROR)
901 .values()
902 .map(|data| data.attribute.format.size())
903 .sum()
904 }
905
906 /// Returns the size required for the vertex buffer in bytes.
907 ///
908 /// # Panics
909 /// Panics when the mesh data has already been extracted to `RenderWorld`.
910 pub fn get_vertex_buffer_size(&self) -> usize {
911 let vertex_size = self.get_vertex_size() as usize;
912 let vertex_count = self.count_vertices();
913 vertex_count * vertex_size
914 }
915
916 /// Computes and returns the index data of the mesh as bytes.
917 /// This is used to transform the index data into a GPU friendly format.
918 ///
919 /// # Panics
920 /// Panics when the mesh data has already been extracted to `RenderWorld`.
921 pub fn get_index_buffer_bytes(&self) -> Option<&[u8]> {
922 let mesh_indices = self.indices.as_ref_option().expect(MESH_EXTRACTED_ERROR);
923
924 mesh_indices.as_ref().map(|indices| match &indices {
925 Indices::U16(indices) => cast_slice(&indices[..]),
926 Indices::U32(indices) => cast_slice(&indices[..]),
927 })
928 }
929
930 /// If any morph displacements are present, returns them as a
931 /// [`MorphAttributes`] array.
932 ///
933 /// # Panics
934 /// Panics when the mesh data has already been extracted to the render
935 /// world.
936 #[cfg(feature = "morph")]
937 pub fn get_morph_targets(&self) -> Option<&[MorphAttributes]> {
938 self.morph_targets
939 .as_ref_option()
940 .expect(MESH_EXTRACTED_ERROR)
941 .map(|morph_attributes| &morph_attributes[..])
942 }
943
944 /// Get this `Mesh`'s [`MeshVertexBufferLayout`], used in `SpecializedMeshPipeline`.
945 ///
946 /// # Panics
947 /// Panics when the mesh data has already been extracted to `RenderWorld`.
948 pub fn get_mesh_vertex_buffer_layout(
949 &self,
950 mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,
951 ) -> MeshVertexBufferLayoutRef {
952 let mesh_attributes = self.attributes.as_ref().expect(MESH_EXTRACTED_ERROR);
953
954 let mut attributes = Vec::with_capacity(mesh_attributes.len());
955 let mut attribute_ids = Vec::with_capacity(mesh_attributes.len());
956 let mut accumulated_offset = 0;
957 for (index, data) in mesh_attributes.values().enumerate() {
958 attribute_ids.push(data.attribute.id);
959 attributes.push(VertexAttribute {
960 offset: accumulated_offset,
961 format: data.attribute.format,
962 shader_location: index as u32,
963 });
964 accumulated_offset += data.attribute.format.size();
965 }
966
967 let layout = MeshVertexBufferLayout {
968 layout: VertexBufferLayout {
969 array_stride: accumulated_offset,
970 step_mode: VertexStepMode::Vertex,
971 attributes,
972 },
973 attribute_ids,
974 attribute_compression: self.attribute_compression,
975 };
976 mesh_vertex_buffer_layouts.insert(layout)
977 }
978
979 /// Counts all vertices of the mesh.
980 ///
981 /// If the attributes have different vertex counts, the smallest is returned.
982 ///
983 /// # Panics
984 /// Panics when the mesh data has already been extracted to `RenderWorld`.
985 pub fn count_vertices(&self) -> usize {
986 let mut vertex_count: Option<usize> = None;
987 let mesh_attributes = self.attributes.as_ref().expect(MESH_EXTRACTED_ERROR);
988
989 for (attribute_id, attribute_data) in mesh_attributes {
990 let attribute_len = attribute_data.values.len();
991 if let Some(previous_vertex_count) = vertex_count {
992 if previous_vertex_count != attribute_len {
993 let name = mesh_attributes
994 .get(attribute_id)
995 .map(|data| data.attribute.name.to_string())
996 .unwrap_or_else(|| format!("{attribute_id:?}"));
997
998 warn!("{name} has a different vertex count ({attribute_len}) than other attributes ({previous_vertex_count}) in this mesh, \
999 all attributes will be truncated to match the smallest.");
1000 vertex_count = Some(core::cmp::min(previous_vertex_count, attribute_len));
1001 }
1002 } else {
1003 vertex_count = Some(attribute_len);
1004 }
1005 }
1006
1007 vertex_count.unwrap_or(0)
1008 }
1009
1010 /// Compute the Axis-Aligned Bounding Box of the mesh vertices in model space
1011 ///
1012 /// Returns `None` if `positions` isn't [`VertexAttributeValues::Float32x3`], or if `positions` is empty.
1013 fn compute_aabb(positions: &VertexAttributeValues) -> Option<Aabb3d> {
1014 match positions {
1015 VertexAttributeValues::Float32x3(val) => {
1016 let mut iter = val.iter().map(|a| Vec3A::from_array(*a));
1017 let first = iter.next()?;
1018 let (min, max) = iter.fold((first, first), |(prev_min, prev_max), point| {
1019 (point.min(prev_min), point.max(prev_max))
1020 });
1021 Some(Aabb3d { min, max })
1022 }
1023 _ => None,
1024 }
1025 }
1026
1027 /// Compute the UV range.
1028 ///
1029 /// Returns `None` if `uvs` isn't [`VertexAttributeValues::Float32x2`], or if `uvs` is empty.
1030 fn compute_uv_range(uvs: &VertexAttributeValues) -> Option<Aabb2d> {
1031 match uvs {
1032 VertexAttributeValues::Float32x2(val) => {
1033 let mut iter = val.iter().map(|a| Vec2::from_array(*a));
1034 let first = iter.next()?;
1035 let (min, max) = iter.fold((first, first), |(prev_min, prev_max), point| {
1036 (point.min(prev_min), point.max(prev_max))
1037 });
1038 Some(Aabb2d { min, max })
1039 }
1040 _ => None,
1041 }
1042 }
1043
1044 /// Compress positions and apply [`MeshAttributeCompressionFlags::COMPRESS_POSITION`].
1045 /// See [`MeshAttributeCompressionFlags`] for the details.
1046 ///
1047 /// Return an error if [`Mesh::ATTRIBUTE_POSITION`] is missing or is empty or is not Float32x3.
1048 ///
1049 /// # Panics
1050 /// Panics when the mesh data has already been extracted to `RenderWorld`.
1051 pub fn compress_positions(&mut self) -> Result<&mut Mesh, MeshAttributeCompressionError> {
1052 let mut attr = Mesh::ATTRIBUTE_POSITION;
1053 let Some(values) = self.attribute(attr) else {
1054 return Err(MeshAttributeCompressionError::MissingAttribute(attr.id));
1055 };
1056 if values.is_empty() {
1057 return Err(MeshAttributeCompressionError::EmptyAttribute(attr));
1058 }
1059
1060 let expected = VertexFormat::Snorm16x4;
1061 let Some(aabb) = Self::compute_aabb(values) else {
1062 return Err(
1063 MeshAttributeCompressionError::UnsupportedAttributeForCompression {
1064 attr,
1065 expected,
1066 },
1067 );
1068 };
1069 attr.format = expected;
1070 self.insert_attribute(
1071 attr,
1072 values
1073 .create_compressed_positions(aabb)
1074 .expect("Compression should succeed since `compute_aabb` is checked above"),
1075 );
1076 self.final_aabb = Some(aabb);
1077 self.attribute_compression |= MeshAttributeCompressionFlags::COMPRESS_POSITION;
1078 Ok(self)
1079 }
1080
1081 fn compress_uvs(
1082 &mut self,
1083 mut attr: MeshVertexAttribute,
1084 on_compressed: impl Fn(&mut Mesh, Aabb2d),
1085 ) -> Result<&mut Mesh, MeshAttributeCompressionError> {
1086 let Some(values) = self.attribute(attr) else {
1087 return Err(MeshAttributeCompressionError::MissingAttribute(attr.id));
1088 };
1089 if values.is_empty() {
1090 return Err(MeshAttributeCompressionError::EmptyAttribute(attr));
1091 }
1092
1093 let expected = VertexFormat::Unorm16x2;
1094 let Some(uv_range) = Self::compute_uv_range(values) else {
1095 return Err(
1096 MeshAttributeCompressionError::UnsupportedAttributeForCompression {
1097 attr,
1098 expected,
1099 },
1100 );
1101 };
1102 attr.format = expected;
1103 self.insert_attribute(
1104 attr,
1105 values
1106 .create_compressed_uvs(uv_range)
1107 .expect("Compression should succeed since `compute_uv_range` is checked above"),
1108 );
1109 on_compressed(self, uv_range);
1110 Ok(self)
1111 }
1112
1113 /// Compress UV0 and apply [`MeshAttributeCompressionFlags::COMPRESS_UV0`].
1114 /// See [`MeshAttributeCompressionFlags`] for the details.
1115 ///
1116 /// Return an error if [`Mesh::ATTRIBUTE_UV_0`] is missing or is empty or is not Float32x2.
1117 ///
1118 /// # Panics
1119 /// Panics when the mesh data has already been extracted to `RenderWorld`.
1120 pub fn compress_uv0(&mut self) -> Result<&mut Mesh, MeshAttributeCompressionError> {
1121 self.compress_uvs(Mesh::ATTRIBUTE_UV_0, |mesh, uv_range| {
1122 mesh.final_uv_ranges[0] = Some(uv_range);
1123 mesh.attribute_compression |= MeshAttributeCompressionFlags::COMPRESS_UV0;
1124 })
1125 }
1126
1127 /// Compress UV1 and apply [`MeshAttributeCompressionFlags::COMPRESS_UV1`].
1128 /// See [`MeshAttributeCompressionFlags`] for the details.
1129 ///
1130 /// Return an error if [`Mesh::ATTRIBUTE_UV_1`] is missing or is empty or is not Float32x2.
1131 ///
1132 /// # Panics
1133 /// Panics when the mesh data has already been extracted to `RenderWorld`.
1134 pub fn compress_uv1(&mut self) -> Result<&mut Mesh, MeshAttributeCompressionError> {
1135 self.compress_uvs(Mesh::ATTRIBUTE_UV_1, |mesh, uv_range| {
1136 mesh.final_uv_ranges[1] = Some(uv_range);
1137 mesh.attribute_compression |= MeshAttributeCompressionFlags::COMPRESS_UV1;
1138 })
1139 }
1140
1141 /// Compress normals and apply [`MeshAttributeCompressionFlags::COMPRESS_NORMAL`].
1142 /// See [`MeshAttributeCompressionFlags`] for the details.
1143 ///
1144 /// Return an error if [`Mesh::ATTRIBUTE_NORMAL`] is missing or is not Float32x3.
1145 ///
1146 /// # Panics
1147 /// Panics when the mesh data has already been extracted to `RenderWorld`.
1148 pub fn compress_normals(&mut self) -> Result<&mut Mesh, MeshAttributeCompressionError> {
1149 let mut attr = Mesh::ATTRIBUTE_NORMAL;
1150 let Some(values) = self.attribute(attr) else {
1151 return Err(MeshAttributeCompressionError::MissingAttribute(attr.id));
1152 };
1153
1154 let expected = VertexFormat::Snorm16x2;
1155 let Some(values) = values.create_octahedral_encode_normals() else {
1156 return Err(
1157 MeshAttributeCompressionError::UnsupportedAttributeForCompression {
1158 attr,
1159 expected,
1160 },
1161 );
1162 };
1163 attr.format = expected;
1164 self.insert_attribute(attr, values);
1165 self.attribute_compression |= MeshAttributeCompressionFlags::COMPRESS_NORMAL;
1166 Ok(self)
1167 }
1168
1169 /// Compress tangents and apply [`MeshAttributeCompressionFlags::COMPRESS_TANGENT`].
1170 /// See [`MeshAttributeCompressionFlags`] for the details.
1171 ///
1172 /// Return an error if [`Mesh::ATTRIBUTE_TANGENT`] is missing or is not Float32x4.
1173 ///
1174 /// # Panics
1175 /// Panics when the mesh data has already been extracted to `RenderWorld`.
1176 pub fn compress_tangents(&mut self) -> Result<&mut Mesh, MeshAttributeCompressionError> {
1177 let mut attr = Mesh::ATTRIBUTE_TANGENT;
1178 let Some(values) = self.attribute(attr) else {
1179 return Err(MeshAttributeCompressionError::MissingAttribute(attr.id));
1180 };
1181
1182 let expected = VertexFormat::Snorm16x2;
1183 let Some(values) = values.create_octahedral_encode_tangents() else {
1184 return Err(
1185 MeshAttributeCompressionError::UnsupportedAttributeForCompression {
1186 attr,
1187 expected,
1188 },
1189 );
1190 };
1191 attr.format = expected;
1192 self.insert_attribute(attr, values);
1193 self.attribute_compression |= MeshAttributeCompressionFlags::COMPRESS_TANGENT;
1194 Ok(self)
1195 }
1196
1197 /// Quantize `Float32`, `Float32x2` or `Float32x4` vertex attribute to the format of `quantization`.
1198 ///
1199 /// Return an error if `attr_id` is missing or is not `Float32`, `Float32x2` or `Float32x4`.
1200 ///
1201 /// # Panics
1202 /// Panics when the mesh data has already been extracted to `RenderWorld`.
1203 pub fn quantize_float32_attribute(
1204 &mut self,
1205 attr_id: impl Into<MeshVertexAttributeId>,
1206 quantization: AttributeQuantization,
1207 ) -> Result<&mut Mesh, MeshAttributeCompressionError> {
1208 let attr_id = attr_id.into();
1209 let Some(MeshAttributeData { attribute, values }) =
1210 self.attributes.as_mut().unwrap().get_mut(&attr_id)
1211 else {
1212 return Err(MeshAttributeCompressionError::MissingAttribute(attr_id));
1213 };
1214 let Some(quantized_values) = values.create_quantized_values(quantization) else {
1215 return Err(
1216 MeshAttributeCompressionError::UnsupportedAttributeForQuantizing {
1217 attr: *attribute,
1218 },
1219 );
1220 };
1221 attribute.format = (&quantized_values).into();
1222 *values = quantized_values;
1223 Ok(self)
1224 }
1225
1226 /// Quantize `Float32x4` colors to the format of `quantization` using [`Mesh::quantize_float32_attribute`].
1227 /// [`AttributeQuantization::Unorm8`] is recommended if you don't need higher precision or floating-point range.
1228 pub fn quantize_colors(
1229 &mut self,
1230 quantization: AttributeQuantization,
1231 ) -> Result<&mut Mesh, MeshAttributeCompressionError> {
1232 self.quantize_float32_attribute(Mesh::ATTRIBUTE_COLOR, quantization)
1233 }
1234
1235 /// Quantize `Float32x4` joint weights to the format of `quantization` using [`Mesh::quantize_float32_attribute`].
1236 /// [`AttributeQuantization::Unorm16`] is recommended.
1237 pub fn quantize_joint_weights(
1238 &mut self,
1239 quantization: AttributeQuantization,
1240 ) -> Result<&mut Mesh, MeshAttributeCompressionError> {
1241 self.quantize_float32_attribute(Mesh::ATTRIBUTE_JOINT_WEIGHT, quantization)
1242 }
1243
1244 /// If indices are u32 and vertex count <= 65535, indices will be converted to u16, otherwise this does nothing.
1245 ///
1246 /// # Panics
1247 /// Panics when the mesh data has already been extracted to `RenderWorld`.
1248 pub fn compress_indices(&mut self) -> &mut Mesh {
1249 // Vertex count should be <= 65535 (max index <= 65534), not 65536 because of primitive restart value.
1250 if let Some(Indices::U32(indices)) = self.indices()
1251 && self.count_vertices() <= 65535
1252 {
1253 self.insert_indices(Indices::U16(
1254 indices.iter().map(|idx| *idx as u16).collect(),
1255 ));
1256 }
1257 self
1258 }
1259
1260 /// Compress the mesh with [`MeshCompressionArgs`] using `Mesh::compress_*` and `Mesh::quantize_*` methods.
1261 ///
1262 /// If any compression error except [`MeshAttributeCompressionError::MissingAttribute`] (which is ignored) occurs,
1263 /// returns a `Vec` of the errors.
1264 ///
1265 /// This is best-effort: any failed compression won't prevent other compressions.
1266 ///
1267 /// # Panics
1268 /// Panics when the mesh data has already been extracted to `RenderWorld`.
1269 pub fn compress_mesh(
1270 &mut self,
1271 args: &MeshCompressionArgs,
1272 ) -> Result<(), Vec<MeshAttributeCompressionError>> {
1273 let mut errors = Vec::new();
1274 let mut push_error_ignore_missing_attribute =
1275 |res: Result<&mut Mesh, MeshAttributeCompressionError>| {
1276 match res {
1277 Ok(_) => {}
1278 Err(err) => match err {
1279 MeshAttributeCompressionError::MissingAttribute(_) => {}
1280 e => errors.push(e),
1281 },
1282 };
1283 };
1284
1285 if args
1286 .compress_attributes
1287 .contains(MeshAttributeCompressionFlags::COMPRESS_POSITION)
1288 {
1289 push_error_ignore_missing_attribute(self.compress_positions());
1290 }
1291 if args
1292 .compress_attributes
1293 .contains(MeshAttributeCompressionFlags::COMPRESS_NORMAL)
1294 {
1295 push_error_ignore_missing_attribute(self.compress_normals());
1296 }
1297 if args
1298 .compress_attributes
1299 .contains(MeshAttributeCompressionFlags::COMPRESS_TANGENT)
1300 {
1301 push_error_ignore_missing_attribute(self.compress_tangents());
1302 }
1303 if args
1304 .compress_attributes
1305 .contains(MeshAttributeCompressionFlags::COMPRESS_UV0)
1306 {
1307 push_error_ignore_missing_attribute(self.compress_uv0());
1308 }
1309 if args
1310 .compress_attributes
1311 .contains(MeshAttributeCompressionFlags::COMPRESS_UV1)
1312 {
1313 push_error_ignore_missing_attribute(self.compress_uv1());
1314 }
1315 for (attr, quantization) in args.quantize_attributes.iter().copied() {
1316 push_error_ignore_missing_attribute(
1317 self.quantize_float32_attribute(attr, quantization),
1318 );
1319 }
1320 if args.compress_indices {
1321 self.compress_indices();
1322 }
1323 if errors.is_empty() {
1324 Ok(())
1325 } else {
1326 Err(errors)
1327 }
1328 }
1329
1330 /// Same as [`Self::compress_mesh`], except this is passed by value instead of by mutable reference.
1331 #[expect(
1332 clippy::result_large_err,
1333 reason = "Returning the passed `Mesh` in `Err`"
1334 )]
1335 pub fn compressed_mesh(
1336 mut self,
1337 args: &MeshCompressionArgs,
1338 ) -> Result<Mesh, (Mesh, Vec<MeshAttributeCompressionError>)> {
1339 let result = self.compress_mesh(args);
1340 match result {
1341 Ok(_) => Ok(self),
1342 Err(err) => Err((self, err)),
1343 }
1344 }
1345
1346 /// Computes and returns the vertex data of the mesh as bytes.
1347 /// Therefore the attributes are located in the order of their [`MeshVertexAttribute::id`].
1348 /// This is used to transform the vertex data into a GPU friendly format.
1349 ///
1350 /// If the vertex attributes have different lengths, they are all truncated to
1351 /// the length of the smallest.
1352 ///
1353 /// This is a convenience method which allocates a Vec.
1354 /// Prefer pre-allocating and using [`Mesh::write_packed_vertex_buffer_data`] when possible.
1355 ///
1356 /// # Panics
1357 /// Panics when the mesh data has already been extracted to `RenderWorld`.
1358 pub fn create_packed_vertex_buffer_data(&self) -> Vec<u8> {
1359 let mut attributes_interleaved_buffer = vec![0; self.get_vertex_buffer_size()];
1360 self.write_packed_vertex_buffer_data(WriteOnly::from_mut(
1361 &mut attributes_interleaved_buffer,
1362 ));
1363 attributes_interleaved_buffer
1364 }
1365
1366 /// Computes and write the vertex data of the mesh into a mutable byte slice.
1367 /// The attributes are located in the order of their [`MeshVertexAttribute::id`].
1368 /// This is used to transform the vertex data into a GPU friendly format.
1369 ///
1370 /// If the vertex attributes have different lengths, they are all truncated to
1371 /// the length of the smallest.
1372 ///
1373 /// # Panics
1374 /// Panics when the mesh data has already been extracted to `RenderWorld`.
1375 pub fn write_packed_vertex_buffer_data(&self, mut slice: WriteOnly<'_, [u8]>) {
1376 let mesh_attributes = self.attributes.as_ref().expect(MESH_EXTRACTED_ERROR);
1377
1378 let vertex_size = self.get_vertex_size() as usize;
1379 let vertex_count = self.count_vertices();
1380 // bundle into interleaved buffers
1381 let mut attribute_offset = 0;
1382 for attribute_data in mesh_attributes.values() {
1383 let attribute_size = attribute_data.attribute.format.size() as usize;
1384 let attributes_bytes = attribute_data.values.get_bytes();
1385 for (vertex_index, attribute_bytes) in attributes_bytes
1386 .chunks_exact(attribute_size)
1387 .take(vertex_count)
1388 .enumerate()
1389 {
1390 let offset = vertex_index * vertex_size + attribute_offset;
1391 slice
1392 .slice(offset..offset + attribute_size)
1393 .copy_from_slice(attribute_bytes);
1394 }
1395
1396 attribute_offset += attribute_size;
1397 }
1398 }
1399
1400 /// Duplicates the vertex attributes so that no vertices are shared.
1401 ///
1402 /// This can dramatically increase the vertex count, so make sure this is what you want.
1403 /// Does nothing if no [Indices] are set.
1404 ///
1405 /// # Panics
1406 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1407 /// this as an error use [`Mesh::try_duplicate_vertices`]
1408 pub fn duplicate_vertices(&mut self) {
1409 self.try_duplicate_vertices().expect(MESH_EXTRACTED_ERROR);
1410 }
1411
1412 /// Duplicates the vertex attributes so that no vertices are shared.
1413 ///
1414 /// This can dramatically increase the vertex count, so make sure this is what you want.
1415 /// Does nothing if no [Indices] are set.
1416 ///
1417 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
1418 pub fn try_duplicate_vertices(&mut self) -> Result<(), MeshAccessError> {
1419 fn duplicate<T: Copy>(values: &[T], indices: impl Iterator<Item = usize>) -> Vec<T> {
1420 indices.map(|i| values[i]).collect()
1421 }
1422
1423 let Some(indices) = self.indices.replace(None)? else {
1424 return Ok(());
1425 };
1426
1427 let mesh_attributes = self.attributes.as_mut()?;
1428
1429 for attributes in mesh_attributes.values_mut() {
1430 let indices = indices.iter();
1431 #[expect(
1432 clippy::match_same_arms,
1433 reason = "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."
1434 )]
1435 match &mut attributes.values {
1436 VertexAttributeValues::Float32(vec) => *vec = duplicate(vec, indices),
1437 VertexAttributeValues::Sint32(vec) => *vec = duplicate(vec, indices),
1438 VertexAttributeValues::Uint32(vec) => *vec = duplicate(vec, indices),
1439 VertexAttributeValues::Float32x2(vec) => *vec = duplicate(vec, indices),
1440 VertexAttributeValues::Sint32x2(vec) => *vec = duplicate(vec, indices),
1441 VertexAttributeValues::Uint32x2(vec) => *vec = duplicate(vec, indices),
1442 VertexAttributeValues::Float32x3(vec) => *vec = duplicate(vec, indices),
1443 VertexAttributeValues::Sint32x3(vec) => *vec = duplicate(vec, indices),
1444 VertexAttributeValues::Uint32x3(vec) => *vec = duplicate(vec, indices),
1445 VertexAttributeValues::Sint32x4(vec) => *vec = duplicate(vec, indices),
1446 VertexAttributeValues::Uint32x4(vec) => *vec = duplicate(vec, indices),
1447 VertexAttributeValues::Float32x4(vec) => *vec = duplicate(vec, indices),
1448 VertexAttributeValues::Sint16x2(vec) => *vec = duplicate(vec, indices),
1449 VertexAttributeValues::Snorm16x2(vec) => *vec = duplicate(vec, indices),
1450 VertexAttributeValues::Uint16x2(vec) => *vec = duplicate(vec, indices),
1451 VertexAttributeValues::Unorm16x2(vec) => *vec = duplicate(vec, indices),
1452 VertexAttributeValues::Sint16x4(vec) => *vec = duplicate(vec, indices),
1453 VertexAttributeValues::Snorm16x4(vec) => *vec = duplicate(vec, indices),
1454 VertexAttributeValues::Uint16x4(vec) => *vec = duplicate(vec, indices),
1455 VertexAttributeValues::Unorm16x4(vec) => *vec = duplicate(vec, indices),
1456 VertexAttributeValues::Sint8x2(vec) => *vec = duplicate(vec, indices),
1457 VertexAttributeValues::Snorm8x2(vec) => *vec = duplicate(vec, indices),
1458 VertexAttributeValues::Uint8x2(vec) => *vec = duplicate(vec, indices),
1459 VertexAttributeValues::Unorm8x2(vec) => *vec = duplicate(vec, indices),
1460 VertexAttributeValues::Sint8x4(vec) => *vec = duplicate(vec, indices),
1461 VertexAttributeValues::Snorm8x4(vec) => *vec = duplicate(vec, indices),
1462 VertexAttributeValues::Uint8x4(vec) => *vec = duplicate(vec, indices),
1463 VertexAttributeValues::Unorm8x4(vec) => *vec = duplicate(vec, indices),
1464 VertexAttributeValues::Uint8(vec) => *vec = duplicate(vec, indices),
1465 VertexAttributeValues::Sint8(vec) => *vec = duplicate(vec, indices),
1466 VertexAttributeValues::Unorm8(vec) => *vec = duplicate(vec, indices),
1467 VertexAttributeValues::Snorm8(vec) => *vec = duplicate(vec, indices),
1468 VertexAttributeValues::Uint16(vec) => *vec = duplicate(vec, indices),
1469 VertexAttributeValues::Sint16(vec) => *vec = duplicate(vec, indices),
1470 VertexAttributeValues::Unorm16(vec) => *vec = duplicate(vec, indices),
1471 VertexAttributeValues::Snorm16(vec) => *vec = duplicate(vec, indices),
1472 VertexAttributeValues::Float16(vec) => *vec = duplicate(vec, indices),
1473 VertexAttributeValues::Float16x2(vec) => *vec = duplicate(vec, indices),
1474 VertexAttributeValues::Float16x4(vec) => *vec = duplicate(vec, indices),
1475 VertexAttributeValues::Float64(vec) => *vec = duplicate(vec, indices),
1476 VertexAttributeValues::Float64x2(vec) => *vec = duplicate(vec, indices),
1477 VertexAttributeValues::Float64x3(vec) => *vec = duplicate(vec, indices),
1478 VertexAttributeValues::Float64x4(vec) => *vec = duplicate(vec, indices),
1479 VertexAttributeValues::Unorm10_10_10_2(vec) => *vec = duplicate(vec, indices),
1480 VertexAttributeValues::Unorm8x4Bgra(vec) => *vec = duplicate(vec, indices),
1481 }
1482 }
1483
1484 Ok(())
1485 }
1486
1487 /// Consumes the mesh and returns a mesh with no shared vertices.
1488 ///
1489 /// This can dramatically increase the vertex count, so make sure this is what you want.
1490 /// Does nothing if no [`Indices`] are set.
1491 ///
1492 /// (Alternatively, you can use [`Mesh::duplicate_vertices`] to mutate an existing mesh in-place)
1493 ///
1494 /// # Panics
1495 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1496 /// this as an error use [`Mesh::try_with_duplicated_vertices`]
1497 #[must_use]
1498 pub fn with_duplicated_vertices(mut self) -> Self {
1499 self.duplicate_vertices();
1500 self
1501 }
1502
1503 /// Consumes the mesh and returns a mesh with no shared vertices.
1504 ///
1505 /// This can dramatically increase the vertex count, so make sure this is what you want.
1506 /// Does nothing if no [`Indices`] are set.
1507 ///
1508 /// (Alternatively, you can use [`Mesh::try_duplicate_vertices`] to mutate an existing mesh in-place)
1509 ///
1510 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
1511 pub fn try_with_duplicated_vertices(mut self) -> Result<Self, MeshAccessError> {
1512 self.try_duplicate_vertices()?;
1513 Ok(self)
1514 }
1515
1516 /// Remove duplicate vertices and create the index pointing to the unique vertices.
1517 ///
1518 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
1519 /// Returns an error if the mesh already has [`Indices`] set, even if there
1520 /// are duplicate vertices. If deduplication is needed with indices already set,
1521 /// consider calling [`Mesh::duplicate_vertices`] and then this function.
1522 pub fn merge_duplicate_vertices(&mut self) -> Result<(), MeshMergeDuplicateVerticesError> {
1523 match self.try_indices() {
1524 Ok(_) => return Err(MeshMergeDuplicateVerticesError::IndicesAlreadySet),
1525 Err(err) => match err {
1526 MeshAccessError::ExtractedToRenderWorld => return Err(err.into()),
1527 MeshAccessError::NotFound => (),
1528 },
1529 }
1530
1531 #[derive(Copy, Clone)]
1532 struct VertexRef<'a> {
1533 mesh_attributes: &'a BTreeMap<MeshVertexAttributeId, MeshAttributeData>,
1534 i: usize,
1535 }
1536 impl<'a> VertexRef<'a> {
1537 fn push_to(&self, target: &mut BTreeMap<MeshVertexAttributeId, MeshAttributeData>) {
1538 for (key, this_attribute_data) in self.mesh_attributes.iter() {
1539 let target_attribute_data = target.get_mut(key).unwrap(); // ok to unwrap, all keys added to new_attributes below
1540 target_attribute_data
1541 .values
1542 .push_from(&this_attribute_data.values, self.i);
1543 }
1544 }
1545 }
1546 impl<'a> PartialEq for VertexRef<'a> {
1547 fn eq(&self, other: &Self) -> bool {
1548 assert!(ptr::eq(self.mesh_attributes, other.mesh_attributes));
1549 for values in self.mesh_attributes.values() {
1550 if values.values.get_bytes_at(self.i) != values.values.get_bytes_at(other.i) {
1551 return false;
1552 }
1553 }
1554 true
1555 }
1556 }
1557 impl<'a> Eq for VertexRef<'a> {}
1558 impl<'a> Hash for VertexRef<'a> {
1559 fn hash<H: Hasher>(&self, state: &mut H) {
1560 for values in self.mesh_attributes.values() {
1561 values.values.get_bytes_at(self.i).hash(state);
1562 }
1563 }
1564 }
1565
1566 let old_attributes = self.attributes.as_ref()?;
1567
1568 let mut new_attributes: BTreeMap<MeshVertexAttributeId, MeshAttributeData> = self
1569 .attributes
1570 .as_ref()?
1571 .iter()
1572 .map(|(k, v)| {
1573 (
1574 *k,
1575 MeshAttributeData {
1576 attribute: v.attribute,
1577 values: VertexAttributeValues::new(VertexFormat::from(&v.values)),
1578 },
1579 )
1580 })
1581 .collect();
1582
1583 let mut vertex_to_new_index: HashMap<VertexRef, u32> = HashMap::new();
1584 let mut indices = Vec::with_capacity(self.count_vertices());
1585 for i in 0..self.count_vertices() {
1586 let len: u32 = vertex_to_new_index
1587 .len()
1588 .try_into()
1589 .expect("The number of vertices exceeds u32::MAX");
1590 let vertex_ref = VertexRef {
1591 mesh_attributes: old_attributes,
1592 i,
1593 };
1594 let j = match vertex_to_new_index.entry(vertex_ref) {
1595 hash_map::Entry::Occupied(e) => *e.get(),
1596 hash_map::Entry::Vacant(e) => {
1597 e.insert(len);
1598 vertex_ref.push_to(&mut new_attributes);
1599 len
1600 }
1601 };
1602 indices.push(j);
1603 }
1604 drop(vertex_to_new_index);
1605
1606 for v in new_attributes.values_mut() {
1607 v.values.shrink_to_fit();
1608 }
1609
1610 self.attributes = MeshExtractableData::Data(new_attributes);
1611 self.indices = MeshExtractableData::Data(Indices::U32(indices));
1612
1613 Ok(())
1614 }
1615
1616 /// Consumes the mesh and returns a mesh with merged vertices.
1617 ///
1618 /// (Alternatively, you can use [`Mesh::merge_duplicate_vertices`] to mutate an existing mesh in-place)
1619 ///
1620 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
1621 /// Returns an error if the mesh already has [`Indices`] set, even if there
1622 /// are duplicate vertices. If deduplication is needed with indices already set,
1623 /// consider calling [`Mesh::duplicate_vertices`] and then this function.
1624 pub fn with_merge_duplicate_vertices(
1625 mut self,
1626 ) -> Result<Self, MeshMergeDuplicateVerticesError> {
1627 self.merge_duplicate_vertices()?;
1628 Ok(self)
1629 }
1630
1631 /// Inverts the winding of the indices such that all counter-clockwise triangles are now
1632 /// clockwise and vice versa.
1633 /// For lines, their start and end indices are flipped.
1634 ///
1635 /// Does nothing if no [`Indices`] are set.
1636 /// If this operation succeeded, an [`Ok`] result is returned.
1637 pub fn invert_winding(&mut self) -> Result<(), MeshWindingInvertError> {
1638 fn invert<I>(
1639 indices: &mut [I],
1640 topology: PrimitiveTopology,
1641 ) -> Result<(), MeshWindingInvertError> {
1642 match topology {
1643 PrimitiveTopology::TriangleList => {
1644 let (chunks, []) = indices.as_chunks_mut() else {
1645 // Early return if the index count doesn't match
1646 return Err(MeshWindingInvertError::AbruptIndicesEnd);
1647 };
1648
1649 for [_, b, c] in chunks {
1650 core::mem::swap(b, c);
1651 }
1652 Ok(())
1653 }
1654 PrimitiveTopology::LineList => {
1655 // Early return if the index count doesn't match
1656 if !indices.len().is_multiple_of(2) {
1657 return Err(MeshWindingInvertError::AbruptIndicesEnd);
1658 }
1659 indices.reverse();
1660 Ok(())
1661 }
1662 PrimitiveTopology::TriangleStrip | PrimitiveTopology::LineStrip => {
1663 indices.reverse();
1664 Ok(())
1665 }
1666 _ => Err(MeshWindingInvertError::WrongTopology),
1667 }
1668 }
1669
1670 let mesh_indices = self.indices.as_mut_option()?;
1671
1672 match mesh_indices {
1673 Some(Indices::U16(vec)) => invert(vec, self.primitive_topology),
1674 Some(Indices::U32(vec)) => invert(vec, self.primitive_topology),
1675 None => Ok(()),
1676 }
1677 }
1678
1679 /// Consumes the mesh and returns a mesh with inverted winding of the indices such
1680 /// that all counter-clockwise triangles are now clockwise and vice versa.
1681 ///
1682 /// Does nothing if no [`Indices`] are set.
1683 pub fn with_inverted_winding(mut self) -> Result<Self, MeshWindingInvertError> {
1684 self.invert_winding().map(|_| self)
1685 }
1686
1687 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
1688 /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
1689 /// normals.
1690 ///
1691 /// # Panics
1692 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1693 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].=
1694 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1695 /// this as an error use [`Mesh::try_compute_normals`]
1696 pub fn compute_normals(&mut self) {
1697 self.try_compute_normals().expect(MESH_EXTRACTED_ERROR);
1698 }
1699
1700 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
1701 /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
1702 /// normals.
1703 ///
1704 /// # Panics
1705 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1706 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].=
1707 pub fn try_compute_normals(&mut self) -> Result<(), MeshAccessError> {
1708 assert!(
1709 matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
1710 "`compute_normals` can only work on `TriangleList`s"
1711 );
1712 if self.try_indices_option()?.is_none() {
1713 self.try_compute_flat_normals()
1714 } else {
1715 self.try_compute_smooth_normals()
1716 }
1717 }
1718
1719 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
1720 ///
1721 /// # Panics
1722 /// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1723 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1724 /// Consider calling [`Mesh::duplicate_vertices`] or exporting your mesh with normal
1725 /// attributes.
1726 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1727 /// this as an error use [`Mesh::try_compute_flat_normals`]
1728 ///
1729 /// FIXME: This should handle more cases since this is called as a part of gltf
1730 /// mesh loading where we can't really blame users for loading meshes that might
1731 /// not conform to the limitations here!
1732 pub fn compute_flat_normals(&mut self) {
1733 self.try_compute_flat_normals().expect(MESH_EXTRACTED_ERROR);
1734 }
1735
1736 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
1737 ///
1738 /// # Panics
1739 /// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1740 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1741 /// Consider calling [`Mesh::duplicate_vertices`] or exporting your mesh with normal
1742 /// attributes.
1743 ///
1744 /// FIXME: This should handle more cases since this is called as a part of gltf
1745 /// mesh loading where we can't really blame users for loading meshes that might
1746 /// not conform to the limitations here!
1747 pub fn try_compute_flat_normals(&mut self) -> Result<(), MeshAccessError> {
1748 assert!(
1749 self.try_indices_option()?.is_none(),
1750 "`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`."
1751 );
1752 assert!(
1753 matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
1754 "`compute_flat_normals` can only work on `TriangleList`s"
1755 );
1756
1757 let positions = self
1758 .try_attribute(Mesh::ATTRIBUTE_POSITION)?
1759 .as_float3()
1760 .expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");
1761
1762 let normals: Vec<_> = positions
1763 .as_chunks()
1764 .0
1765 .iter()
1766 .flat_map(|&[a, b, c]| [triangle_normal(a, b, c); 3])
1767 .collect();
1768
1769 self.try_insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1770 }
1771
1772 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1773 /// vertices.
1774 ///
1775 /// This method weights normals by the angles of the corners of connected triangles, thus
1776 /// eliminating triangle area and count as factors in the final normal. This does make it
1777 /// somewhat slower than [`Mesh::compute_area_weighted_normals`] which does not need to
1778 /// greedily normalize each triangle's normal or calculate corner angles.
1779 ///
1780 /// If you would rather have the computed normals be weighted by triangle area, see
1781 /// [`Mesh::compute_area_weighted_normals`] instead. If you need to weight them in some other
1782 /// way, see [`Mesh::compute_custom_smooth_normals`].
1783 ///
1784 /// # Panics
1785 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1786 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1787 /// Panics if the mesh does not have indices defined.
1788 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1789 /// this as an error use [`Mesh::try_compute_smooth_normals`]
1790 pub fn compute_smooth_normals(&mut self) {
1791 self.try_compute_smooth_normals()
1792 .expect(MESH_EXTRACTED_ERROR);
1793 }
1794
1795 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1796 /// vertices.
1797 ///
1798 /// This method weights normals by the angles of the corners of connected triangles, thus
1799 /// eliminating triangle area and count as factors in the final normal. This does make it
1800 /// somewhat slower than [`Mesh::compute_area_weighted_normals`] which does not need to
1801 /// greedily normalize each triangle's normal or calculate corner angles.
1802 ///
1803 /// If you would rather have the computed normals be weighted by triangle area, see
1804 /// [`Mesh::compute_area_weighted_normals`] instead. If you need to weight them in some other
1805 /// way, see [`Mesh::compute_custom_smooth_normals`].
1806 ///
1807 /// # Panics
1808 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1809 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1810 /// Panics if the mesh does not have indices defined.
1811 pub fn try_compute_smooth_normals(&mut self) -> Result<(), MeshAccessError> {
1812 self.try_compute_custom_smooth_normals(|[a, b, c], positions, normals| {
1813 let pa = Vec3::from(positions[a]);
1814 let pb = Vec3::from(positions[b]);
1815 let pc = Vec3::from(positions[c]);
1816
1817 let ab = pb - pa;
1818 let ba = pa - pb;
1819 let bc = pc - pb;
1820 let cb = pb - pc;
1821 let ca = pa - pc;
1822 let ac = pc - pa;
1823
1824 const EPS: f32 = f32::EPSILON;
1825 let weight_a = if ab.length_squared() * ac.length_squared() > EPS {
1826 ab.angle_between(ac)
1827 } else {
1828 0.0
1829 };
1830 let weight_b = if ba.length_squared() * bc.length_squared() > EPS {
1831 ba.angle_between(bc)
1832 } else {
1833 0.0
1834 };
1835 let weight_c = if ca.length_squared() * cb.length_squared() > EPS {
1836 ca.angle_between(cb)
1837 } else {
1838 0.0
1839 };
1840
1841 let normal = Vec3::from(triangle_normal(positions[a], positions[b], positions[c]));
1842
1843 normals[a] += normal * weight_a;
1844 normals[b] += normal * weight_b;
1845 normals[c] += normal * weight_c;
1846 })
1847 }
1848
1849 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1850 /// vertices.
1851 ///
1852 /// This method weights normals by the area of each triangle containing the vertex. Thus,
1853 /// larger triangles will skew the normals of their vertices towards their own normal more
1854 /// than smaller triangles will.
1855 ///
1856 /// This method is actually somewhat faster than [`Mesh::compute_smooth_normals`] because an
1857 /// intermediate result of triangle normal calculation is already scaled by the triangle's area.
1858 ///
1859 /// If you would rather have the computed normals be influenced only by the angles of connected
1860 /// edges, see [`Mesh::compute_smooth_normals`] instead. If you need to weight them in some
1861 /// other way, see [`Mesh::compute_custom_smooth_normals`].
1862 ///
1863 /// # Panics
1864 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1865 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1866 /// Panics if the mesh does not have indices defined.
1867 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1868 /// this as an error use [`Mesh::try_compute_area_weighted_normals`]
1869 pub fn compute_area_weighted_normals(&mut self) {
1870 self.try_compute_area_weighted_normals()
1871 .expect(MESH_EXTRACTED_ERROR);
1872 }
1873
1874 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1875 /// vertices.
1876 ///
1877 /// This method weights normals by the area of each triangle containing the vertex. Thus,
1878 /// larger triangles will skew the normals of their vertices towards their own normal more
1879 /// than smaller triangles will.
1880 ///
1881 /// This method is actually somewhat faster than [`Mesh::compute_smooth_normals`] because an
1882 /// intermediate result of triangle normal calculation is already scaled by the triangle's area.
1883 ///
1884 /// If you would rather have the computed normals be influenced only by the angles of connected
1885 /// edges, see [`Mesh::compute_smooth_normals`] instead. If you need to weight them in some
1886 /// other way, see [`Mesh::compute_custom_smooth_normals`].
1887 ///
1888 /// # Panics
1889 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1890 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1891 /// Panics if the mesh does not have indices defined.
1892 pub fn try_compute_area_weighted_normals(&mut self) -> Result<(), MeshAccessError> {
1893 self.try_compute_custom_smooth_normals(|[a, b, c], positions, normals| {
1894 let normal = Vec3::from(triangle_area_normal(
1895 positions[a],
1896 positions[b],
1897 positions[c],
1898 ));
1899 [a, b, c].into_iter().for_each(|pos| {
1900 normals[pos] += normal;
1901 });
1902 })
1903 }
1904
1905 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1906 /// vertices.
1907 ///
1908 /// This method allows you to customize how normals are weighted via the `per_triangle` parameter,
1909 /// which must be a function or closure that accepts 3 parameters:
1910 /// - The indices of the three vertices of the triangle as a `[usize; 3]`.
1911 /// - A reference to the values of the [`Mesh::ATTRIBUTE_POSITION`] of the mesh (`&[[f32; 3]]`).
1912 /// - A mutable reference to the sums of all normals so far.
1913 ///
1914 /// See also the standard methods included in Bevy for calculating smooth normals:
1915 /// - [`Mesh::compute_smooth_normals`]
1916 /// - [`Mesh::compute_area_weighted_normals`]
1917 ///
1918 /// An example that would weight each connected triangle's normal equally, thus skewing normals
1919 /// towards the planes divided into the most triangles:
1920 /// ```
1921 /// # use bevy_asset::RenderAssetUsages;
1922 /// # use bevy_mesh::{Mesh, PrimitiveTopology, Meshable, MeshBuilder};
1923 /// # use bevy_math::Vec3;
1924 /// # use bevy_shape::Cuboid;
1925 /// # let mut mesh = Cuboid::default().mesh().build();
1926 /// mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| {
1927 /// let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c]));
1928 /// for idx in [a, b, c] {
1929 /// normals[idx] += normal;
1930 /// }
1931 /// });
1932 /// ```
1933 ///
1934 /// # Panics
1935 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1936 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1937 /// Panics if the mesh does not have indices defined.
1938 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1939 /// this as an error use [`Mesh::try_compute_custom_smooth_normals`]
1940 //
1941 // FIXME: This should handle more cases since this is called as a part of gltf
1942 // mesh loading where we can't really blame users for loading meshes that might
1943 // not conform to the limitations here!
1944 //
1945 // When fixed, also update "Panics" sections of
1946 // - [Mesh::compute_smooth_normals]
1947 // - [Mesh::with_computed_smooth_normals]
1948 // - [Mesh::compute_area_weighted_normals]
1949 // - [Mesh::with_computed_area_weighted_normals]
1950 pub fn compute_custom_smooth_normals(
1951 &mut self,
1952 per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]),
1953 ) {
1954 self.try_compute_custom_smooth_normals(per_triangle)
1955 .expect(MESH_EXTRACTED_ERROR);
1956 }
1957
1958 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1959 /// vertices.
1960 ///
1961 /// This method allows you to customize how normals are weighted via the `per_triangle` parameter,
1962 /// which must be a function or closure that accepts 3 parameters:
1963 /// - The indices of the three vertices of the triangle as a `[usize; 3]`.
1964 /// - A reference to the values of the [`Mesh::ATTRIBUTE_POSITION`] of the mesh (`&[[f32; 3]]`).
1965 /// - A mutable reference to the sums of all normals so far.
1966 ///
1967 /// See also the standard methods included in Bevy for calculating smooth normals:
1968 /// - [`Mesh::compute_smooth_normals`]
1969 /// - [`Mesh::compute_area_weighted_normals`]
1970 ///
1971 /// An example that would weight each connected triangle's normal equally, thus skewing normals
1972 /// towards the planes divided into the most triangles:
1973 /// ```
1974 /// # use bevy_asset::RenderAssetUsages;
1975 /// # use bevy_mesh::{Mesh, PrimitiveTopology, Meshable, MeshBuilder};
1976 /// # use bevy_math::Vec3;
1977 /// # use bevy_shape::Cuboid;
1978 /// # let mut mesh = Cuboid::default().mesh().build();
1979 /// mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| {
1980 /// let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c]));
1981 /// for idx in [a, b, c] {
1982 /// normals[idx] += normal;
1983 /// }
1984 /// });
1985 /// ```
1986 ///
1987 /// # Panics
1988 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1989 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1990 /// Panics if the mesh does not have indices defined.
1991 //
1992 // FIXME: This should handle more cases since this is called as a part of gltf
1993 // mesh loading where we can't really blame users for loading meshes that might
1994 // not conform to the limitations here!
1995 //
1996 // When fixed, also update "Panics" sections of
1997 // - [Mesh::compute_smooth_normals]
1998 // - [Mesh::with_computed_smooth_normals]
1999 // - [Mesh::compute_area_weighted_normals]
2000 // - [Mesh::with_computed_area_weighted_normals]
2001 pub fn try_compute_custom_smooth_normals(
2002 &mut self,
2003 mut per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]),
2004 ) -> Result<(), MeshAccessError> {
2005 assert!(
2006 matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
2007 "smooth normals can only be computed on `TriangleList`s"
2008 );
2009 assert!(
2010 self.try_indices_option()?.is_some(),
2011 "smooth normals can only be computed on indexed meshes"
2012 );
2013
2014 let positions = self
2015 .try_attribute(Mesh::ATTRIBUTE_POSITION)?
2016 .as_float3()
2017 .expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");
2018
2019 let mut normals = vec![Vec3::ZERO; positions.len()];
2020
2021 match self.try_indices()? {
2022 Indices::U16(vec) => vec.as_chunks().0.iter().for_each(|&chunk| {
2023 per_triangle(chunk.map(|i| i as usize), positions, &mut normals);
2024 }),
2025 Indices::U32(vec) => vec.as_chunks().0.iter().for_each(|&chunk| {
2026 per_triangle(chunk.map(|i| i as usize), positions, &mut normals);
2027 }),
2028 }
2029
2030 for normal in &mut normals {
2031 *normal = normal.try_normalize().unwrap_or(Vec3::ZERO);
2032 }
2033
2034 self.try_insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
2035 }
2036
2037 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
2038 /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
2039 /// normals.
2040 ///
2041 /// (Alternatively, you can use [`Mesh::compute_normals`] to mutate an existing mesh in-place)
2042 ///
2043 /// # Panics
2044 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
2045 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
2046 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2047 /// this as an error use [`Mesh::try_with_computed_normals`]
2048 #[must_use]
2049 pub fn with_computed_normals(self) -> Self {
2050 self.try_with_computed_normals()
2051 .expect(MESH_EXTRACTED_ERROR)
2052 }
2053
2054 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
2055 /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
2056 /// normals.
2057 ///
2058 /// (Alternatively, you can use [`Mesh::compute_normals`] to mutate an existing mesh in-place)
2059 ///
2060 /// # Panics
2061 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
2062 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
2063 pub fn try_with_computed_normals(mut self) -> Result<Self, MeshAccessError> {
2064 self.try_compute_normals()?;
2065 Ok(self)
2066 }
2067
2068 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
2069 ///
2070 /// (Alternatively, you can use [`Mesh::compute_flat_normals`] to mutate an existing mesh in-place)
2071 ///
2072 /// # Panics
2073 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
2074 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
2075 /// Panics if the mesh has indices defined
2076 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2077 /// this as an error use [`Mesh::try_with_computed_flat_normals`]
2078 pub fn with_computed_flat_normals(mut self) -> Self {
2079 self.compute_flat_normals();
2080 self
2081 }
2082
2083 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
2084 ///
2085 /// (Alternatively, you can use [`Mesh::compute_flat_normals`] to mutate an existing mesh in-place)
2086 ///
2087 /// # Panics
2088 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
2089 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
2090 /// Panics if the mesh has indices defined
2091 pub fn try_with_computed_flat_normals(mut self) -> Result<Self, MeshAccessError> {
2092 self.try_compute_flat_normals()?;
2093 Ok(self)
2094 }
2095
2096 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
2097 ///
2098 /// (Alternatively, you can use [`Mesh::compute_smooth_normals`] to mutate an existing mesh in-place)
2099 ///
2100 /// This method weights normals by the angles of triangle corners connected to each vertex. If
2101 /// you would rather have the computed normals be weighted by triangle area, see
2102 /// [`Mesh::with_computed_area_weighted_normals`] instead.
2103 ///
2104 /// # Panics
2105 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
2106 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
2107 /// Panics if the mesh does not have indices defined.
2108 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2109 /// this as an error use [`Mesh::try_with_computed_smooth_normals`]
2110 pub fn with_computed_smooth_normals(mut self) -> Self {
2111 self.compute_smooth_normals();
2112 self
2113 }
2114 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
2115 ///
2116 /// (Alternatively, you can use [`Mesh::compute_smooth_normals`] to mutate an existing mesh in-place)
2117 ///
2118 /// This method weights normals by the angles of triangle corners connected to each vertex. If
2119 /// you would rather have the computed normals be weighted by triangle area, see
2120 /// [`Mesh::with_computed_area_weighted_normals`] instead.
2121 ///
2122 /// # Panics
2123 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
2124 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
2125 /// Panics if the mesh does not have indices defined.
2126 pub fn try_with_computed_smooth_normals(mut self) -> Result<Self, MeshAccessError> {
2127 self.try_compute_smooth_normals()?;
2128 Ok(self)
2129 }
2130
2131 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
2132 ///
2133 /// (Alternatively, you can use [`Mesh::compute_area_weighted_normals`] to mutate an existing mesh in-place)
2134 ///
2135 /// This method weights normals by the area of each triangle containing the vertex. Thus,
2136 /// larger triangles will skew the normals of their vertices towards their own normal more
2137 /// than smaller triangles will. If you would rather have the computed normals be influenced
2138 /// only by the angles of connected edges, see [`Mesh::with_computed_smooth_normals`] instead.
2139 ///
2140 /// # Panics
2141 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
2142 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
2143 /// Panics if the mesh does not have indices defined.
2144 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2145 /// this as an error use [`Mesh::try_with_computed_area_weighted_normals`]
2146 pub fn with_computed_area_weighted_normals(mut self) -> Self {
2147 self.compute_area_weighted_normals();
2148 self
2149 }
2150
2151 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
2152 ///
2153 /// (Alternatively, you can use [`Mesh::compute_area_weighted_normals`] to mutate an existing mesh in-place)
2154 ///
2155 /// This method weights normals by the area of each triangle containing the vertex. Thus,
2156 /// larger triangles will skew the normals of their vertices towards their own normal more
2157 /// than smaller triangles will. If you would rather have the computed normals be influenced
2158 /// only by the angles of connected edges, see [`Mesh::with_computed_smooth_normals`] instead.
2159 ///
2160 /// # Panics
2161 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
2162 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
2163 /// Panics if the mesh does not have indices defined.
2164 pub fn try_with_computed_area_weighted_normals(mut self) -> Result<Self, MeshAccessError> {
2165 self.try_compute_area_weighted_normals()?;
2166 Ok(self)
2167 }
2168
2169 /// Generate tangents for the mesh using the `mikktspace` algorithm.
2170 ///
2171 /// Sets the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful.
2172 /// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set.
2173 #[cfg(feature = "bevy_mikktspace")]
2174 pub fn generate_tangents(&mut self) -> Result<(), super::GenerateTangentsError> {
2175 let tangents = super::generate_tangents_for_mesh(self)?;
2176 self.try_insert_attribute(Mesh::ATTRIBUTE_TANGENT, tangents)?;
2177 Ok(())
2178 }
2179
2180 /// Consumes the mesh and returns a mesh with tangents generated using the `mikktspace` algorithm.
2181 ///
2182 /// The resulting mesh will have the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful.
2183 ///
2184 /// (Alternatively, you can use [`Mesh::generate_tangents`] to mutate an existing mesh in-place)
2185 ///
2186 /// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set.
2187 #[cfg(feature = "bevy_mikktspace")]
2188 pub fn with_generated_tangents(mut self) -> Result<Mesh, super::GenerateTangentsError> {
2189 self.generate_tangents()?;
2190 Ok(self)
2191 }
2192
2193 /// Merges the [`Mesh`] data of `other` with `self`. The attributes and indices of `other` will be appended to `self`.
2194 ///
2195 /// Note that attributes of `other` that don't exist on `self` will be ignored.
2196 ///
2197 /// `Aabb` of entities with modified mesh are not updated automatically.
2198 ///
2199 /// # Errors
2200 ///
2201 /// If any of the following conditions are not met, this function errors:
2202 /// * All of the vertex attributes that have the same attribute id, must also
2203 /// have the same attribute type.
2204 /// For example two attributes with the same id, but where one is a
2205 /// [`VertexAttributeValues::Float32`] and the other is a
2206 /// [`VertexAttributeValues::Float32x3`], would be invalid.
2207 /// * Both meshes must have the same primitive topology.
2208 pub fn merge(&mut self, other: &Mesh) -> Result<(), MeshMergeError> {
2209 use VertexAttributeValues::*;
2210
2211 // Check if the meshes `primitive_topology` field is the same,
2212 // as if that is not the case, the resulting mesh could (and most likely would)
2213 // be invalid.
2214 if self.primitive_topology != other.primitive_topology {
2215 return Err(MeshMergeError::IncompatiblePrimitiveTopology {
2216 self_primitive_topology: self.primitive_topology,
2217 other_primitive_topology: other.primitive_topology,
2218 });
2219 }
2220
2221 // The indices of `other` should start after the last vertex of `self`.
2222 let index_offset = self.count_vertices();
2223
2224 // Extend attributes of `self` with attributes of `other`.
2225 for (attribute, values) in self.try_attributes_mut()? {
2226 if let Some(other_values) = other.try_attribute_option(attribute.id)? {
2227 #[expect(
2228 clippy::match_same_arms,
2229 reason = "Although the bindings on some match arms may have different types, each variant has different semantics; thus it's not guaranteed that they will use the same type forever."
2230 )]
2231 match (values, other_values) {
2232 (Float32(vec1), Float32(vec2)) => vec1.extend(vec2),
2233 (Sint32(vec1), Sint32(vec2)) => vec1.extend(vec2),
2234 (Uint32(vec1), Uint32(vec2)) => vec1.extend(vec2),
2235 (Float32x2(vec1), Float32x2(vec2)) => vec1.extend(vec2),
2236 (Sint32x2(vec1), Sint32x2(vec2)) => vec1.extend(vec2),
2237 (Uint32x2(vec1), Uint32x2(vec2)) => vec1.extend(vec2),
2238 (Float32x3(vec1), Float32x3(vec2)) => vec1.extend(vec2),
2239 (Sint32x3(vec1), Sint32x3(vec2)) => vec1.extend(vec2),
2240 (Uint32x3(vec1), Uint32x3(vec2)) => vec1.extend(vec2),
2241 (Sint32x4(vec1), Sint32x4(vec2)) => vec1.extend(vec2),
2242 (Uint32x4(vec1), Uint32x4(vec2)) => vec1.extend(vec2),
2243 (Float32x4(vec1), Float32x4(vec2)) => vec1.extend(vec2),
2244 (Sint16x2(vec1), Sint16x2(vec2)) => vec1.extend(vec2),
2245 (Snorm16x2(vec1), Snorm16x2(vec2)) => vec1.extend(vec2),
2246 (Uint16x2(vec1), Uint16x2(vec2)) => vec1.extend(vec2),
2247 (Unorm16x2(vec1), Unorm16x2(vec2)) => vec1.extend(vec2),
2248 (Sint16x4(vec1), Sint16x4(vec2)) => vec1.extend(vec2),
2249 (Snorm16x4(vec1), Snorm16x4(vec2)) => vec1.extend(vec2),
2250 (Uint16x4(vec1), Uint16x4(vec2)) => vec1.extend(vec2),
2251 (Unorm16x4(vec1), Unorm16x4(vec2)) => vec1.extend(vec2),
2252 (Sint8x2(vec1), Sint8x2(vec2)) => vec1.extend(vec2),
2253 (Snorm8x2(vec1), Snorm8x2(vec2)) => vec1.extend(vec2),
2254 (Uint8x2(vec1), Uint8x2(vec2)) => vec1.extend(vec2),
2255 (Unorm8x2(vec1), Unorm8x2(vec2)) => vec1.extend(vec2),
2256 (Sint8x4(vec1), Sint8x4(vec2)) => vec1.extend(vec2),
2257 (Snorm8x4(vec1), Snorm8x4(vec2)) => vec1.extend(vec2),
2258 (Uint8x4(vec1), Uint8x4(vec2)) => vec1.extend(vec2),
2259 (Unorm8x4(vec1), Unorm8x4(vec2)) => vec1.extend(vec2),
2260 _ => {
2261 return Err(MeshMergeError::IncompatibleVertexAttributes {
2262 self_attribute: *attribute,
2263 other_attribute: other
2264 .try_attribute_data(attribute.id)?
2265 .map(|data| data.attribute),
2266 })
2267 }
2268 }
2269 }
2270 }
2271
2272 // Extend indices of `self` with indices of `other`.
2273 if let (Some(indices), Some(other_indices)) =
2274 (self.try_indices_mut_option()?, other.try_indices_option()?)
2275 {
2276 indices.extend(other_indices.iter().map(|i| (i + index_offset) as u32));
2277 }
2278 Ok(())
2279 }
2280
2281 /// Transforms the vertex positions, normals, and tangents of the mesh by the given [`Transform`].
2282 ///
2283 /// `Aabb` of entities with modified mesh are not updated automatically.
2284 ///
2285 /// # Panics
2286 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2287 /// this as an error use [`Mesh::try_transformed_by`]
2288 pub fn transformed_by(mut self, transform: Transform) -> Self {
2289 self.transform_by(transform);
2290 self
2291 }
2292
2293 /// Transforms the vertex positions, normals, and tangents of the mesh by the given [`Transform`].
2294 ///
2295 /// `Aabb` of entities with modified mesh are not updated automatically.
2296 pub fn try_transformed_by(mut self, transform: Transform) -> Result<Self, MeshAccessError> {
2297 self.try_transform_by(transform)?;
2298 Ok(self)
2299 }
2300
2301 /// Transforms the vertex positions, normals, and tangents of the mesh in place by the given [`Transform`].
2302 ///
2303 /// `Aabb` of entities with modified mesh are not updated automatically.
2304 ///
2305 /// # Panics
2306 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2307 /// this as an error use [`Mesh::try_transform_by`]
2308 pub fn transform_by(&mut self, transform: Transform) {
2309 self.try_transform_by(transform)
2310 .expect(MESH_EXTRACTED_ERROR);
2311 }
2312
2313 /// Transforms the vertex positions, normals, and tangents of the mesh in place by the given [`Transform`].
2314 ///
2315 /// `Aabb` of entities with modified mesh are not updated automatically.
2316 pub fn try_transform_by(&mut self, transform: Transform) -> Result<(), MeshAccessError> {
2317 // Needed when transforming normals and tangents
2318 let scale_recip = 1. / transform.scale;
2319 debug_assert!(
2320 transform.scale.yzx() * transform.scale.zxy() != Vec3::ZERO,
2321 "mesh transform scale cannot be zero on more than one axis"
2322 );
2323
2324 if let Some(VertexAttributeValues::Float32x3(positions)) =
2325 self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?
2326 {
2327 // Apply scale, rotation, and translation to vertex positions
2328 positions
2329 .iter_mut()
2330 .for_each(|pos| *pos = transform.transform_point(Vec3::from_slice(pos)).to_array());
2331 }
2332
2333 // No need to transform normals or tangents if rotation is near identity and scale is uniform
2334 if transform.rotation.is_near_identity()
2335 && transform.scale.x == transform.scale.y
2336 && transform.scale.y == transform.scale.z
2337 {
2338 return Ok(());
2339 }
2340
2341 if let Some(VertexAttributeValues::Float32x3(normals)) =
2342 self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)?
2343 {
2344 // Transform normals, taking into account non-uniform scaling and rotation
2345 normals.iter_mut().for_each(|normal| {
2346 *normal = (transform.rotation
2347 * scale_normal(Vec3::from_array(*normal), scale_recip))
2348 .to_array();
2349 });
2350 }
2351
2352 if let Some(VertexAttributeValues::Float32x4(tangents)) =
2353 self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)?
2354 {
2355 // Transform tangents, taking into account non-uniform scaling and rotation
2356 tangents.iter_mut().for_each(|tangent| {
2357 let handedness = tangent[3];
2358 let scaled_tangent = Vec3::from_slice(tangent) * transform.scale;
2359 *tangent = (transform.rotation * scaled_tangent.normalize_or_zero())
2360 .extend(handedness)
2361 .to_array();
2362 });
2363 }
2364
2365 Ok(())
2366 }
2367
2368 /// Translates the vertex positions of the mesh by the given [`Vec3`].
2369 ///
2370 /// `Aabb` of entities with modified mesh are not updated automatically.
2371 ///
2372 /// # Panics
2373 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2374 /// this as an error use [`Mesh::try_translated_by`]
2375 pub fn translated_by(mut self, translation: Vec3) -> Self {
2376 self.translate_by(translation);
2377 self
2378 }
2379
2380 /// Translates the vertex positions of the mesh by the given [`Vec3`].
2381 ///
2382 /// `Aabb` of entities with modified mesh are not updated automatically.
2383 pub fn try_translated_by(mut self, translation: Vec3) -> Result<Self, MeshAccessError> {
2384 self.try_translate_by(translation)?;
2385 Ok(self)
2386 }
2387
2388 /// Translates the vertex positions of the mesh in place by the given [`Vec3`].
2389 ///
2390 /// `Aabb` of entities with modified mesh are not updated automatically.
2391 ///
2392 /// # Panics
2393 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2394 /// this as an error use [`Mesh::try_translate_by`]
2395 pub fn translate_by(&mut self, translation: Vec3) {
2396 self.try_translate_by(translation)
2397 .expect(MESH_EXTRACTED_ERROR);
2398 }
2399
2400 /// Translates the vertex positions of the mesh in place by the given [`Vec3`].
2401 ///
2402 /// `Aabb` of entities with modified mesh are not updated automatically.
2403 pub fn try_translate_by(&mut self, translation: Vec3) -> Result<(), MeshAccessError> {
2404 if translation == Vec3::ZERO {
2405 return Ok(());
2406 }
2407
2408 if let Some(VertexAttributeValues::Float32x3(positions)) =
2409 self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?
2410 {
2411 // Apply translation to vertex positions
2412 positions
2413 .iter_mut()
2414 .for_each(|pos| *pos = (Vec3::from_slice(pos) + translation).to_array());
2415 }
2416
2417 Ok(())
2418 }
2419
2420 /// Rotates the vertex positions, normals, and tangents of the mesh by the given [`Quat`].
2421 ///
2422 /// `Aabb` of entities with modified mesh are not updated automatically.
2423 ///
2424 /// # Panics
2425 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2426 /// this as an error use [`Mesh::try_rotated_by`]
2427 pub fn rotated_by(mut self, rotation: Quat) -> Self {
2428 self.try_rotate_by(rotation).expect(MESH_EXTRACTED_ERROR);
2429 self
2430 }
2431
2432 /// Rotates the vertex positions, normals, and tangents of the mesh by the given [`Quat`].
2433 ///
2434 /// `Aabb` of entities with modified mesh are not updated automatically.
2435 pub fn try_rotated_by(mut self, rotation: Quat) -> Result<Self, MeshAccessError> {
2436 self.try_rotate_by(rotation)?;
2437 Ok(self)
2438 }
2439
2440 /// Rotates the vertex positions, normals, and tangents of the mesh in place by the given [`Quat`].
2441 ///
2442 /// `Aabb` of entities with modified mesh are not updated automatically.
2443 ///
2444 /// # Panics
2445 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2446 /// this as an error use [`Mesh::try_rotate_by`]
2447 pub fn rotate_by(&mut self, rotation: Quat) {
2448 self.try_rotate_by(rotation).expect(MESH_EXTRACTED_ERROR);
2449 }
2450
2451 /// Rotates the vertex positions, normals, and tangents of the mesh in place by the given [`Quat`].
2452 ///
2453 /// `Aabb` of entities with modified mesh are not updated automatically.
2454 pub fn try_rotate_by(&mut self, rotation: Quat) -> Result<(), MeshAccessError> {
2455 if let Some(VertexAttributeValues::Float32x3(positions)) =
2456 self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?
2457 {
2458 // Apply rotation to vertex positions
2459 positions
2460 .iter_mut()
2461 .for_each(|pos| *pos = (rotation * Vec3::from_slice(pos)).to_array());
2462 }
2463
2464 // No need to transform normals or tangents if rotation is near identity
2465 if rotation.is_near_identity() {
2466 return Ok(());
2467 }
2468
2469 if let Some(VertexAttributeValues::Float32x3(normals)) =
2470 self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)?
2471 {
2472 // Transform normals
2473 normals.iter_mut().for_each(|normal| {
2474 *normal = (rotation * Vec3::from_slice(normal).normalize_or_zero()).to_array();
2475 });
2476 }
2477
2478 if let Some(VertexAttributeValues::Float32x4(tangents)) =
2479 self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)?
2480 {
2481 // Transform tangents
2482 tangents.iter_mut().for_each(|tangent| {
2483 let handedness = tangent[3];
2484 *tangent = (rotation * Vec3::from_slice(tangent).normalize_or_zero())
2485 .extend(handedness)
2486 .to_array();
2487 });
2488 }
2489
2490 Ok(())
2491 }
2492
2493 /// Scales the vertex positions, normals, and tangents of the mesh by the given [`Vec3`].
2494 ///
2495 /// `Aabb` of entities with modified mesh are not updated automatically.
2496 ///
2497 /// # Panics
2498 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2499 /// this as an error use [`Mesh::try_scaled_by`]
2500 pub fn scaled_by(mut self, scale: Vec3) -> Self {
2501 self.scale_by(scale);
2502 self
2503 }
2504
2505 /// Scales the vertex positions, normals, and tangents of the mesh by the given [`Vec3`].
2506 ///
2507 /// `Aabb` of entities with modified mesh are not updated automatically.
2508 pub fn try_scaled_by(mut self, scale: Vec3) -> Result<Self, MeshAccessError> {
2509 self.try_scale_by(scale)?;
2510 Ok(self)
2511 }
2512
2513 /// Scales the vertex positions, normals, and tangents of the mesh in place by the given [`Vec3`].
2514 ///
2515 /// `Aabb` of entities with modified mesh are not updated automatically.
2516 ///
2517 /// # Panics
2518 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2519 /// this as an error use [`Mesh::try_scale_by`]
2520 pub fn scale_by(&mut self, scale: Vec3) {
2521 self.try_scale_by(scale).expect(MESH_EXTRACTED_ERROR);
2522 }
2523
2524 /// Scales the vertex positions, normals, and tangents of the mesh in place by the given [`Vec3`].
2525 ///
2526 /// `Aabb` of entities with modified mesh are not updated automatically.
2527 pub fn try_scale_by(&mut self, scale: Vec3) -> Result<(), MeshAccessError> {
2528 // Needed when transforming normals and tangents
2529 let scale_recip = 1. / scale;
2530 debug_assert!(
2531 scale.yzx() * scale.zxy() != Vec3::ZERO,
2532 "mesh transform scale cannot be zero on more than one axis"
2533 );
2534
2535 if let Some(VertexAttributeValues::Float32x3(positions)) =
2536 self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?
2537 {
2538 // Apply scale to vertex positions
2539 positions
2540 .iter_mut()
2541 .for_each(|pos| *pos = (scale * Vec3::from_slice(pos)).to_array());
2542 }
2543
2544 // No need to transform normals or tangents if scale is uniform
2545 if scale.x == scale.y && scale.y == scale.z {
2546 return Ok(());
2547 }
2548
2549 if let Some(VertexAttributeValues::Float32x3(normals)) =
2550 self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)?
2551 {
2552 // Transform normals, taking into account non-uniform scaling
2553 normals.iter_mut().for_each(|normal| {
2554 *normal = scale_normal(Vec3::from_array(*normal), scale_recip).to_array();
2555 });
2556 }
2557
2558 if let Some(VertexAttributeValues::Float32x4(tangents)) =
2559 self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)?
2560 {
2561 // Transform tangents, taking into account non-uniform scaling
2562 tangents.iter_mut().for_each(|tangent| {
2563 let handedness = tangent[3];
2564 let scaled_tangent = Vec3::from_slice(tangent) * scale;
2565 *tangent = scaled_tangent
2566 .normalize_or_zero()
2567 .extend(handedness)
2568 .to_array();
2569 });
2570 }
2571
2572 Ok(())
2573 }
2574
2575 /// Normalize joint weights so they sum to 1.
2576 ///
2577 /// # Panics
2578 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2579 /// this as an error use [`Mesh::try_normalize_joint_weights`]
2580 pub fn normalize_joint_weights(&mut self) {
2581 self.try_normalize_joint_weights()
2582 .expect(MESH_EXTRACTED_ERROR);
2583 }
2584
2585 /// Normalize joint weights so they sum to 1.
2586 pub fn try_normalize_joint_weights(&mut self) -> Result<(), MeshAccessError> {
2587 if let Some(VertexAttributeValues::Float32x4(joints)) =
2588 self.try_attribute_mut_option(Self::ATTRIBUTE_JOINT_WEIGHT)?
2589 {
2590 for weights in joints.iter_mut() {
2591 // force negative weights to zero
2592 weights.iter_mut().for_each(|w| *w = w.max(0.0));
2593
2594 let sum: f32 = weights.iter().sum();
2595 if sum == 0.0 {
2596 // all-zero weights are invalid
2597 weights[0] = 1.0;
2598 } else {
2599 let recip = sum.recip();
2600 for weight in weights.iter_mut() {
2601 *weight *= recip;
2602 }
2603 }
2604 }
2605 }
2606
2607 Ok(())
2608 }
2609
2610 /// Get a list of this Mesh's [triangles] as an iterator if possible.
2611 ///
2612 /// Returns an error if any of the following conditions are met (see [`MeshTrianglesError`]):
2613 /// * The Mesh's [primitive topology] is not `TriangleList` or `TriangleStrip`.
2614 /// * The Mesh is missing position or index data.
2615 /// * The Mesh's position data has the wrong format (not `Float32x3`).
2616 ///
2617 /// [primitive topology]: PrimitiveTopology
2618 /// [triangles]: Triangle3d
2619 pub fn triangles(&self) -> Result<impl Iterator<Item = Triangle3d> + '_, MeshTrianglesError> {
2620 fn indices_to_triangle<T: TryInto<usize> + Copy>(
2621 vertices: &[[f32; 3]],
2622 indices: &[T; 3],
2623 ) -> Option<Triangle3d> {
2624 let vert0 = Vec3::from(*vertices.get(indices[0].try_into().ok()?)?);
2625 let vert1 = Vec3::from(*vertices.get(indices[1].try_into().ok()?)?);
2626 let vert2 = Vec3::from(*vertices.get(indices[2].try_into().ok()?)?);
2627 Some(Triangle3d {
2628 vertices: [vert0, vert1, vert2],
2629 })
2630 }
2631
2632 let position_data = self.try_attribute(Mesh::ATTRIBUTE_POSITION)?;
2633
2634 let Some(vertices) = position_data.as_float3() else {
2635 return Err(MeshTrianglesError::PositionsFormat);
2636 };
2637
2638 let indices = self.try_indices()?;
2639
2640 match self.primitive_topology {
2641 PrimitiveTopology::TriangleList => {
2642 // When indices reference out-of-bounds vertex data, the triangle is omitted.
2643 // This implicitly truncates the indices to a multiple of 3.
2644 let iterator = match indices {
2645 Indices::U16(vec) => FourIterators::First(
2646 vec.as_chunks()
2647 .0
2648 .iter()
2649 .flat_map(|indices| indices_to_triangle(vertices, indices)),
2650 ),
2651 Indices::U32(vec) => FourIterators::Second(
2652 vec.as_chunks()
2653 .0
2654 .iter()
2655 .flat_map(|indices| indices_to_triangle(vertices, indices)),
2656 ),
2657 };
2658
2659 Ok(iterator)
2660 }
2661 PrimitiveTopology::TriangleStrip => {
2662 // When indices reference out-of-bounds vertex data, the triangle is omitted.
2663 // If there aren't enough indices to make a triangle, then an empty vector will be
2664 // returned.
2665 let iterator = match indices {
2666 Indices::U16(vec) => {
2667 FourIterators::Third(vec.array_windows().enumerate().flat_map(
2668 |(i, indices @ &[idx0, idx1, idx2])| {
2669 if i % 2 == 0 {
2670 indices_to_triangle(vertices, indices)
2671 } else {
2672 indices_to_triangle(vertices, &[idx1, idx0, idx2])
2673 }
2674 },
2675 ))
2676 }
2677 Indices::U32(vec) => {
2678 FourIterators::Fourth(vec.array_windows().enumerate().flat_map(
2679 |(i, indices @ &[idx0, idx1, idx2])| {
2680 if i % 2 == 0 {
2681 indices_to_triangle(vertices, indices)
2682 } else {
2683 indices_to_triangle(vertices, &[idx1, idx0, idx2])
2684 }
2685 },
2686 ))
2687 }
2688 };
2689
2690 Ok(iterator)
2691 }
2692 _ => Err(MeshTrianglesError::WrongTopology),
2693 }
2694 }
2695
2696 /// Extracts the mesh vertex, index and morph target data for GPU upload.
2697 /// This function is called internally in render world extraction, it is
2698 /// unlikely to be useful outside of that context.
2699 ///
2700 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
2701 pub fn take_gpu_data(&mut self) -> Result<Self, MeshAccessError> {
2702 let attributes = self.attributes.extract()?;
2703 let indices = self.indices.extract()?;
2704 #[cfg(feature = "morph")]
2705 let morph_targets = self.morph_targets.extract()?;
2706 #[cfg(feature = "morph")]
2707 let morph_target_names = self.morph_target_names.extract()?;
2708
2709 // store the aabb extents as they cannot be computed after extraction
2710 if let Some(MeshAttributeData { values, .. }) = attributes
2711 .as_ref_option()?
2712 .and_then(|attrs| attrs.get(&Self::ATTRIBUTE_POSITION.id))
2713 {
2714 self.final_aabb = Self::compute_aabb(values);
2715 }
2716 Ok(Self {
2717 attributes,
2718 indices,
2719 #[cfg(feature = "morph")]
2720 morph_targets,
2721 #[cfg(feature = "morph")]
2722 morph_target_names,
2723 ..self.clone()
2724 })
2725 }
2726
2727 /// Get this mesh's [`SkinnedMeshBounds`].
2728 pub fn skinned_mesh_bounds(&self) -> Option<&SkinnedMeshBounds> {
2729 self.skinned_mesh_bounds.as_ref()
2730 }
2731
2732 /// Set this mesh's [`SkinnedMeshBounds`].
2733 pub fn set_skinned_mesh_bounds(&mut self, skinned_mesh_bounds: Option<SkinnedMeshBounds>) {
2734 self.skinned_mesh_bounds = skinned_mesh_bounds;
2735 }
2736
2737 /// Consumes the mesh and returns a mesh with the given [`SkinnedMeshBounds`].
2738 pub fn with_skinned_mesh_bounds(
2739 mut self,
2740 skinned_mesh_bounds: Option<SkinnedMeshBounds>,
2741 ) -> Self {
2742 self.set_skinned_mesh_bounds(skinned_mesh_bounds);
2743 self
2744 }
2745
2746 /// Generate [`SkinnedMeshBounds`] for this mesh.
2747 pub fn generate_skinned_mesh_bounds(&mut self) -> Result<(), SkinnedMeshBoundsError> {
2748 self.skinned_mesh_bounds = Some(SkinnedMeshBounds::from_mesh(self)?);
2749 Ok(())
2750 }
2751
2752 /// Consumes the mesh and returns a mesh with generated [`SkinnedMeshBounds`].
2753 pub fn with_generated_skinned_mesh_bounds(mut self) -> Result<Self, SkinnedMeshBoundsError> {
2754 self.generate_skinned_mesh_bounds()?;
2755 Ok(self)
2756 }
2757
2758 /// Get multiple vertex attributes of the [`Mesh`] mutably.
2759 ///
2760 /// The result, if attributes are available and not extracted into the
2761 /// render world, will have the same length as `attributes`, and missing
2762 /// or duplicated ids will be returned as [`None`].
2763 ///
2764 /// ## Example
2765 /// ```
2766 /// # use bevy_asset::RenderAssetUsages;
2767 /// # use bevy_math::{Vec3, Vec4};
2768 /// # use bevy_mesh::{Mesh, PrimitiveTopology};
2769 /// # let mut mesh = Mesh::new(PrimitiveTopology::PointList, RenderAssetUsages::default())
2770 /// # .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vec![Vec3::new(0., 0., 0.)])
2771 /// # .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![Vec3::new(1., 1., 1.)])
2772 /// # .with_inserted_attribute(Mesh::ATTRIBUTE_COLOR, vec![Vec4::new(2., 2., 2., 2.)]);
2773 /// let Ok([pos1, pos2, pos3, normal, color]) = mesh.try_disjoint_attributes_mut([
2774 /// &Mesh::ATTRIBUTE_POSITION.id,
2775 /// &Mesh::ATTRIBUTE_POSITION.id,
2776 /// &Mesh::ATTRIBUTE_POSITION.id,
2777 /// &Mesh::ATTRIBUTE_NORMAL.id,
2778 /// &Mesh::ATTRIBUTE_COLOR.id,
2779 /// ]) else {
2780 /// // Data may have been extracted to the render world
2781 /// return;
2782 /// };
2783 /// assert_eq!(pos1.unwrap().0, &Mesh::ATTRIBUTE_POSITION);
2784 /// assert!(pos2.is_none());
2785 /// assert!(pos3.is_none());
2786 /// assert_eq!(normal.unwrap().0, &Mesh::ATTRIBUTE_NORMAL);
2787 /// assert_eq!(color.unwrap().0, &Mesh::ATTRIBUTE_COLOR);
2788 /// ```
2789 pub fn try_disjoint_attributes_mut<'a, const N: usize>(
2790 &'a mut self,
2791 mesh_vertex_attribute_ids: [&MeshVertexAttributeId; N],
2792 ) -> Result<
2793 [Option<(&'a MeshVertexAttribute, &'a mut VertexAttributeValues)>; N],
2794 MeshAccessError,
2795 > {
2796 let attrs = self.attributes.as_mut()?;
2797 let mut mut_attrs = attrs.iter_mut();
2798 let mut attrs: [_; N] = std::array::from_fn(|_| {
2799 for (attr_id, mut_attr) in mut_attrs.by_ref() {
2800 if mesh_vertex_attribute_ids.contains(&attr_id) {
2801 return Some((&mut_attr.attribute, &mut mut_attr.values));
2802 }
2803 }
2804 None
2805 });
2806
2807 let mut attrs_slice = attrs.as_mut_slice();
2808 for mesh_vertex_attribute_id in mesh_vertex_attribute_ids {
2809 // If current key is different from top of attributes slice,
2810 // swap is needed
2811 if attrs_slice[0]
2812 .as_ref()
2813 .filter(|(attr, _)| &attr.id != mesh_vertex_attribute_id)
2814 .is_some()
2815 {
2816 if let Some(pos) = attrs_slice.iter().position(|attr| {
2817 attr.as_ref()
2818 .filter(|(attr, _)| &attr.id == mesh_vertex_attribute_id)
2819 .is_some()
2820 }) {
2821 // Swap for the attribute with the correct id
2822 attrs_slice.swap(0, pos);
2823 } else if attrs_slice[0].is_some() {
2824 // If attribute with id does not exist and current attribute is not `None`
2825 // swap for a `None`
2826 if let Some(pos) = attrs_slice[1..].iter().position(Option::is_none) {
2827 attrs_slice.swap(0, pos + 1);
2828 } else {
2829 // attrs_slice will always have a least the same length as ks
2830 unreachable!("Attribute list must have enough `None`s to be able to sort.");
2831 }
2832 }
2833 }
2834 attrs_slice = &mut attrs_slice[1..];
2835 }
2836
2837 Ok(attrs)
2838 }
2839}
2840
2841#[cfg(feature = "morph")]
2842impl Mesh {
2843 /// Whether this mesh has morph targets.
2844 ///
2845 /// # Panics
2846 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2847 /// this as an error use [`Mesh::try_has_morph_targets`]
2848 pub fn has_morph_targets(&self) -> bool {
2849 self.try_has_morph_targets().expect(MESH_EXTRACTED_ERROR)
2850 }
2851
2852 /// Whether this mesh has morph targets.
2853 pub fn try_has_morph_targets(&self) -> Result<bool, MeshAccessError> {
2854 Ok(self.morph_targets.as_ref_option()?.is_some())
2855 }
2856
2857 /// Set the [morph target] displacements for this mesh.
2858 ///
2859 /// [morph target]: https://en.wikipedia.org/wiki/Morph_target_animation
2860 ///
2861 /// # Panics
2862 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2863 /// this as an error use [`Mesh::try_set_morph_targets`]
2864 #[cfg(feature = "morph")]
2865 pub fn set_morph_targets(&mut self, morph_targets: Vec<MorphAttributes>) {
2866 self.try_set_morph_targets(morph_targets)
2867 .expect(MESH_EXTRACTED_ERROR);
2868 }
2869
2870 /// Set the [morph target] displacements for this mesh.
2871 ///
2872 /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
2873 #[cfg(feature = "morph")]
2874 pub fn try_set_morph_targets(
2875 &mut self,
2876 morph_targets: Vec<MorphAttributes>,
2877 ) -> Result<(), MeshAccessError> {
2878 self.morph_targets.replace(Some(morph_targets))?;
2879 Ok(())
2880 }
2881
2882 /// Retrieve the morph target displacements for this mesh, or None if there
2883 /// are no morph targets.
2884 ///
2885 /// # Panics
2886 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2887 /// this as an error use [`Mesh::try_morph_targets`]
2888 #[cfg(feature = "morph")]
2889 pub fn morph_targets(&self) -> Option<&Vec<MorphAttributes>> {
2890 self.morph_targets
2891 .as_ref_option()
2892 .expect(MESH_EXTRACTED_ERROR)
2893 }
2894
2895 /// Retrieve the morph displacements for this mesh, or None if there are no
2896 /// morph targets.
2897 ///
2898 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
2899 /// if the morph targets do not exist.
2900 #[cfg(feature = "morph")]
2901 pub fn try_morph_targets(&self) -> Result<&Vec<MorphAttributes>, MeshAccessError> {
2902 self.morph_targets.as_ref()
2903 }
2904
2905 /// Consumes the mesh and returns a mesh with the given [morph target]
2906 /// displacements.
2907 ///
2908 /// (Alternatively, you can use [`Mesh::set_morph_targets`] to mutate an existing mesh in-place)
2909 ///
2910 /// [morph target]: https://en.wikipedia.org/wiki/Morph_target_animation
2911 ///
2912 /// # Panics
2913 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2914 /// this as an error use [`Mesh::try_with_morph_targets`]
2915 #[must_use]
2916 #[cfg(feature = "morph")]
2917 pub fn with_morph_targets(mut self, morph_targets: Vec<MorphAttributes>) -> Self {
2918 self.set_morph_targets(morph_targets);
2919 self
2920 }
2921
2922 /// Consumes the mesh and returns a mesh with the given [morph targets].
2923 ///
2924 /// (Alternatively, you can use [`Mesh::set_morph_targets`] to mutate an existing mesh in-place)
2925 ///
2926 /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
2927 ///
2928 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
2929 #[cfg(feature = "morph")]
2930 pub fn try_with_morph_targets(
2931 mut self,
2932 morph_targets: Vec<MorphAttributes>,
2933 ) -> Result<Self, MeshAccessError> {
2934 self.try_set_morph_targets(morph_targets)?;
2935 Ok(self)
2936 }
2937
2938 /// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`.
2939 ///
2940 /// # Panics
2941 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2942 /// this as an error use [`Mesh::try_set_morph_target_names`]
2943 pub fn set_morph_target_names(&mut self, names: Vec<String>) {
2944 self.try_set_morph_target_names(names)
2945 .expect(MESH_EXTRACTED_ERROR);
2946 }
2947
2948 /// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`.
2949 ///
2950 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
2951 pub fn try_set_morph_target_names(
2952 &mut self,
2953 names: Vec<String>,
2954 ) -> Result<(), MeshAccessError> {
2955 self.morph_target_names.replace(Some(names))?;
2956 Ok(())
2957 }
2958
2959 /// Consumes the mesh and returns a mesh with morph target names.
2960 /// Names should correspond to the order of the morph targets in `set_morph_targets`.
2961 ///
2962 /// (Alternatively, you can use [`Mesh::set_morph_target_names`] to mutate an existing mesh in-place)
2963 ///
2964 /// # Panics
2965 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2966 /// this as an error use [`Mesh::try_set_morph_target_names`]
2967 #[must_use]
2968 pub fn with_morph_target_names(self, names: Vec<String>) -> Self {
2969 self.try_with_morph_target_names(names)
2970 .expect(MESH_EXTRACTED_ERROR)
2971 }
2972
2973 /// Consumes the mesh and returns a mesh with morph target names.
2974 /// Names should correspond to the order of the morph targets in `set_morph_targets`.
2975 ///
2976 /// (Alternatively, you can use [`Mesh::set_morph_target_names`] to mutate an existing mesh in-place)
2977 ///
2978 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
2979 pub fn try_with_morph_target_names(
2980 mut self,
2981 names: Vec<String>,
2982 ) -> Result<Self, MeshAccessError> {
2983 self.try_set_morph_target_names(names)?;
2984 Ok(self)
2985 }
2986
2987 /// Gets a list of all morph target names, if they exist.
2988 ///
2989 /// # Panics
2990 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2991 /// this as an error use [`Mesh::try_morph_target_names`]
2992 pub fn morph_target_names(&self) -> Option<&[String]> {
2993 self.try_morph_target_names().expect(MESH_EXTRACTED_ERROR)
2994 }
2995
2996 /// Gets a list of all morph target names, if they exist.
2997 ///
2998 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
2999 /// if the morph targets do not exist.
3000 pub fn try_morph_target_names(&self) -> Result<Option<&[String]>, MeshAccessError> {
3001 Ok(self
3002 .morph_target_names
3003 .as_ref_option()?
3004 .map(core::ops::Deref::deref))
3005 }
3006}
3007
3008/// An enum to define which UV attribute to use for a texture.
3009///
3010/// It only supports two UV attributes, [`Mesh::ATTRIBUTE_UV_0`] and
3011/// [`Mesh::ATTRIBUTE_UV_1`].
3012/// The default is [`UvChannel::Uv0`].
3013#[derive(Reflect, Default, Debug, Clone, PartialEq, Eq)]
3014#[reflect(Default, Debug, Clone, PartialEq)]
3015pub enum UvChannel {
3016 #[default]
3017 Uv0,
3018 Uv1,
3019}
3020
3021/// Correctly scales and renormalizes an already normalized `normal` by the scale determined by its reciprocal `scale_recip`
3022pub(crate) fn scale_normal(normal: Vec3, scale_recip: Vec3) -> Vec3 {
3023 // This is basically just `normal * scale_recip` but with the added rule that `0. * anything == 0.`
3024 // This is necessary because components of `scale_recip` may be infinities, which do not multiply to zero
3025 let n = Vec3::select(normal.cmpeq(Vec3::ZERO), Vec3::ZERO, normal * scale_recip);
3026
3027 // If n is finite, no component of `scale_recip` was infinite or the normal was perpendicular to the scale
3028 // else the scale had at least one zero-component and the normal needs to point along the direction of that component
3029 if n.is_finite() {
3030 n.normalize_or_zero()
3031 } else {
3032 Vec3::select(n.abs().cmpeq(Vec3::INFINITY), n.signum(), Vec3::ZERO).normalize()
3033 }
3034}
3035
3036impl core::ops::Mul<Mesh> for Transform {
3037 type Output = Mesh;
3038
3039 fn mul(self, rhs: Mesh) -> Self::Output {
3040 rhs.transformed_by(self)
3041 }
3042}
3043
3044/// A version of [`Mesh`] suitable for serializing for short-term transfer.
3045///
3046/// [`Mesh`] does not implement [`Serialize`] / [`Deserialize`] because it is made with the renderer in mind.
3047/// It is not a general-purpose mesh implementation, and its internals are subject to frequent change.
3048/// As such, storing a [`Mesh`] on disk is highly discouraged.
3049///
3050/// But there are still some valid use cases for serializing a [`Mesh`], namely transferring meshes between processes.
3051/// To support this, you can create a [`SerializedMesh`] from a [`Mesh`] with [`SerializedMesh::from_mesh`],
3052/// and then deserialize it with [`SerializedMesh::deserialize`]. The caveats are:
3053/// - The mesh representation is not valid across different versions of Bevy.
3054/// - This conversion is lossy. Only the following information is preserved:
3055/// - Primitive topology
3056/// - Vertex attributes
3057/// - Indices
3058/// - Custom attributes that were not specified with [`MeshDeserializer::add_custom_vertex_attribute`] will be ignored while deserializing.
3059#[cfg(feature = "serialize")]
3060#[derive(Debug, Clone, Serialize, Deserialize)]
3061pub struct SerializedMesh {
3062 primitive_topology: PrimitiveTopology,
3063 attributes: Vec<(MeshVertexAttributeId, SerializedMeshAttributeData)>,
3064 indices: Option<Indices>,
3065}
3066
3067#[cfg(feature = "serialize")]
3068impl SerializedMesh {
3069 /// Create a [`SerializedMesh`] from a [`Mesh`]. See the documentation for [`SerializedMesh`] for caveats.
3070 pub fn from_mesh(mut mesh: Mesh) -> Self {
3071 Self {
3072 primitive_topology: mesh.primitive_topology,
3073 attributes: mesh
3074 .attributes
3075 .replace(None)
3076 .expect(MESH_EXTRACTED_ERROR)
3077 .unwrap()
3078 .into_iter()
3079 .map(|(id, data)| {
3080 (
3081 id,
3082 SerializedMeshAttributeData::from_mesh_attribute_data(data),
3083 )
3084 })
3085 .collect(),
3086 indices: mesh.indices.replace(None).expect(MESH_EXTRACTED_ERROR),
3087 }
3088 }
3089
3090 /// Create a [`Mesh`] from a [`SerializedMesh`]. See the documentation for [`SerializedMesh`] for caveats.
3091 ///
3092 /// Use [`MeshDeserializer`] if you need to pass extra options to the deserialization process, such as specifying custom vertex attributes.
3093 pub fn into_mesh(self) -> Mesh {
3094 MeshDeserializer::default().deserialize(self)
3095 }
3096}
3097
3098/// Use to specify extra options when deserializing a [`SerializedMesh`] into a [`Mesh`].
3099#[cfg(feature = "serialize")]
3100pub struct MeshDeserializer {
3101 custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,
3102}
3103
3104#[cfg(feature = "serialize")]
3105impl Default for MeshDeserializer {
3106 fn default() -> Self {
3107 // Written like this so that the compiler can validate that we use all the built-in attributes.
3108 // If you just added a new attribute and got a compile error, please add it to this list :)
3109 const BUILTINS: [MeshVertexAttribute; Mesh::FIRST_AVAILABLE_CUSTOM_ATTRIBUTE as usize] = [
3110 Mesh::ATTRIBUTE_POSITION,
3111 Mesh::ATTRIBUTE_NORMAL,
3112 Mesh::ATTRIBUTE_UV_0,
3113 Mesh::ATTRIBUTE_UV_1,
3114 Mesh::ATTRIBUTE_TANGENT,
3115 Mesh::ATTRIBUTE_COLOR,
3116 Mesh::ATTRIBUTE_JOINT_WEIGHT,
3117 Mesh::ATTRIBUTE_JOINT_INDEX,
3118 ];
3119 Self {
3120 custom_vertex_attributes: BUILTINS
3121 .into_iter()
3122 .map(|attribute| (attribute.name.into(), attribute))
3123 .collect(),
3124 }
3125 }
3126}
3127
3128#[cfg(feature = "serialize")]
3129impl MeshDeserializer {
3130 /// Create a new [`MeshDeserializer`].
3131 pub fn new() -> Self {
3132 Self::default()
3133 }
3134
3135 /// Register a custom vertex attribute to the deserializer. Custom vertex attributes that were not added with this method will be ignored while deserializing.
3136 pub fn add_custom_vertex_attribute(
3137 &mut self,
3138 name: &str,
3139 attribute: MeshVertexAttribute,
3140 ) -> &mut Self {
3141 self.custom_vertex_attributes.insert(name.into(), attribute);
3142 self
3143 }
3144
3145 /// Deserialize a [`SerializedMesh`] into a [`Mesh`].
3146 ///
3147 /// See the documentation for [`SerializedMesh`] for caveats.
3148 pub fn deserialize(&self, serialized_mesh: SerializedMesh) -> Mesh {
3149 Mesh {
3150 attributes: MeshExtractableData::Data(
3151 serialized_mesh
3152 .attributes
3153 .into_iter()
3154 .filter_map(|(id, data)| {
3155 let attribute = data.attribute.clone();
3156 let Some(data) =
3157 data.try_into_mesh_attribute_data(&self.custom_vertex_attributes)
3158 else {
3159 warn!(
3160 "Deserialized mesh contains custom vertex attribute {attribute:?} that \
3161 was not specified with `MeshDeserializer::add_custom_vertex_attribute`. Ignoring."
3162 );
3163 return None;
3164 };
3165 Some((id, data))
3166 })
3167 .collect()),
3168 indices: serialized_mesh.indices.into(),
3169 ..Mesh::new(serialized_mesh.primitive_topology, RenderAssetUsages::default())
3170 }
3171 }
3172}
3173
3174/// Error that can occur when compressing/quantizing mesh vertex attributes.
3175#[derive(Error, Debug, Clone)]
3176pub enum MeshAttributeCompressionError {
3177 #[error("Vertex attribute {0:?} doesn't exist")]
3178 MissingAttribute(MeshVertexAttributeId),
3179 #[error("Vertex attribute {0:?} must not be empty")]
3180 EmptyAttribute(MeshVertexAttribute),
3181 #[error(
3182 "Vertex attribute {attr:?} must have format {expected:?} before compressing/quantizing"
3183 )]
3184 UnsupportedAttributeForCompression {
3185 attr: MeshVertexAttribute,
3186 expected: VertexFormat,
3187 },
3188 #[error("Vertex attribute {attr:?} must have format `Float32`, `Float32x2` or `Float32x4` before quantizing")]
3189 UnsupportedAttributeForQuantizing { attr: MeshVertexAttribute },
3190}
3191
3192/// Error that can occur when calling [`Mesh::merge_duplicate_vertices`]
3193#[derive(Error, Debug, Clone)]
3194pub enum MeshMergeDuplicateVerticesError {
3195 #[error("Index attribute already set.")]
3196 IndicesAlreadySet,
3197 #[error("Mesh access error: {0}")]
3198 MeshAccessError(#[from] MeshAccessError),
3199}
3200
3201/// Error that can occur when calling [`Mesh::merge`].
3202#[derive(Error, Debug, Clone)]
3203pub enum MeshMergeError {
3204 #[error("Incompatible vertex attribute types: {} and {}", self_attribute.name, other_attribute.map(|a| a.name).unwrap_or("None"))]
3205 IncompatibleVertexAttributes {
3206 self_attribute: MeshVertexAttribute,
3207 other_attribute: Option<MeshVertexAttribute>,
3208 },
3209 #[error(
3210 "Incompatible primitive topologies: {:?} and {:?}",
3211 self_primitive_topology,
3212 other_primitive_topology
3213 )]
3214 IncompatiblePrimitiveTopology {
3215 self_primitive_topology: PrimitiveTopology,
3216 other_primitive_topology: PrimitiveTopology,
3217 },
3218 #[error("Mesh access error: {0}")]
3219 MeshAccessError(#[from] MeshAccessError),
3220}
3221
3222#[cfg(test)]
3223mod tests {
3224 use super::Mesh;
3225 #[cfg(feature = "serialize")]
3226 use super::SerializedMesh;
3227 use crate::mesh::{Indices, MeshWindingInvertError, VertexAttributeValues};
3228 use crate::{
3229 AttributeQuantization, MeshAttributeCompressionFlags, MeshVertexAttribute,
3230 PrimitiveTopology,
3231 };
3232 use bevy_asset::RenderAssetUsages;
3233 use bevy_math::{Vec2, Vec3, Vec3A, Vec4};
3234 use bevy_shape::{Aabb2d, Aabb3d, Triangle3d};
3235 use bevy_transform::components::Transform;
3236
3237 #[test]
3238 #[should_panic]
3239 fn panic_invalid_format() {
3240 let _mesh = Mesh::new(
3241 PrimitiveTopology::TriangleList,
3242 RenderAssetUsages::default(),
3243 )
3244 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0, 0.0]]);
3245 }
3246
3247 #[test]
3248 fn transform_mesh() {
3249 let mesh = Mesh::new(
3250 PrimitiveTopology::TriangleList,
3251 RenderAssetUsages::default(),
3252 )
3253 .with_inserted_attribute(
3254 Mesh::ATTRIBUTE_POSITION,
3255 vec![[-1., -1., 2.], [1., -1., 2.], [0., 1., 2.]],
3256 )
3257 .with_inserted_attribute(
3258 Mesh::ATTRIBUTE_NORMAL,
3259 vec![
3260 Vec3::new(-1., -1., 1.).normalize().to_array(),
3261 Vec3::new(1., -1., 1.).normalize().to_array(),
3262 [0., 0., 1.],
3263 ],
3264 )
3265 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0., 0.], [1., 0.], [0.5, 1.]]);
3266
3267 let mesh = mesh.transformed_by(
3268 Transform::from_translation(Vec3::splat(-2.)).with_scale(Vec3::new(2., 0., -1.)),
3269 );
3270
3271 if let Some(VertexAttributeValues::Float32x3(positions)) =
3272 mesh.attribute(Mesh::ATTRIBUTE_POSITION)
3273 {
3274 // All positions are first scaled resulting in `vec![[-2, 0., -2.], [2., 0., -2.], [0., 0., -2.]]`
3275 // and then shifted by `-2.` along each axis
3276 assert_eq!(
3277 positions,
3278 &vec![[-4.0, -2.0, -4.0], [0.0, -2.0, -4.0], [-2.0, -2.0, -4.0]]
3279 );
3280 } else {
3281 panic!("Mesh does not have a position attribute");
3282 }
3283
3284 if let Some(VertexAttributeValues::Float32x3(normals)) =
3285 mesh.attribute(Mesh::ATTRIBUTE_NORMAL)
3286 {
3287 assert_eq!(normals, &vec![[0., -1., 0.], [0., -1., 0.], [0., 0., -1.]]);
3288 } else {
3289 panic!("Mesh does not have a normal attribute");
3290 }
3291
3292 if let Some(VertexAttributeValues::Float32x2(uvs)) = mesh.attribute(Mesh::ATTRIBUTE_UV_0) {
3293 assert_eq!(uvs, &vec![[0., 0.], [1., 0.], [0.5, 1.]]);
3294 } else {
3295 panic!("Mesh does not have a uv attribute");
3296 }
3297 }
3298
3299 #[test]
3300 fn point_list_mesh_invert_winding() {
3301 let mesh = Mesh::new(PrimitiveTopology::PointList, RenderAssetUsages::default())
3302 .with_inserted_indices(Indices::U32(vec![]));
3303 assert!(matches!(
3304 mesh.with_inverted_winding(),
3305 Err(MeshWindingInvertError::WrongTopology)
3306 ));
3307 }
3308
3309 #[test]
3310 fn line_list_mesh_invert_winding() {
3311 let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
3312 .with_inserted_indices(Indices::U32(vec![0, 1, 1, 2, 2, 3]));
3313 let mesh = mesh.with_inverted_winding().unwrap();
3314 assert_eq!(
3315 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
3316 vec![3, 2, 2, 1, 1, 0]
3317 );
3318 }
3319
3320 #[test]
3321 fn line_list_mesh_invert_winding_fail() {
3322 let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
3323 .with_inserted_indices(Indices::U32(vec![0, 1, 1]));
3324 assert!(matches!(
3325 mesh.with_inverted_winding(),
3326 Err(MeshWindingInvertError::AbruptIndicesEnd)
3327 ));
3328 }
3329
3330 #[test]
3331 fn line_strip_mesh_invert_winding() {
3332 let mesh = Mesh::new(PrimitiveTopology::LineStrip, RenderAssetUsages::default())
3333 .with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));
3334 let mesh = mesh.with_inverted_winding().unwrap();
3335 assert_eq!(
3336 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
3337 vec![3, 2, 1, 0]
3338 );
3339 }
3340
3341 #[test]
3342 fn triangle_list_mesh_invert_winding() {
3343 let mesh = Mesh::new(
3344 PrimitiveTopology::TriangleList,
3345 RenderAssetUsages::default(),
3346 )
3347 .with_inserted_indices(Indices::U32(vec![
3348 0, 3, 1, // First triangle
3349 1, 3, 2, // Second triangle
3350 ]));
3351 let mesh = mesh.with_inverted_winding().unwrap();
3352 assert_eq!(
3353 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
3354 vec![
3355 0, 1, 3, // First triangle
3356 1, 2, 3, // Second triangle
3357 ]
3358 );
3359 }
3360
3361 #[test]
3362 fn triangle_list_mesh_invert_winding_fail() {
3363 let mesh = Mesh::new(
3364 PrimitiveTopology::TriangleList,
3365 RenderAssetUsages::default(),
3366 )
3367 .with_inserted_indices(Indices::U32(vec![0, 3, 1, 2]));
3368 assert!(matches!(
3369 mesh.with_inverted_winding(),
3370 Err(MeshWindingInvertError::AbruptIndicesEnd)
3371 ));
3372 }
3373
3374 #[test]
3375 fn triangle_strip_mesh_invert_winding() {
3376 let mesh = Mesh::new(
3377 PrimitiveTopology::TriangleStrip,
3378 RenderAssetUsages::default(),
3379 )
3380 .with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));
3381 let mesh = mesh.with_inverted_winding().unwrap();
3382 assert_eq!(
3383 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
3384 vec![3, 2, 1, 0]
3385 );
3386 }
3387
3388 #[test]
3389 fn compute_area_weighted_normals() {
3390 let mut mesh = Mesh::new(
3391 PrimitiveTopology::TriangleList,
3392 RenderAssetUsages::default(),
3393 );
3394
3395 // z y
3396 // | /
3397 // 3---2
3398 // | / \
3399 // 0-----1--x
3400
3401 mesh.insert_attribute(
3402 Mesh::ATTRIBUTE_POSITION,
3403 vec![[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
3404 );
3405 mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));
3406 mesh.compute_area_weighted_normals();
3407 let normals = mesh
3408 .attribute(Mesh::ATTRIBUTE_NORMAL)
3409 .unwrap()
3410 .as_float3()
3411 .unwrap();
3412 assert_eq!(4, normals.len());
3413 // 0
3414 assert_eq!(Vec3::new(1., 0., 1.).normalize().to_array(), normals[0]);
3415 // 1
3416 assert_eq!([0., 0., 1.], normals[1]);
3417 // 2
3418 assert_eq!(Vec3::new(1., 0., 1.).normalize().to_array(), normals[2]);
3419 // 3
3420 assert_eq!([1., 0., 0.], normals[3]);
3421 }
3422
3423 #[test]
3424 fn compute_area_weighted_normals_proportionate() {
3425 let mut mesh = Mesh::new(
3426 PrimitiveTopology::TriangleList,
3427 RenderAssetUsages::default(),
3428 );
3429
3430 // z y
3431 // | /
3432 // 3---2..
3433 // | / \
3434 // 0-------1---x
3435
3436 mesh.insert_attribute(
3437 Mesh::ATTRIBUTE_POSITION,
3438 vec![[0., 0., 0.], [2., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
3439 );
3440 mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));
3441 mesh.compute_area_weighted_normals();
3442 let normals = mesh
3443 .attribute(Mesh::ATTRIBUTE_NORMAL)
3444 .unwrap()
3445 .as_float3()
3446 .unwrap();
3447 assert_eq!(4, normals.len());
3448 // 0
3449 assert_eq!(Vec3::new(1., 0., 2.).normalize().to_array(), normals[0]);
3450 // 1
3451 assert_eq!([0., 0., 1.], normals[1]);
3452 // 2
3453 assert_eq!(Vec3::new(1., 0., 2.).normalize().to_array(), normals[2]);
3454 // 3
3455 assert_eq!([1., 0., 0.], normals[3]);
3456 }
3457
3458 #[test]
3459 fn compute_angle_weighted_normals() {
3460 // CuboidMeshBuilder duplicates vertices (even though it is indexed)
3461
3462 // 5---------4
3463 // /| /|
3464 // 1-+-------0 |
3465 // | 6-------|-7
3466 // |/ |/
3467 // 2---------3
3468 let verts = vec![
3469 [1.0, 1.0, 1.0],
3470 [-1.0, 1.0, 1.0],
3471 [-1.0, -1.0, 1.0],
3472 [1.0, -1.0, 1.0],
3473 [1.0, 1.0, -1.0],
3474 [-1.0, 1.0, -1.0],
3475 [-1.0, -1.0, -1.0],
3476 [1.0, -1.0, -1.0],
3477 ];
3478
3479 let indices = Indices::U16(vec![
3480 0, 1, 2, 2, 3, 0, // front
3481 5, 4, 7, 7, 6, 5, // back
3482 1, 5, 6, 6, 2, 1, // left
3483 4, 0, 3, 3, 7, 4, // right
3484 4, 5, 1, 1, 0, 4, // top
3485 3, 2, 6, 6, 7, 3, // bottom
3486 ]);
3487 let mut mesh = Mesh::new(
3488 PrimitiveTopology::TriangleList,
3489 RenderAssetUsages::default(),
3490 );
3491 mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, verts);
3492 mesh.insert_indices(indices);
3493 mesh.compute_smooth_normals();
3494
3495 let normals = mesh
3496 .attribute(Mesh::ATTRIBUTE_NORMAL)
3497 .unwrap()
3498 .as_float3()
3499 .unwrap();
3500
3501 for new in normals.iter().copied().flatten() {
3502 // std impl is unstable
3503 const FRAC_1_SQRT_3: f32 = 0.57735026;
3504 const MIN: f32 = FRAC_1_SQRT_3 - f32::EPSILON;
3505 const MAX: f32 = FRAC_1_SQRT_3 + f32::EPSILON;
3506 assert!(new.abs() >= MIN, "{new} < {MIN}");
3507 assert!(new.abs() <= MAX, "{new} > {MAX}");
3508 }
3509 }
3510
3511 #[test]
3512 fn triangles_from_triangle_list() {
3513 let mut mesh = Mesh::new(
3514 PrimitiveTopology::TriangleList,
3515 RenderAssetUsages::default(),
3516 );
3517 mesh.insert_attribute(
3518 Mesh::ATTRIBUTE_POSITION,
3519 vec![[0., 0., 0.], [1., 0., 0.], [1., 1., 0.], [0., 1., 0.]],
3520 );
3521 mesh.insert_indices(Indices::U32(vec![0, 1, 2, 2, 3, 0]));
3522 assert_eq!(
3523 vec![
3524 Triangle3d {
3525 vertices: [
3526 Vec3::new(0., 0., 0.),
3527 Vec3::new(1., 0., 0.),
3528 Vec3::new(1., 1., 0.),
3529 ]
3530 },
3531 Triangle3d {
3532 vertices: [
3533 Vec3::new(1., 1., 0.),
3534 Vec3::new(0., 1., 0.),
3535 Vec3::new(0., 0., 0.),
3536 ]
3537 }
3538 ],
3539 mesh.triangles().unwrap().collect::<Vec<Triangle3d>>()
3540 );
3541 }
3542
3543 #[test]
3544 fn triangles_from_triangle_strip() {
3545 let mut mesh = Mesh::new(
3546 PrimitiveTopology::TriangleStrip,
3547 RenderAssetUsages::default(),
3548 );
3549 // Triangles: (0, 1, 2), (2, 1, 3), (2, 3, 4), (4, 3, 5)
3550 //
3551 // 4 - 5
3552 // | \ |
3553 // 2 - 3
3554 // | \ |
3555 // 0 - 1
3556 let positions: Vec<Vec3> = [
3557 [0., 0., 0.],
3558 [1., 0., 0.],
3559 [0., 1., 0.],
3560 [1., 1., 0.],
3561 [0., 2., 0.],
3562 [1., 2., 0.],
3563 ]
3564 .into_iter()
3565 .map(Vec3::from_array)
3566 .collect();
3567 mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions.clone());
3568 mesh.insert_indices(Indices::U32(vec![0, 1, 2, 3, 4, 5]));
3569 assert_eq!(
3570 vec![
3571 Triangle3d {
3572 vertices: [positions[0], positions[1], positions[2]]
3573 },
3574 Triangle3d {
3575 vertices: [positions[2], positions[1], positions[3]]
3576 },
3577 Triangle3d {
3578 vertices: [positions[2], positions[3], positions[4]]
3579 },
3580 Triangle3d {
3581 vertices: [positions[4], positions[3], positions[5]]
3582 },
3583 ],
3584 mesh.triangles().unwrap().collect::<Vec<Triangle3d>>()
3585 );
3586 }
3587
3588 #[test]
3589 fn take_gpu_data_calculates_aabb() {
3590 let mut mesh = Mesh::new(
3591 PrimitiveTopology::TriangleList,
3592 RenderAssetUsages::default(),
3593 );
3594 mesh.insert_attribute(
3595 Mesh::ATTRIBUTE_POSITION,
3596 vec![
3597 [-0.5, 0., 0.],
3598 [-1., 0., 0.],
3599 [-1., -1., 0.],
3600 [-0.5, -1., 0.],
3601 ],
3602 );
3603 mesh.insert_indices(Indices::U32(vec![0, 1, 2, 2, 3, 0]));
3604 mesh = mesh.take_gpu_data().unwrap();
3605 assert_eq!(
3606 mesh.final_aabb,
3607 Some(Aabb3d::from_min_max([-1., -1., 0.], [-0.5, 0., 0.]))
3608 );
3609 }
3610
3611 #[cfg(feature = "serialize")]
3612 #[test]
3613 fn serialize_deserialize_mesh() {
3614 let mut mesh = Mesh::new(
3615 PrimitiveTopology::TriangleList,
3616 RenderAssetUsages::default(),
3617 );
3618
3619 mesh.insert_attribute(
3620 Mesh::ATTRIBUTE_POSITION,
3621 vec![[0., 0., 0.], [2., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
3622 );
3623 mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));
3624
3625 let serialized_mesh = SerializedMesh::from_mesh(mesh.clone());
3626 let serialized_string = serde_json::to_string(&serialized_mesh).unwrap();
3627 let serialized_mesh_from_string: SerializedMesh =
3628 serde_json::from_str(&serialized_string).unwrap();
3629 let deserialized_mesh = serialized_mesh_from_string.into_mesh();
3630 assert_eq!(mesh, deserialized_mesh);
3631 }
3632
3633 #[test]
3634 fn merge_duplicate_vertices() {
3635 let mut mesh = Mesh::new(
3636 PrimitiveTopology::TriangleList,
3637 RenderAssetUsages::default(),
3638 );
3639 // Quad made of two triangles.
3640 let positions = vec![
3641 [0.0, 0.0, 0.0],
3642 [1.0, 0.0, 0.0],
3643 [1.0, 1.0, 0.0],
3644 // This will be deduplicated.
3645 [1.0, 1.0, 0.0],
3646 [0.0, 1.0, 0.0],
3647 // Position is equal to the first one but UV is different so it won't be deduplicated.
3648 [0.0, 0.0, 0.0],
3649 ];
3650 let uvs = vec![
3651 [0.0, 0.0],
3652 [1.0, 0.0],
3653 [1.0, 1.0],
3654 // This will be deduplicated.
3655 [1.0, 1.0],
3656 [0.0, 1.0],
3657 // Use different UV here so it won't be deduplicated.
3658 [0.0, 0.5],
3659 ];
3660 mesh.insert_attribute(
3661 Mesh::ATTRIBUTE_POSITION,
3662 VertexAttributeValues::Float32x3(positions.clone()),
3663 );
3664 mesh.insert_attribute(
3665 Mesh::ATTRIBUTE_UV_0,
3666 VertexAttributeValues::Float32x2(uvs.clone()),
3667 );
3668
3669 let res = mesh.merge_duplicate_vertices();
3670 assert!(res.is_ok());
3671 assert_eq!(6, mesh.indices().unwrap().len());
3672 // Note we have 5 unique vertices, not 6.
3673 assert_eq!(5, mesh.attribute(Mesh::ATTRIBUTE_POSITION).unwrap().len());
3674 assert_eq!(5, mesh.attribute(Mesh::ATTRIBUTE_UV_0).unwrap().len());
3675
3676 // Duplicate back.
3677 mesh.duplicate_vertices();
3678 assert!(mesh.indices().is_none());
3679 let VertexAttributeValues::Float32x3(new_positions) =
3680 mesh.attribute(Mesh::ATTRIBUTE_POSITION).unwrap()
3681 else {
3682 panic!("Unexpected attribute type")
3683 };
3684 let VertexAttributeValues::Float32x2(new_uvs) =
3685 mesh.attribute(Mesh::ATTRIBUTE_UV_0).unwrap()
3686 else {
3687 panic!("Unexpected attribute type")
3688 };
3689 assert_eq!(&positions, new_positions);
3690 assert_eq!(&uvs, new_uvs);
3691 }
3692
3693 #[test]
3694 fn compress_mesh_positions() {
3695 let mut mesh = Mesh::new(
3696 PrimitiveTopology::TriangleList,
3697 RenderAssetUsages::default(),
3698 )
3699 .with_inserted_attribute(
3700 Mesh::ATTRIBUTE_POSITION,
3701 vec![
3702 [0.0, 1.0, -1.0],
3703 [1.0, -0.5, -1.0],
3704 [-1.0, -0.5, -1.0],
3705 [0.0, -0.5, 1.0],
3706 ],
3707 );
3708 mesh.compress_positions().unwrap();
3709 assert_eq!(
3710 mesh.final_aabb,
3711 Some(Aabb3d::from_min_max(
3712 Vec3A::new(-1.0, -0.5, -1.0),
3713 Vec3A::new(1.0, 1.0, 1.0)
3714 ))
3715 );
3716 assert_eq!(
3717 mesh.attribute_compression,
3718 MeshAttributeCompressionFlags::COMPRESS_POSITION
3719 );
3720 assert_eq!(
3721 mesh.attribute(Mesh::ATTRIBUTE_POSITION),
3722 Some(&VertexAttributeValues::Snorm16x4(vec![
3723 [0, 32767, -32767, 0],
3724 [32767, -32767, -32767, 0],
3725 [-32767, -32767, -32767, 0],
3726 [0, -32767, 32767, 0],
3727 ]))
3728 );
3729 }
3730
3731 #[test]
3732 fn compress_mesh_uvs() {
3733 let mut mesh = Mesh::new(
3734 PrimitiveTopology::TriangleList,
3735 RenderAssetUsages::default(),
3736 )
3737 .with_inserted_attribute(
3738 Mesh::ATTRIBUTE_UV_0,
3739 vec![[0.126, 0.497], [0.126, 1.0], [0.05, 0.0], [0.0, 0.5]],
3740 )
3741 .with_inserted_attribute(
3742 Mesh::ATTRIBUTE_UV_1,
3743 vec![[12.6, 0.497], [0.126, 1.0], [-4.05, 0.0], [1.0, -0.5]],
3744 );
3745 mesh.compress_uv0().unwrap();
3746 assert_eq!(
3747 mesh.final_uv_ranges,
3748 [
3749 Some(Aabb2d {
3750 min: Vec2::new(0.0, 0.0),
3751 max: Vec2::new(0.126, 1.0)
3752 }),
3753 None
3754 ]
3755 );
3756 assert_eq!(
3757 mesh.attribute_compression,
3758 MeshAttributeCompressionFlags::COMPRESS_UV0
3759 );
3760 assert_eq!(
3761 mesh.attribute(Mesh::ATTRIBUTE_UV_0),
3762 Some(&VertexAttributeValues::Unorm16x2(vec![
3763 [65535, 32571],
3764 [65535, 65535],
3765 [26006, 0],
3766 [0, 32768],
3767 ]))
3768 );
3769
3770 mesh.compress_uv1().unwrap();
3771 assert_eq!(
3772 mesh.final_uv_ranges,
3773 [
3774 Some(Aabb2d {
3775 min: Vec2::new(0.0, 0.0),
3776 max: Vec2::new(0.126, 1.0)
3777 }),
3778 Some(Aabb2d {
3779 min: Vec2::new(-4.05, -0.5),
3780 max: Vec2::new(12.6, 1.0)
3781 })
3782 ]
3783 );
3784 assert_eq!(
3785 mesh.attribute_compression,
3786 MeshAttributeCompressionFlags::COMPRESS_UV0
3787 | MeshAttributeCompressionFlags::COMPRESS_UV1
3788 );
3789 assert_eq!(
3790 mesh.attribute(Mesh::ATTRIBUTE_UV_1),
3791 Some(&VertexAttributeValues::Unorm16x2(vec![
3792 [65535, 43559],
3793 [16437, 65535],
3794 [0, 21845],
3795 [19877, 0]
3796 ]))
3797 );
3798 }
3799
3800 #[test]
3801 fn compress_mesh_normals() {
3802 let mut mesh = Mesh::new(
3803 PrimitiveTopology::TriangleList,
3804 RenderAssetUsages::default(),
3805 )
3806 .with_inserted_attribute(
3807 Mesh::ATTRIBUTE_NORMAL,
3808 vec![
3809 Vec3::new(0.0, 1.0, -1.0).normalize().to_array(),
3810 Vec3::new(1.0, 0.0, -1.0).normalize().to_array(),
3811 Vec3::new(-1.0, 0.0, -1.0).normalize().to_array(),
3812 [0.0, 0.0, 1.0],
3813 ],
3814 );
3815 mesh.compress_normals().unwrap();
3816 assert_eq!(
3817 mesh.attribute_compression,
3818 MeshAttributeCompressionFlags::COMPRESS_NORMAL
3819 );
3820 assert_eq!(
3821 mesh.attribute(Mesh::ATTRIBUTE_NORMAL),
3822 Some(&VertexAttributeValues::Snorm16x2(vec![
3823 [16384, 32767],
3824 [32767, 16384],
3825 [-32767, 16384],
3826 [0, 0],
3827 ]))
3828 );
3829 }
3830
3831 #[test]
3832 fn compress_mesh_tangents() {
3833 let mut mesh = Mesh::new(
3834 PrimitiveTopology::TriangleList,
3835 RenderAssetUsages::default(),
3836 )
3837 .with_inserted_attribute(
3838 Mesh::ATTRIBUTE_TANGENT,
3839 vec![
3840 Vec3::new(0.0, 1.0, 1.0).normalize().extend(1.0).to_array(),
3841 Vec3::new(1.0, 0.0, 1.0).normalize().extend(-1.0).to_array(),
3842 Vec3::new(-1.0, 0.0, 1.0).normalize().extend(1.0).to_array(),
3843 [1.0, 0.0, 0.0, 1.0],
3844 ],
3845 );
3846 mesh.compress_tangents().unwrap();
3847 assert_eq!(
3848 mesh.attribute_compression,
3849 MeshAttributeCompressionFlags::COMPRESS_TANGENT
3850 );
3851 assert_eq!(
3852 mesh.attribute(Mesh::ATTRIBUTE_TANGENT),
3853 Some(&VertexAttributeValues::Snorm16x2(vec![
3854 [0, 24575],
3855 [16384, -16384],
3856 [-16384, 16384],
3857 [32767, 16384],
3858 ]))
3859 );
3860 }
3861
3862 #[test]
3863 fn quantize_mesh_colors() {
3864 let mut mesh = Mesh::new(
3865 PrimitiveTopology::TriangleList,
3866 RenderAssetUsages::default(),
3867 )
3868 .with_inserted_attribute(
3869 Mesh::ATTRIBUTE_COLOR,
3870 vec![
3871 [0.05, 1.0, 0.15, 1.0],
3872 [0.75, 0.07, 1.0, 1.0],
3873 [0.04, 0.11, 1.0, 1.0],
3874 [1.0, 0.11, 0.13, 1.0],
3875 ],
3876 );
3877 let mut mesh_color_f16 = mesh.clone();
3878
3879 mesh.quantize_colors(AttributeQuantization::Unorm8).unwrap();
3880 assert_eq!(
3881 mesh.attribute_compression,
3882 MeshAttributeCompressionFlags::empty()
3883 );
3884 assert_eq!(
3885 mesh.attribute(Mesh::ATTRIBUTE_COLOR),
3886 Some(&VertexAttributeValues::Unorm8x4(vec![
3887 [13, 255, 38, 255],
3888 [191, 18, 255, 255],
3889 [10, 28, 255, 255],
3890 [255, 28, 33, 255],
3891 ]))
3892 );
3893
3894 mesh_color_f16
3895 .quantize_colors(AttributeQuantization::Float16)
3896 .unwrap();
3897 assert_eq!(
3898 mesh_color_f16.attribute_compression,
3899 MeshAttributeCompressionFlags::empty()
3900 );
3901 let VertexAttributeValues::Float16x4(color_f16) =
3902 mesh_color_f16.attribute(Mesh::ATTRIBUTE_COLOR).unwrap()
3903 else {
3904 panic!("Color attribute is not quantized to Float16x4")
3905 };
3906 assert!(color_f16
3907 .iter()
3908 .flatten()
3909 .zip(
3910 [
3911 [0.049987793, 1.0, 0.15002441, 1.0].map(half::f16::from_f32),
3912 [0.75, 0.070007324, 1.0, 1.0].map(half::f16::from_f32),
3913 [0.040008545, 0.10998535, 1.0, 1.0].map(half::f16::from_f32),
3914 [1.0, 0.10998535, 0.13000488, 1.0].map(half::f16::from_f32)
3915 ]
3916 .iter()
3917 .flatten()
3918 )
3919 .all(|(a, b)| approx::relative_eq!(a.to_f32(), b.to_f32())));
3920 }
3921
3922 #[test]
3923 fn quantize_mesh_joint_weights() {
3924 let mut mesh = Mesh::new(
3925 PrimitiveTopology::TriangleList,
3926 RenderAssetUsages::default(),
3927 )
3928 .with_inserted_attribute(
3929 Mesh::ATTRIBUTE_JOINT_WEIGHT,
3930 vec![
3931 [1.0, 0.0, 0.0, 0.0],
3932 [0.0, 1.0, 0.0, 0.0],
3933 [0.0, 0.0, 1.0, 0.0],
3934 [0.0, 0.0, 0.0, 1.0],
3935 [0.1, 0.9, 0.0, 0.0],
3936 [0.1, 0.2, 0.7, 0.0],
3937 [0.1, 0.2, 0.4, 0.3],
3938 ],
3939 );
3940
3941 mesh.quantize_joint_weights(AttributeQuantization::Unorm16)
3942 .unwrap();
3943 assert_eq!(
3944 mesh.attribute_compression,
3945 MeshAttributeCompressionFlags::empty()
3946 );
3947 assert_eq!(
3948 mesh.attribute(Mesh::ATTRIBUTE_JOINT_WEIGHT),
3949 Some(&VertexAttributeValues::Unorm16x4(vec![
3950 [65535, 0, 0, 0],
3951 [0, 65535, 0, 0],
3952 [0, 0, 65535, 0],
3953 [0, 0, 0, 65535],
3954 [6554, 58982, 0, 0],
3955 [6554, 13107, 45875, 0],
3956 [6554, 13107, 26214, 19661]
3957 ]))
3958 );
3959 }
3960
3961 #[test]
3962 fn compress_mesh_indices() {
3963 let mut mesh = Mesh::new(
3964 PrimitiveTopology::TriangleList,
3965 RenderAssetUsages::default(),
3966 )
3967 .with_inserted_indices(Indices::U32(vec![0, 1, 2, 0, 3, 1, 0, 2, 3, 1, 3, 2]));
3968
3969 mesh.compress_indices();
3970 assert_eq!(
3971 mesh.attribute_compression,
3972 MeshAttributeCompressionFlags::empty()
3973 );
3974 assert_eq!(
3975 mesh.indices(),
3976 Some(&Indices::U16(vec![0, 1, 2, 0, 3, 1, 0, 2, 3, 1, 3, 2]))
3977 );
3978 }
3979
3980 #[test]
3981 fn quantize_mesh_float32_attributes() {
3982 let f4 = MeshVertexAttribute::new("f32x4", 10, wgpu_types::VertexFormat::Float32x4);
3983 let f2 = MeshVertexAttribute::new("f32x2", 11, wgpu_types::VertexFormat::Float32x2);
3984 let f1 = MeshVertexAttribute::new("f32x1", 12, wgpu_types::VertexFormat::Float32);
3985 let mut mesh = Mesh::new(
3986 PrimitiveTopology::TriangleList,
3987 RenderAssetUsages::default(),
3988 )
3989 .with_inserted_attribute(
3990 f4,
3991 vec![
3992 [1.0, 0.0, -2.0, 0.0],
3993 [0.0, 5.0, 0.0, 0.0],
3994 [0.0, 0.0, -1.0, 0.0],
3995 [0.0, 0.0, 0.0, 1.0],
3996 [0.1, -0.9, 0.0, 0.0],
3997 [0.1, 0.2, -0.7, 0.0],
3998 [0.1, 0.2, 0.4, 0.3],
3999 ],
4000 )
4001 .with_inserted_attribute(
4002 f2,
4003 vec![
4004 [10.0, 0.0],
4005 [0.0, 1.0],
4006 [-2.0, 0.0],
4007 [-0.5, 0.0],
4008 [0.1, 0.9],
4009 [0.1, 0.2],
4010 [0.1, 0.2],
4011 ],
4012 )
4013 .with_inserted_attribute(f1, vec![10.0, 0.0, 2.0, 0.0, 0.1, 0.1, 0.1]);
4014
4015 mesh.quantize_float32_attribute(f4, AttributeQuantization::Unorm16)
4016 .unwrap();
4017 assert_eq!(
4018 mesh.attribute(f4),
4019 Some(&VertexAttributeValues::Unorm16x4(vec![
4020 [65535, 0, 0, 0],
4021 [0, 65535, 0, 0],
4022 [0, 0, 0, 0],
4023 [0, 0, 0, 65535],
4024 [6554, 0, 0, 0],
4025 [6554, 13107, 0, 0],
4026 [6554, 13107, 26214, 19661]
4027 ]))
4028 );
4029
4030 mesh.quantize_float32_attribute(f2, AttributeQuantization::Snorm8)
4031 .unwrap();
4032 assert_eq!(
4033 mesh.attribute(f2),
4034 Some(&VertexAttributeValues::Snorm8x2(vec![
4035 [127, 0],
4036 [0, 127],
4037 [-127, 0],
4038 [-64, 0],
4039 [13, 114],
4040 [13, 25],
4041 [13, 25]
4042 ]))
4043 );
4044
4045 mesh.quantize_float32_attribute(f1, AttributeQuantization::Float16)
4046 .unwrap();
4047 let VertexAttributeValues::Float16(f16_values) = mesh.attribute(f1).unwrap() else {
4048 panic!("Attribute f1 is not quantized to Float16")
4049 };
4050 assert!(f16_values
4051 .iter()
4052 .zip([10.0, 0.0, 2.0, 0.0, 0.1, 0.1, 0.1].map(half::f16::from_f32))
4053 .all(|(a, b)| approx::relative_eq!(a.to_f32(), b.to_f32())));
4054 }
4055
4056 #[test]
4057 fn get_attributes_mut_test() {
4058 let mut mesh = Mesh::new(PrimitiveTopology::PointList, RenderAssetUsages::default());
4059
4060 mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vec![Vec3::new(0., 0., 0.)]);
4061 mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vec![Vec3::new(1., 1., 1.)]);
4062 mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vec![Vec4::new(2., 2., 2., 2.)]);
4063 {
4064 let Ok([pos, normal, color]) = mesh.try_disjoint_attributes_mut([
4065 &Mesh::ATTRIBUTE_POSITION.id,
4066 &Mesh::ATTRIBUTE_NORMAL.id,
4067 &Mesh::ATTRIBUTE_COLOR.id,
4068 ]) else {
4069 unreachable!("Data shouldn't have been extracted.");
4070 };
4071 assert_eq!(pos.unwrap().0, &Mesh::ATTRIBUTE_POSITION);
4072 assert_eq!(normal.unwrap().0, &Mesh::ATTRIBUTE_NORMAL);
4073 assert_eq!(color.unwrap().0, &Mesh::ATTRIBUTE_COLOR);
4074 }
4075 {
4076 let Ok([pos, color, normal]) = mesh.try_disjoint_attributes_mut([
4077 &Mesh::ATTRIBUTE_POSITION.id,
4078 &Mesh::ATTRIBUTE_COLOR.id,
4079 &Mesh::ATTRIBUTE_NORMAL.id,
4080 ]) else {
4081 unreachable!("Data shouldn't have been extracted.");
4082 };
4083 assert_eq!(pos.unwrap().0, &Mesh::ATTRIBUTE_POSITION);
4084 assert_eq!(color.unwrap().0, &Mesh::ATTRIBUTE_COLOR);
4085 assert_eq!(normal.unwrap().0, &Mesh::ATTRIBUTE_NORMAL);
4086 }
4087 {
4088 let Ok([pos1, pos2, pos3]) = mesh.try_disjoint_attributes_mut([
4089 &Mesh::ATTRIBUTE_POSITION.id,
4090 &Mesh::ATTRIBUTE_POSITION.id,
4091 &Mesh::ATTRIBUTE_POSITION.id,
4092 ]) else {
4093 unreachable!("Data shouldn't have been extracted.");
4094 };
4095 assert_eq!(pos1.unwrap().0, &Mesh::ATTRIBUTE_POSITION);
4096 assert!(pos2.is_none());
4097 assert!(pos3.is_none());
4098 }
4099 {
4100 let Ok([pos1, pos2, pos3, normal, color]) = mesh.try_disjoint_attributes_mut([
4101 &Mesh::ATTRIBUTE_POSITION.id,
4102 &Mesh::ATTRIBUTE_POSITION.id,
4103 &Mesh::ATTRIBUTE_POSITION.id,
4104 &Mesh::ATTRIBUTE_NORMAL.id,
4105 &Mesh::ATTRIBUTE_COLOR.id,
4106 ]) else {
4107 unreachable!("Data shouldn't have been extracted.");
4108 };
4109 assert_eq!(pos1.unwrap().0, &Mesh::ATTRIBUTE_POSITION);
4110 assert!(pos2.is_none());
4111 assert!(pos3.is_none());
4112 assert_eq!(normal.unwrap().0, &Mesh::ATTRIBUTE_NORMAL);
4113 assert_eq!(color.unwrap().0, &Mesh::ATTRIBUTE_COLOR);
4114 }
4115 {
4116 let Ok([color, uv0, normal, uv1, pos]) = mesh.try_disjoint_attributes_mut([
4117 &Mesh::ATTRIBUTE_COLOR.id,
4118 &Mesh::ATTRIBUTE_UV_0.id,
4119 &Mesh::ATTRIBUTE_NORMAL.id,
4120 &Mesh::ATTRIBUTE_UV_1.id,
4121 &Mesh::ATTRIBUTE_POSITION.id,
4122 ]) else {
4123 unreachable!("Data shouldn't have been extracted.");
4124 };
4125 assert_eq!(color.unwrap().0, &Mesh::ATTRIBUTE_COLOR);
4126 assert!(uv0.is_none());
4127 assert_eq!(normal.unwrap().0, &Mesh::ATTRIBUTE_NORMAL);
4128 assert!(uv1.is_none());
4129 assert_eq!(pos.unwrap().0, &Mesh::ATTRIBUTE_POSITION);
4130 }
4131 }
4132}