bevy_mesh/mesh.rs
1use bevy_transform::components::Transform;
2pub use wgpu_types::PrimitiveTopology;
3
4use super::{
5 triangle_area_normal, triangle_normal, FourIterators, Indices, MeshAttributeData,
6 MeshTrianglesError, MeshVertexAttribute, MeshVertexAttributeId, MeshVertexBufferLayout,
7 MeshVertexBufferLayoutRef, MeshVertexBufferLayouts, MeshWindingInvertError,
8 VertexAttributeValues, VertexBufferLayout,
9};
10#[cfg(feature = "serialize")]
11use crate::SerializedMeshAttributeData;
12use alloc::collections::BTreeMap;
13#[cfg(feature = "morph")]
14use bevy_asset::Handle;
15use bevy_asset::{Asset, RenderAssetUsages};
16#[cfg(feature = "morph")]
17use bevy_image::Image;
18use bevy_math::{bounding::Aabb3d, primitives::Triangle3d, *};
19#[cfg(feature = "serialize")]
20use bevy_platform::collections::HashMap;
21use bevy_reflect::Reflect;
22use bytemuck::cast_slice;
23#[cfg(feature = "serialize")]
24use serde::{Deserialize, Serialize};
25use thiserror::Error;
26use tracing::warn;
27use wgpu_types::{VertexAttribute, VertexFormat, VertexStepMode};
28
29pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
30pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
31
32/// Error from accessing mesh vertex attributes or indices
33#[derive(Error, Debug, Clone)]
34pub enum MeshAccessError {
35 #[error("The mesh vertex/index data has been extracted to the RenderWorld (via `Mesh::asset_usage`)")]
36 ExtractedToRenderWorld,
37 #[error("The requested mesh data wasn't found in this mesh")]
38 NotFound,
39}
40
41const MESH_EXTRACTED_ERROR: &str = "Mesh has been extracted to RenderWorld. To access vertex attributes, the mesh `asset_usage` must include `MAIN_WORLD`";
42
43// storage for extractable data with access methods which return errors if the
44// contents have already been extracted
45#[derive(Debug, Clone, PartialEq, Reflect, Default)]
46enum MeshExtractableData<T> {
47 Data(T),
48 #[default]
49 NoData,
50 ExtractedToRenderWorld,
51}
52
53impl<T> MeshExtractableData<T> {
54 // get a reference to internal data. returns error if data has been extracted, or if no
55 // data exists
56 fn as_ref(&self) -> Result<&T, MeshAccessError> {
57 match self {
58 MeshExtractableData::Data(data) => Ok(data),
59 MeshExtractableData::NoData => Err(MeshAccessError::NotFound),
60 MeshExtractableData::ExtractedToRenderWorld => {
61 Err(MeshAccessError::ExtractedToRenderWorld)
62 }
63 }
64 }
65
66 // get an optional reference to internal data. returns error if data has been extracted
67 fn as_ref_option(&self) -> Result<Option<&T>, MeshAccessError> {
68 match self {
69 MeshExtractableData::Data(data) => Ok(Some(data)),
70 MeshExtractableData::NoData => Ok(None),
71 MeshExtractableData::ExtractedToRenderWorld => {
72 Err(MeshAccessError::ExtractedToRenderWorld)
73 }
74 }
75 }
76
77 // get a mutable reference to internal data. returns error if data has been extracted,
78 // or if no data exists
79 fn as_mut(&mut self) -> Result<&mut T, MeshAccessError> {
80 match self {
81 MeshExtractableData::Data(data) => Ok(data),
82 MeshExtractableData::NoData => Err(MeshAccessError::NotFound),
83 MeshExtractableData::ExtractedToRenderWorld => {
84 Err(MeshAccessError::ExtractedToRenderWorld)
85 }
86 }
87 }
88
89 // get an optional mutable reference to internal data. returns error if data has been extracted
90 fn as_mut_option(&mut self) -> Result<Option<&mut T>, MeshAccessError> {
91 match self {
92 MeshExtractableData::Data(data) => Ok(Some(data)),
93 MeshExtractableData::NoData => Ok(None),
94 MeshExtractableData::ExtractedToRenderWorld => {
95 Err(MeshAccessError::ExtractedToRenderWorld)
96 }
97 }
98 }
99
100 // extract data and replace self with `ExtractedToRenderWorld`. returns error if
101 // data has been extracted
102 fn extract(&mut self) -> Result<MeshExtractableData<T>, MeshAccessError> {
103 match core::mem::replace(self, MeshExtractableData::ExtractedToRenderWorld) {
104 MeshExtractableData::ExtractedToRenderWorld => {
105 Err(MeshAccessError::ExtractedToRenderWorld)
106 }
107 not_extracted => Ok(not_extracted),
108 }
109 }
110
111 // replace internal data. returns the existing data, or an error if data has been extracted
112 fn replace(
113 &mut self,
114 data: impl Into<MeshExtractableData<T>>,
115 ) -> Result<Option<T>, MeshAccessError> {
116 match core::mem::replace(self, data.into()) {
117 MeshExtractableData::ExtractedToRenderWorld => {
118 *self = MeshExtractableData::ExtractedToRenderWorld;
119 Err(MeshAccessError::ExtractedToRenderWorld)
120 }
121 MeshExtractableData::Data(t) => Ok(Some(t)),
122 MeshExtractableData::NoData => Ok(None),
123 }
124 }
125}
126
127impl<T> From<Option<T>> for MeshExtractableData<T> {
128 fn from(value: Option<T>) -> Self {
129 match value {
130 Some(data) => MeshExtractableData::Data(data),
131 None => MeshExtractableData::NoData,
132 }
133 }
134}
135
136/// A 3D object made out of vertices representing triangles, lines, or points,
137/// with "attribute" values for each vertex.
138///
139/// Meshes can be automatically generated by a bevy `AssetLoader` (generally by loading a `Gltf` file),
140/// or by converting a [primitive](bevy_math::primitives) using [`into`](Into).
141/// It is also possible to create one manually. They can be edited after creation.
142///
143/// Meshes can be rendered with a [`Mesh2d`](crate::Mesh2d) and `MeshMaterial2d`
144/// or [`Mesh3d`](crate::Mesh3d) and `MeshMaterial3d` for 2D and 3D respectively.
145///
146/// A [`Mesh`] in Bevy is equivalent to a "primitive" in the glTF format, for a
147/// glTF Mesh representation, see `GltfMesh`.
148///
149/// ## Manual creation
150///
151/// The following function will construct a flat mesh, to be rendered with a
152/// `StandardMaterial` or `ColorMaterial`:
153///
154/// ```
155/// # use bevy_mesh::{Mesh, Indices, PrimitiveTopology};
156/// # use bevy_asset::RenderAssetUsages;
157/// fn create_simple_parallelogram() -> Mesh {
158/// // Create a new mesh using a triangle list topology, where each set of 3 vertices composes a triangle.
159/// Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default())
160/// // Add 4 vertices, each with its own position attribute (coordinate in
161/// // 3D space), for each of the corners of the parallelogram.
162/// .with_inserted_attribute(
163/// Mesh::ATTRIBUTE_POSITION,
164/// 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]]
165/// )
166/// // Assign a UV coordinate to each vertex.
167/// .with_inserted_attribute(
168/// Mesh::ATTRIBUTE_UV_0,
169/// vec![[0.0, 1.0], [0.5, 0.0], [1.0, 0.0], [0.5, 1.0]]
170/// )
171/// // Assign normals (everything points outwards)
172/// .with_inserted_attribute(
173/// Mesh::ATTRIBUTE_NORMAL,
174/// 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]]
175/// )
176/// // After defining all the vertices and their attributes, build each triangle using the
177/// // indices of the vertices that make it up in a counter-clockwise order.
178/// .with_inserted_indices(Indices::U32(vec![
179/// // First triangle
180/// 0, 3, 1,
181/// // Second triangle
182/// 1, 3, 2
183/// ]))
184/// }
185/// ```
186///
187/// You can see how it looks like [here](https://github.com/bevyengine/bevy/blob/main/assets/docs/Mesh.png),
188/// used in a [`Mesh3d`](crate::Mesh3d) with a square bevy logo texture, with added axis, points,
189/// lines and text for clarity.
190///
191/// ## Other examples
192///
193/// For further visualization, explanation, and examples, see the built-in Bevy examples,
194/// and the [implementation of the built-in shapes](https://github.com/bevyengine/bevy/tree/main/crates/bevy_mesh/src/primitives).
195/// In particular, [generate_custom_mesh](https://github.com/bevyengine/bevy/blob/main/examples/3d/generate_custom_mesh.rs)
196/// teaches you to access and modify the attributes of a [`Mesh`] after creating it.
197///
198/// ## Common points of confusion
199///
200/// - UV maps in Bevy start at the top-left, see [`ATTRIBUTE_UV_0`](Mesh::ATTRIBUTE_UV_0),
201/// other APIs can have other conventions, `OpenGL` starts at bottom-left.
202/// - It is possible and sometimes useful for multiple vertices to have the same
203/// [position attribute](Mesh::ATTRIBUTE_POSITION) value,
204/// it's a common technique in 3D modeling for complex UV mapping or other calculations.
205/// - Bevy performs frustum culling based on the `Aabb` of meshes, which is calculated
206/// and added automatically for new meshes only. If a mesh is modified, the entity's `Aabb`
207/// needs to be updated manually or deleted so that it is re-calculated.
208///
209/// ## Use with `StandardMaterial`
210///
211/// To render correctly with `StandardMaterial`, a mesh needs to have properly defined:
212/// - [`UVs`](Mesh::ATTRIBUTE_UV_0): Bevy needs to know how to map a texture onto the mesh
213/// (also true for `ColorMaterial`).
214/// - [`Normals`](Mesh::ATTRIBUTE_NORMAL): Bevy needs to know how light interacts with your mesh.
215/// [0.0, 0.0, 1.0] is very common for simple flat meshes on the XY plane,
216/// because simple meshes are smooth and they don't require complex light calculations.
217/// - Vertex winding order: by default, `StandardMaterial.cull_mode` is `Some(Face::Back)`,
218/// which means that Bevy would *only* render the "front" of each triangle, which
219/// is the side of the triangle from where the vertices appear in a *counter-clockwise* order.
220///
221/// ## Remote Inspection
222///
223/// To transmit a [`Mesh`] between two running Bevy apps, e.g. through BRP, use [`SerializedMesh`].
224/// This type is only meant for short-term transmission between same versions and should not be stored anywhere.
225#[derive(Asset, Debug, Clone, Reflect, PartialEq)]
226#[reflect(Clone)]
227pub struct Mesh {
228 #[reflect(ignore, clone)]
229 primitive_topology: PrimitiveTopology,
230 /// `std::collections::BTreeMap` with all defined vertex attributes (Positions, Normals, ...)
231 /// for this mesh. Attribute ids to attribute values.
232 /// Uses a [`BTreeMap`] because, unlike `HashMap`, it has a defined iteration order,
233 /// which allows easy stable `VertexBuffers` (i.e. same buffer order)
234 #[reflect(ignore, clone)]
235 attributes: MeshExtractableData<BTreeMap<MeshVertexAttributeId, MeshAttributeData>>,
236 indices: MeshExtractableData<Indices>,
237 #[cfg(feature = "morph")]
238 morph_targets: MeshExtractableData<Handle<Image>>,
239 #[cfg(feature = "morph")]
240 morph_target_names: MeshExtractableData<Vec<String>>,
241 pub asset_usage: RenderAssetUsages,
242 /// Whether or not to build a BLAS for use with `bevy_solari` raytracing.
243 ///
244 /// Note that this is _not_ whether the mesh is _compatible_ with `bevy_solari` raytracing.
245 /// This field just controls whether or not a BLAS gets built for this mesh, assuming that
246 /// the mesh is compatible.
247 ///
248 /// The use case for this field is using lower-resolution proxy meshes for raytracing (to save on BLAS memory usage),
249 /// while using higher-resolution meshes for raster. You can set this field to true for the lower-resolution proxy mesh,
250 /// and to false for the high-resolution raster mesh.
251 ///
252 /// Alternatively, you can use the same mesh for both raster and raytracing, with this field set to true.
253 ///
254 /// Does nothing if not used with `bevy_solari`, or if the mesh is not compatible
255 /// with `bevy_solari` (see `bevy_solari`'s docs).
256 pub enable_raytracing: bool,
257 /// Precomputed min and max extents of the mesh position data. Used mainly for constructing `Aabb`s for frustum culling.
258 /// This data will be set if/when a mesh is extracted to the GPU
259 pub final_aabb: Option<Aabb3d>,
260}
261
262impl Mesh {
263 /// Where the vertex is located in space. Use in conjunction with [`Mesh::insert_attribute`]
264 /// or [`Mesh::with_inserted_attribute`].
265 ///
266 /// The format of this attribute is [`VertexFormat::Float32x3`].
267 pub const ATTRIBUTE_POSITION: MeshVertexAttribute =
268 MeshVertexAttribute::new("Vertex_Position", 0, VertexFormat::Float32x3);
269
270 /// The direction the vertex normal is facing in.
271 /// Use in conjunction with [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`].
272 ///
273 /// The format of this attribute is [`VertexFormat::Float32x3`].
274 pub const ATTRIBUTE_NORMAL: MeshVertexAttribute =
275 MeshVertexAttribute::new("Vertex_Normal", 1, VertexFormat::Float32x3);
276
277 /// Texture coordinates for the vertex. Use in conjunction with [`Mesh::insert_attribute`]
278 /// or [`Mesh::with_inserted_attribute`].
279 ///
280 /// Generally `[0.,0.]` is mapped to the top left of the texture, and `[1.,1.]` to the bottom-right.
281 ///
282 /// By default values outside will be clamped per pixel not for the vertex,
283 /// "stretching" the borders of the texture.
284 /// This behavior can be useful in some cases, usually when the borders have only
285 /// one color, for example a logo, and you want to "extend" those borders.
286 ///
287 /// For different mapping outside of `0..=1` range,
288 /// see [`ImageAddressMode`](bevy_image::ImageAddressMode).
289 ///
290 /// The format of this attribute is [`VertexFormat::Float32x2`].
291 pub const ATTRIBUTE_UV_0: MeshVertexAttribute =
292 MeshVertexAttribute::new("Vertex_Uv", 2, VertexFormat::Float32x2);
293
294 /// Alternate texture coordinates for the vertex. Use in conjunction with
295 /// [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`].
296 ///
297 /// Typically, these are used for lightmaps, textures that provide
298 /// precomputed illumination.
299 ///
300 /// The format of this attribute is [`VertexFormat::Float32x2`].
301 pub const ATTRIBUTE_UV_1: MeshVertexAttribute =
302 MeshVertexAttribute::new("Vertex_Uv_1", 3, VertexFormat::Float32x2);
303
304 /// The direction of the vertex tangent. Used for normal mapping.
305 /// Usually generated with [`generate_tangents`](Mesh::generate_tangents) or
306 /// [`with_generated_tangents`](Mesh::with_generated_tangents).
307 ///
308 /// The format of this attribute is [`VertexFormat::Float32x4`].
309 pub const ATTRIBUTE_TANGENT: MeshVertexAttribute =
310 MeshVertexAttribute::new("Vertex_Tangent", 4, VertexFormat::Float32x4);
311
312 /// Per vertex coloring. Use in conjunction with [`Mesh::insert_attribute`]
313 /// or [`Mesh::with_inserted_attribute`].
314 ///
315 /// The format of this attribute is [`VertexFormat::Float32x4`].
316 pub const ATTRIBUTE_COLOR: MeshVertexAttribute =
317 MeshVertexAttribute::new("Vertex_Color", 5, VertexFormat::Float32x4);
318
319 /// Per vertex joint transform matrix weight. Use in conjunction with [`Mesh::insert_attribute`]
320 /// or [`Mesh::with_inserted_attribute`].
321 ///
322 /// The format of this attribute is [`VertexFormat::Float32x4`].
323 pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute =
324 MeshVertexAttribute::new("Vertex_JointWeight", 6, VertexFormat::Float32x4);
325
326 /// Per vertex joint transform matrix index. Use in conjunction with [`Mesh::insert_attribute`]
327 /// or [`Mesh::with_inserted_attribute`].
328 ///
329 /// The format of this attribute is [`VertexFormat::Uint16x4`].
330 pub const ATTRIBUTE_JOINT_INDEX: MeshVertexAttribute =
331 MeshVertexAttribute::new("Vertex_JointIndex", 7, VertexFormat::Uint16x4);
332
333 /// The first index that can be used for custom vertex attributes.
334 /// Only the attributes with an index below this are used by Bevy.
335 pub const FIRST_AVAILABLE_CUSTOM_ATTRIBUTE: u64 = 8;
336
337 /// Construct a new mesh. You need to provide a [`PrimitiveTopology`] so that the
338 /// renderer knows how to treat the vertex data. Most of the time this will be
339 /// [`PrimitiveTopology::TriangleList`].
340 pub fn new(primitive_topology: PrimitiveTopology, asset_usage: RenderAssetUsages) -> Self {
341 Mesh {
342 primitive_topology,
343 attributes: MeshExtractableData::Data(Default::default()),
344 indices: MeshExtractableData::NoData,
345 #[cfg(feature = "morph")]
346 morph_targets: MeshExtractableData::NoData,
347 #[cfg(feature = "morph")]
348 morph_target_names: MeshExtractableData::NoData,
349 asset_usage,
350 enable_raytracing: true,
351 final_aabb: None,
352 }
353 }
354
355 /// Returns the topology of the mesh.
356 pub fn primitive_topology(&self) -> PrimitiveTopology {
357 self.primitive_topology
358 }
359
360 /// Sets the data for a vertex attribute (position, normal, etc.). The name will
361 /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
362 ///
363 /// `Aabb` of entities with modified mesh are not updated automatically.
364 ///
365 /// # Panics
366 /// Panics when the format of the values does not match the attribute's format.
367 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
368 /// this as an error use [`Mesh::try_insert_attribute`]
369 #[inline]
370 pub fn insert_attribute(
371 &mut self,
372 attribute: MeshVertexAttribute,
373 values: impl Into<VertexAttributeValues>,
374 ) {
375 self.try_insert_attribute(attribute, values)
376 .expect(MESH_EXTRACTED_ERROR);
377 }
378
379 /// Sets the data for a vertex attribute (position, normal, etc.). The name will
380 /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
381 ///
382 /// `Aabb` of entities with modified mesh are not updated automatically.
383 ///
384 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
385 ///
386 /// # Panics
387 /// Panics when the format of the values does not match the attribute's format.
388 #[inline]
389 pub fn try_insert_attribute(
390 &mut self,
391 attribute: MeshVertexAttribute,
392 values: impl Into<VertexAttributeValues>,
393 ) -> Result<(), MeshAccessError> {
394 let values = values.into();
395 let values_format = VertexFormat::from(&values);
396 if values_format != attribute.format {
397 panic!(
398 "Failed to insert attribute. Invalid attribute format for {}. Given format is {values_format:?} but expected {:?}",
399 attribute.name, attribute.format
400 );
401 }
402
403 self.attributes
404 .as_mut()?
405 .insert(attribute.id, MeshAttributeData { attribute, values });
406 Ok(())
407 }
408
409 /// Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.).
410 /// The name will often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
411 ///
412 /// (Alternatively, you can use [`Mesh::insert_attribute`] to mutate an existing mesh in-place)
413 ///
414 /// `Aabb` of entities with modified mesh are not updated automatically.
415 ///
416 /// # Panics
417 /// Panics when the format of the values does not match the attribute's format.
418 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
419 /// this as an error use [`Mesh::try_with_inserted_attribute`]
420 #[must_use]
421 #[inline]
422 pub fn with_inserted_attribute(
423 mut self,
424 attribute: MeshVertexAttribute,
425 values: impl Into<VertexAttributeValues>,
426 ) -> Self {
427 self.insert_attribute(attribute, values);
428 self
429 }
430
431 /// Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.).
432 /// The name will often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
433 ///
434 /// (Alternatively, you can use [`Mesh::insert_attribute`] to mutate an existing mesh in-place)
435 ///
436 /// `Aabb` of entities with modified mesh are not updated automatically.
437 ///
438 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
439 #[inline]
440 pub fn try_with_inserted_attribute(
441 mut self,
442 attribute: MeshVertexAttribute,
443 values: impl Into<VertexAttributeValues>,
444 ) -> Result<Self, MeshAccessError> {
445 self.try_insert_attribute(attribute, values)?;
446 Ok(self)
447 }
448
449 /// Removes the data for a vertex attribute
450 ///
451 /// # Panics
452 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
453 /// this as an error use [`Mesh::try_remove_attribute`]
454 pub fn remove_attribute(
455 &mut self,
456 attribute: impl Into<MeshVertexAttributeId>,
457 ) -> Option<VertexAttributeValues> {
458 self.attributes
459 .as_mut()
460 .expect(MESH_EXTRACTED_ERROR)
461 .remove(&attribute.into())
462 .map(|data| data.values)
463 }
464
465 /// Removes the data for a vertex attribute
466 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
467 /// if the attribute does not exist.
468 pub fn try_remove_attribute(
469 &mut self,
470 attribute: impl Into<MeshVertexAttributeId>,
471 ) -> Result<VertexAttributeValues, MeshAccessError> {
472 Ok(self
473 .attributes
474 .as_mut()?
475 .remove(&attribute.into())
476 .ok_or(MeshAccessError::NotFound)?
477 .values)
478 }
479
480 /// Consumes the mesh and returns a mesh without the data for a vertex attribute
481 ///
482 /// (Alternatively, you can use [`Mesh::remove_attribute`] to mutate an existing mesh in-place)
483 ///
484 /// # Panics
485 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
486 /// this as an error use [`Mesh::try_with_removed_attribute`]
487 #[must_use]
488 pub fn with_removed_attribute(mut self, attribute: impl Into<MeshVertexAttributeId>) -> Self {
489 self.remove_attribute(attribute);
490 self
491 }
492
493 /// Consumes the mesh and returns a mesh without the data for a vertex attribute
494 ///
495 /// (Alternatively, you can use [`Mesh::remove_attribute`] to mutate an existing mesh in-place)
496 ///
497 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
498 /// if the attribute does not exist.
499 pub fn try_with_removed_attribute(
500 mut self,
501 attribute: impl Into<MeshVertexAttributeId>,
502 ) -> Result<Self, MeshAccessError> {
503 self.try_remove_attribute(attribute)?;
504 Ok(self)
505 }
506
507 /// Returns a bool indicating if the attribute is present in this mesh's vertex data.
508 ///
509 /// # Panics
510 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
511 /// this as an error use [`Mesh::try_contains_attribute`]
512 #[inline]
513 pub fn contains_attribute(&self, id: impl Into<MeshVertexAttributeId>) -> bool {
514 self.attributes
515 .as_ref()
516 .expect(MESH_EXTRACTED_ERROR)
517 .contains_key(&id.into())
518 }
519
520 /// Returns a bool indicating if the attribute is present in this mesh's vertex data.
521 ///
522 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
523 #[inline]
524 pub fn try_contains_attribute(
525 &self,
526 id: impl Into<MeshVertexAttributeId>,
527 ) -> Result<bool, MeshAccessError> {
528 Ok(self.attributes.as_ref()?.contains_key(&id.into()))
529 }
530
531 /// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].
532 ///
533 /// # Panics
534 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
535 /// this as an error use [`Mesh::try_attribute`] or [`Mesh::try_attribute_option`]
536 #[inline]
537 pub fn attribute(
538 &self,
539 id: impl Into<MeshVertexAttributeId>,
540 ) -> Option<&VertexAttributeValues> {
541 self.try_attribute_option(id).expect(MESH_EXTRACTED_ERROR)
542 }
543
544 /// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].
545 ///
546 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
547 /// if the attribute does not exist.
548 #[inline]
549 pub fn try_attribute(
550 &self,
551 id: impl Into<MeshVertexAttributeId>,
552 ) -> Result<&VertexAttributeValues, MeshAccessError> {
553 self.try_attribute_option(id)?
554 .ok_or(MeshAccessError::NotFound)
555 }
556
557 /// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].
558 ///
559 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
560 #[inline]
561 pub fn try_attribute_option(
562 &self,
563 id: impl Into<MeshVertexAttributeId>,
564 ) -> Result<Option<&VertexAttributeValues>, MeshAccessError> {
565 Ok(self
566 .attributes
567 .as_ref()?
568 .get(&id.into())
569 .map(|data| &data.values))
570 }
571
572 /// Retrieves the full data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].
573 #[inline]
574 pub(crate) fn try_attribute_data(
575 &self,
576 id: impl Into<MeshVertexAttributeId>,
577 ) -> Result<Option<&MeshAttributeData>, MeshAccessError> {
578 Ok(self.attributes.as_ref()?.get(&id.into()))
579 }
580
581 /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.
582 ///
583 /// # Panics
584 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
585 /// this as an error use [`Mesh::try_attribute_mut`]
586 #[inline]
587 pub fn attribute_mut(
588 &mut self,
589 id: impl Into<MeshVertexAttributeId>,
590 ) -> Option<&mut VertexAttributeValues> {
591 self.try_attribute_mut_option(id)
592 .expect(MESH_EXTRACTED_ERROR)
593 }
594
595 /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.
596 ///
597 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
598 /// if the attribute does not exist.
599 #[inline]
600 pub fn try_attribute_mut(
601 &mut self,
602 id: impl Into<MeshVertexAttributeId>,
603 ) -> Result<&mut VertexAttributeValues, MeshAccessError> {
604 self.try_attribute_mut_option(id)?
605 .ok_or(MeshAccessError::NotFound)
606 }
607
608 /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.
609 ///
610 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
611 #[inline]
612 pub fn try_attribute_mut_option(
613 &mut self,
614 id: impl Into<MeshVertexAttributeId>,
615 ) -> Result<Option<&mut VertexAttributeValues>, MeshAccessError> {
616 Ok(self
617 .attributes
618 .as_mut()?
619 .get_mut(&id.into())
620 .map(|data| &mut data.values))
621 }
622
623 /// Returns an iterator that yields references to the data of each vertex attribute.
624 ///
625 /// # Panics
626 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
627 /// this as an error use [`Mesh::try_attributes`]
628 pub fn attributes(
629 &self,
630 ) -> impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)> {
631 self.try_attributes().expect(MESH_EXTRACTED_ERROR)
632 }
633
634 /// Returns an iterator that yields references to the data of each vertex attribute.
635 /// Returns an error if data has been extracted to `RenderWorld`
636 pub fn try_attributes(
637 &self,
638 ) -> Result<impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)>, MeshAccessError>
639 {
640 Ok(self
641 .attributes
642 .as_ref()?
643 .values()
644 .map(|data| (&data.attribute, &data.values)))
645 }
646
647 /// Returns an iterator that yields mutable references to the data of each vertex attribute.
648 ///
649 /// # Panics
650 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
651 /// this as an error use [`Mesh::try_attributes_mut`]
652 pub fn attributes_mut(
653 &mut self,
654 ) -> impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)> {
655 self.try_attributes_mut().expect(MESH_EXTRACTED_ERROR)
656 }
657
658 /// Returns an iterator that yields mutable references to the data of each vertex attribute.
659 ///
660 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
661 pub fn try_attributes_mut(
662 &mut self,
663 ) -> Result<
664 impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)>,
665 MeshAccessError,
666 > {
667 Ok(self
668 .attributes
669 .as_mut()?
670 .values_mut()
671 .map(|data| (&data.attribute, &mut data.values)))
672 }
673
674 /// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the
675 /// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants
676 /// that use triangles.
677 ///
678 /// # Panics
679 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
680 /// this as an error use [`Mesh::try_insert_indices`]
681 #[inline]
682 pub fn insert_indices(&mut self, indices: Indices) {
683 self.indices
684 .replace(Some(indices))
685 .expect(MESH_EXTRACTED_ERROR);
686 }
687
688 /// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the
689 /// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants
690 /// that use triangles.
691 ///
692 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
693 #[inline]
694 pub fn try_insert_indices(&mut self, indices: Indices) -> Result<(), MeshAccessError> {
695 self.indices.replace(Some(indices))?;
696 Ok(())
697 }
698
699 /// Consumes the mesh and returns a mesh with the given vertex indices. They describe how triangles
700 /// are constructed out of the vertex attributes and are therefore only useful for the
701 /// [`PrimitiveTopology`] variants that use triangles.
702 ///
703 /// (Alternatively, you can use [`Mesh::insert_indices`] to mutate an existing mesh in-place)
704 ///
705 /// # Panics
706 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
707 /// this as an error use [`Mesh::try_with_inserted_indices`]
708 #[must_use]
709 #[inline]
710 pub fn with_inserted_indices(mut self, indices: Indices) -> Self {
711 self.insert_indices(indices);
712 self
713 }
714
715 /// Consumes the mesh and returns a mesh with the given vertex indices. They describe how triangles
716 /// are constructed out of the vertex attributes and are therefore only useful for the
717 /// [`PrimitiveTopology`] variants that use triangles.
718 ///
719 /// (Alternatively, you can use [`Mesh::try_insert_indices`] to mutate an existing mesh in-place)
720 ///
721 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
722 #[inline]
723 pub fn try_with_inserted_indices(mut self, indices: Indices) -> Result<Self, MeshAccessError> {
724 self.try_insert_indices(indices)?;
725 Ok(self)
726 }
727
728 /// Retrieves the vertex `indices` of the mesh, returns None if not found.
729 ///
730 /// # Panics
731 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
732 /// this as an error use [`Mesh::try_indices`]
733 #[inline]
734 pub fn indices(&self) -> Option<&Indices> {
735 self.indices.as_ref_option().expect(MESH_EXTRACTED_ERROR)
736 }
737
738 /// Retrieves the vertex `indices` of the mesh.
739 ///
740 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
741 /// if the attribute does not exist.
742 #[inline]
743 pub fn try_indices(&self) -> Result<&Indices, MeshAccessError> {
744 self.indices.as_ref()
745 }
746
747 /// Retrieves the vertex `indices` of the mesh, returns None if not found.
748 ///
749 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
750 #[inline]
751 pub fn try_indices_option(&self) -> Result<Option<&Indices>, MeshAccessError> {
752 self.indices.as_ref_option()
753 }
754
755 /// Retrieves the vertex `indices` of the mesh mutably.
756 #[inline]
757 pub fn indices_mut(&mut self) -> Option<&mut Indices> {
758 self.try_indices_mut_option().expect(MESH_EXTRACTED_ERROR)
759 }
760
761 /// Retrieves the vertex `indices` of the mesh mutably.
762 ///
763 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
764 #[inline]
765 pub fn try_indices_mut(&mut self) -> Result<&mut Indices, MeshAccessError> {
766 self.indices.as_mut()
767 }
768
769 /// Retrieves the vertex `indices` of the mesh mutably.
770 ///
771 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
772 #[inline]
773 pub fn try_indices_mut_option(&mut self) -> Result<Option<&mut Indices>, MeshAccessError> {
774 self.indices.as_mut_option()
775 }
776
777 /// Removes the vertex `indices` from the mesh and returns them.
778 ///
779 /// # Panics
780 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
781 /// this as an error use [`Mesh::try_remove_indices`]
782 #[inline]
783 pub fn remove_indices(&mut self) -> Option<Indices> {
784 self.try_remove_indices().expect(MESH_EXTRACTED_ERROR)
785 }
786
787 /// Removes the vertex `indices` from the mesh and returns them.
788 ///
789 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
790 #[inline]
791 pub fn try_remove_indices(&mut self) -> Result<Option<Indices>, MeshAccessError> {
792 self.indices.replace(None)
793 }
794
795 /// Consumes the mesh and returns a mesh without the vertex `indices` of the mesh.
796 ///
797 /// (Alternatively, you can use [`Mesh::remove_indices`] to mutate an existing mesh in-place)
798 ///
799 /// # Panics
800 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
801 /// this as an error use [`Mesh::try_with_removed_indices`]
802 #[must_use]
803 pub fn with_removed_indices(mut self) -> Self {
804 self.remove_indices();
805 self
806 }
807
808 /// Consumes the mesh and returns a mesh without the vertex `indices` of the mesh.
809 ///
810 /// (Alternatively, you can use [`Mesh::try_remove_indices`] to mutate an existing mesh in-place)
811 ///
812 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
813 pub fn try_with_removed_indices(mut self) -> Result<Self, MeshAccessError> {
814 self.try_remove_indices()?;
815 Ok(self)
816 }
817
818 /// Returns the size of a vertex in bytes.
819 ///
820 /// # Panics
821 /// Panics when the mesh data has already been extracted to `RenderWorld`.
822 pub fn get_vertex_size(&self) -> u64 {
823 self.attributes
824 .as_ref()
825 .expect(MESH_EXTRACTED_ERROR)
826 .values()
827 .map(|data| data.attribute.format.size())
828 .sum()
829 }
830
831 /// Returns the size required for the vertex buffer in bytes.
832 ///
833 /// # Panics
834 /// Panics when the mesh data has already been extracted to `RenderWorld`.
835 pub fn get_vertex_buffer_size(&self) -> usize {
836 let vertex_size = self.get_vertex_size() as usize;
837 let vertex_count = self.count_vertices();
838 vertex_count * vertex_size
839 }
840
841 /// Computes and returns the index data of the mesh as bytes.
842 /// This is used to transform the index data into a GPU friendly format.
843 ///
844 /// # Panics
845 /// Panics when the mesh data has already been extracted to `RenderWorld`.
846 pub fn get_index_buffer_bytes(&self) -> Option<&[u8]> {
847 let mesh_indices = self.indices.as_ref_option().expect(MESH_EXTRACTED_ERROR);
848
849 mesh_indices.as_ref().map(|indices| match &indices {
850 Indices::U16(indices) => cast_slice(&indices[..]),
851 Indices::U32(indices) => cast_slice(&indices[..]),
852 })
853 }
854
855 /// Get this `Mesh`'s [`MeshVertexBufferLayout`], used in `SpecializedMeshPipeline`.
856 ///
857 /// # Panics
858 /// Panics when the mesh data has already been extracted to `RenderWorld`.
859 pub fn get_mesh_vertex_buffer_layout(
860 &self,
861 mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,
862 ) -> MeshVertexBufferLayoutRef {
863 let mesh_attributes = self.attributes.as_ref().expect(MESH_EXTRACTED_ERROR);
864
865 let mut attributes = Vec::with_capacity(mesh_attributes.len());
866 let mut attribute_ids = Vec::with_capacity(mesh_attributes.len());
867 let mut accumulated_offset = 0;
868 for (index, data) in mesh_attributes.values().enumerate() {
869 attribute_ids.push(data.attribute.id);
870 attributes.push(VertexAttribute {
871 offset: accumulated_offset,
872 format: data.attribute.format,
873 shader_location: index as u32,
874 });
875 accumulated_offset += data.attribute.format.size();
876 }
877
878 let layout = MeshVertexBufferLayout {
879 layout: VertexBufferLayout {
880 array_stride: accumulated_offset,
881 step_mode: VertexStepMode::Vertex,
882 attributes,
883 },
884 attribute_ids,
885 };
886 mesh_vertex_buffer_layouts.insert(layout)
887 }
888
889 /// Counts all vertices of the mesh.
890 ///
891 /// If the attributes have different vertex counts, the smallest is returned.
892 ///
893 /// # Panics
894 /// Panics when the mesh data has already been extracted to `RenderWorld`.
895 pub fn count_vertices(&self) -> usize {
896 let mut vertex_count: Option<usize> = None;
897 let mesh_attributes = self.attributes.as_ref().expect(MESH_EXTRACTED_ERROR);
898
899 for (attribute_id, attribute_data) in mesh_attributes {
900 let attribute_len = attribute_data.values.len();
901 if let Some(previous_vertex_count) = vertex_count {
902 if previous_vertex_count != attribute_len {
903 let name = mesh_attributes
904 .get(attribute_id)
905 .map(|data| data.attribute.name.to_string())
906 .unwrap_or_else(|| format!("{attribute_id:?}"));
907
908 warn!("{name} has a different vertex count ({attribute_len}) than other attributes ({previous_vertex_count}) in this mesh, \
909 all attributes will be truncated to match the smallest.");
910 vertex_count = Some(core::cmp::min(previous_vertex_count, attribute_len));
911 }
912 } else {
913 vertex_count = Some(attribute_len);
914 }
915 }
916
917 vertex_count.unwrap_or(0)
918 }
919
920 /// Computes and returns the vertex data of the mesh as bytes.
921 /// Therefore the attributes are located in the order of their [`MeshVertexAttribute::id`].
922 /// This is used to transform the vertex data into a GPU friendly format.
923 ///
924 /// If the vertex attributes have different lengths, they are all truncated to
925 /// the length of the smallest.
926 ///
927 /// This is a convenience method which allocates a Vec.
928 /// Prefer pre-allocating and using [`Mesh::write_packed_vertex_buffer_data`] when possible.
929 ///
930 /// # Panics
931 /// Panics when the mesh data has already been extracted to `RenderWorld`.
932 pub fn create_packed_vertex_buffer_data(&self) -> Vec<u8> {
933 let mut attributes_interleaved_buffer = vec![0; self.get_vertex_buffer_size()];
934 self.write_packed_vertex_buffer_data(&mut attributes_interleaved_buffer);
935 attributes_interleaved_buffer
936 }
937
938 /// Computes and write the vertex data of the mesh into a mutable byte slice.
939 /// The attributes are located in the order of their [`MeshVertexAttribute::id`].
940 /// This is used to transform the vertex data into a GPU friendly format.
941 ///
942 /// If the vertex attributes have different lengths, they are all truncated to
943 /// the length of the smallest.
944 ///
945 /// # Panics
946 /// Panics when the mesh data has already been extracted to `RenderWorld`.
947 pub fn write_packed_vertex_buffer_data(&self, slice: &mut [u8]) {
948 let mesh_attributes = self.attributes.as_ref().expect(MESH_EXTRACTED_ERROR);
949
950 let vertex_size = self.get_vertex_size() as usize;
951 let vertex_count = self.count_vertices();
952 // bundle into interleaved buffers
953 let mut attribute_offset = 0;
954 for attribute_data in mesh_attributes.values() {
955 let attribute_size = attribute_data.attribute.format.size() as usize;
956 let attributes_bytes = attribute_data.values.get_bytes();
957 for (vertex_index, attribute_bytes) in attributes_bytes
958 .chunks_exact(attribute_size)
959 .take(vertex_count)
960 .enumerate()
961 {
962 let offset = vertex_index * vertex_size + attribute_offset;
963 slice[offset..offset + attribute_size].copy_from_slice(attribute_bytes);
964 }
965
966 attribute_offset += attribute_size;
967 }
968 }
969
970 /// Duplicates the vertex attributes so that no vertices are shared.
971 ///
972 /// This can dramatically increase the vertex count, so make sure this is what you want.
973 /// Does nothing if no [Indices] are set.
974 ///
975 /// # Panics
976 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
977 /// this as an error use [`Mesh::try_duplicate_vertices`]
978 pub fn duplicate_vertices(&mut self) {
979 self.try_duplicate_vertices().expect(MESH_EXTRACTED_ERROR);
980 }
981
982 /// Duplicates the vertex attributes so that no vertices are shared.
983 ///
984 /// This can dramatically increase the vertex count, so make sure this is what you want.
985 /// Does nothing if no [Indices] are set.
986 ///
987 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
988 pub fn try_duplicate_vertices(&mut self) -> Result<(), MeshAccessError> {
989 fn duplicate<T: Copy>(values: &[T], indices: impl Iterator<Item = usize>) -> Vec<T> {
990 indices.map(|i| values[i]).collect()
991 }
992
993 let Some(indices) = self.indices.replace(None)? else {
994 return Ok(());
995 };
996
997 let mesh_attributes = self.attributes.as_mut()?;
998
999 for attributes in mesh_attributes.values_mut() {
1000 let indices = indices.iter();
1001 #[expect(
1002 clippy::match_same_arms,
1003 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."
1004 )]
1005 match &mut attributes.values {
1006 VertexAttributeValues::Float32(vec) => *vec = duplicate(vec, indices),
1007 VertexAttributeValues::Sint32(vec) => *vec = duplicate(vec, indices),
1008 VertexAttributeValues::Uint32(vec) => *vec = duplicate(vec, indices),
1009 VertexAttributeValues::Float32x2(vec) => *vec = duplicate(vec, indices),
1010 VertexAttributeValues::Sint32x2(vec) => *vec = duplicate(vec, indices),
1011 VertexAttributeValues::Uint32x2(vec) => *vec = duplicate(vec, indices),
1012 VertexAttributeValues::Float32x3(vec) => *vec = duplicate(vec, indices),
1013 VertexAttributeValues::Sint32x3(vec) => *vec = duplicate(vec, indices),
1014 VertexAttributeValues::Uint32x3(vec) => *vec = duplicate(vec, indices),
1015 VertexAttributeValues::Sint32x4(vec) => *vec = duplicate(vec, indices),
1016 VertexAttributeValues::Uint32x4(vec) => *vec = duplicate(vec, indices),
1017 VertexAttributeValues::Float32x4(vec) => *vec = duplicate(vec, indices),
1018 VertexAttributeValues::Sint16x2(vec) => *vec = duplicate(vec, indices),
1019 VertexAttributeValues::Snorm16x2(vec) => *vec = duplicate(vec, indices),
1020 VertexAttributeValues::Uint16x2(vec) => *vec = duplicate(vec, indices),
1021 VertexAttributeValues::Unorm16x2(vec) => *vec = duplicate(vec, indices),
1022 VertexAttributeValues::Sint16x4(vec) => *vec = duplicate(vec, indices),
1023 VertexAttributeValues::Snorm16x4(vec) => *vec = duplicate(vec, indices),
1024 VertexAttributeValues::Uint16x4(vec) => *vec = duplicate(vec, indices),
1025 VertexAttributeValues::Unorm16x4(vec) => *vec = duplicate(vec, indices),
1026 VertexAttributeValues::Sint8x2(vec) => *vec = duplicate(vec, indices),
1027 VertexAttributeValues::Snorm8x2(vec) => *vec = duplicate(vec, indices),
1028 VertexAttributeValues::Uint8x2(vec) => *vec = duplicate(vec, indices),
1029 VertexAttributeValues::Unorm8x2(vec) => *vec = duplicate(vec, indices),
1030 VertexAttributeValues::Sint8x4(vec) => *vec = duplicate(vec, indices),
1031 VertexAttributeValues::Snorm8x4(vec) => *vec = duplicate(vec, indices),
1032 VertexAttributeValues::Uint8x4(vec) => *vec = duplicate(vec, indices),
1033 VertexAttributeValues::Unorm8x4(vec) => *vec = duplicate(vec, indices),
1034 }
1035 }
1036
1037 Ok(())
1038 }
1039
1040 /// Consumes the mesh and returns a mesh with no shared vertices.
1041 ///
1042 /// This can dramatically increase the vertex count, so make sure this is what you want.
1043 /// Does nothing if no [`Indices`] are set.
1044 ///
1045 /// (Alternatively, you can use [`Mesh::duplicate_vertices`] to mutate an existing mesh in-place)
1046 ///
1047 /// # Panics
1048 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1049 /// this as an error use [`Mesh::try_with_duplicated_vertices`]
1050 #[must_use]
1051 pub fn with_duplicated_vertices(mut self) -> Self {
1052 self.duplicate_vertices();
1053 self
1054 }
1055
1056 /// Consumes the mesh and returns a mesh with no shared vertices.
1057 ///
1058 /// This can dramatically increase the vertex count, so make sure this is what you want.
1059 /// Does nothing if no [`Indices`] are set.
1060 ///
1061 /// (Alternatively, you can use [`Mesh::try_duplicate_vertices`] to mutate an existing mesh in-place)
1062 ///
1063 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
1064 pub fn try_with_duplicated_vertices(mut self) -> Result<Self, MeshAccessError> {
1065 self.try_duplicate_vertices()?;
1066 Ok(self)
1067 }
1068
1069 /// Inverts the winding of the indices such that all counter-clockwise triangles are now
1070 /// clockwise and vice versa.
1071 /// For lines, their start and end indices are flipped.
1072 ///
1073 /// Does nothing if no [`Indices`] are set.
1074 /// If this operation succeeded, an [`Ok`] result is returned.
1075 pub fn invert_winding(&mut self) -> Result<(), MeshWindingInvertError> {
1076 fn invert<I>(
1077 indices: &mut [I],
1078 topology: PrimitiveTopology,
1079 ) -> Result<(), MeshWindingInvertError> {
1080 match topology {
1081 PrimitiveTopology::TriangleList => {
1082 // Early return if the index count doesn't match
1083 if !indices.len().is_multiple_of(3) {
1084 return Err(MeshWindingInvertError::AbruptIndicesEnd);
1085 }
1086 for chunk in indices.chunks_mut(3) {
1087 // This currently can only be optimized away with unsafe, rework this when `feature(slice_as_chunks)` gets stable.
1088 let [_, b, c] = chunk else {
1089 return Err(MeshWindingInvertError::AbruptIndicesEnd);
1090 };
1091 core::mem::swap(b, c);
1092 }
1093 Ok(())
1094 }
1095 PrimitiveTopology::LineList => {
1096 // Early return if the index count doesn't match
1097 if !indices.len().is_multiple_of(2) {
1098 return Err(MeshWindingInvertError::AbruptIndicesEnd);
1099 }
1100 indices.reverse();
1101 Ok(())
1102 }
1103 PrimitiveTopology::TriangleStrip | PrimitiveTopology::LineStrip => {
1104 indices.reverse();
1105 Ok(())
1106 }
1107 _ => Err(MeshWindingInvertError::WrongTopology),
1108 }
1109 }
1110
1111 let mesh_indices = self.indices.as_mut_option()?;
1112
1113 match mesh_indices {
1114 Some(Indices::U16(vec)) => invert(vec, self.primitive_topology),
1115 Some(Indices::U32(vec)) => invert(vec, self.primitive_topology),
1116 None => Ok(()),
1117 }
1118 }
1119
1120 /// Consumes the mesh and returns a mesh with inverted winding of the indices such
1121 /// that all counter-clockwise triangles are now clockwise and vice versa.
1122 ///
1123 /// Does nothing if no [`Indices`] are set.
1124 pub fn with_inverted_winding(mut self) -> Result<Self, MeshWindingInvertError> {
1125 self.invert_winding().map(|_| self)
1126 }
1127
1128 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
1129 /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
1130 /// normals.
1131 ///
1132 /// # Panics
1133 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1134 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].=
1135 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1136 /// this as an error use [`Mesh::try_compute_normals`]
1137 pub fn compute_normals(&mut self) {
1138 self.try_compute_normals().expect(MESH_EXTRACTED_ERROR);
1139 }
1140
1141 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
1142 /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
1143 /// normals.
1144 ///
1145 /// # Panics
1146 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1147 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].=
1148 pub fn try_compute_normals(&mut self) -> Result<(), MeshAccessError> {
1149 assert!(
1150 matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
1151 "`compute_normals` can only work on `TriangleList`s"
1152 );
1153 if self.try_indices_option()?.is_none() {
1154 self.try_compute_flat_normals()
1155 } else {
1156 self.try_compute_smooth_normals()
1157 }
1158 }
1159
1160 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
1161 ///
1162 /// # Panics
1163 /// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1164 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1165 /// Consider calling [`Mesh::duplicate_vertices`] or exporting your mesh with normal
1166 /// attributes.
1167 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1168 /// this as an error use [`Mesh::try_compute_flat_normals`]
1169 ///
1170 /// FIXME: This should handle more cases since this is called as a part of gltf
1171 /// mesh loading where we can't really blame users for loading meshes that might
1172 /// not conform to the limitations here!
1173 pub fn compute_flat_normals(&mut self) {
1174 self.try_compute_flat_normals().expect(MESH_EXTRACTED_ERROR);
1175 }
1176
1177 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
1178 ///
1179 /// # Panics
1180 /// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1181 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1182 /// Consider calling [`Mesh::duplicate_vertices`] or exporting your mesh with normal
1183 /// attributes.
1184 ///
1185 /// FIXME: This should handle more cases since this is called as a part of gltf
1186 /// mesh loading where we can't really blame users for loading meshes that might
1187 /// not conform to the limitations here!
1188 pub fn try_compute_flat_normals(&mut self) -> Result<(), MeshAccessError> {
1189 assert!(
1190 self.try_indices_option()?.is_none(),
1191 "`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`."
1192 );
1193 assert!(
1194 matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
1195 "`compute_flat_normals` can only work on `TriangleList`s"
1196 );
1197
1198 let positions = self
1199 .try_attribute(Mesh::ATTRIBUTE_POSITION)?
1200 .as_float3()
1201 .expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");
1202
1203 let normals: Vec<_> = positions
1204 .chunks_exact(3)
1205 .map(|p| triangle_normal(p[0], p[1], p[2]))
1206 .flat_map(|normal| [normal; 3])
1207 .collect();
1208
1209 self.try_insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1210 }
1211
1212 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1213 /// vertices.
1214 ///
1215 /// This method weights normals by the angles of the corners of connected triangles, thus
1216 /// eliminating triangle area and count as factors in the final normal. This does make it
1217 /// somewhat slower than [`Mesh::compute_area_weighted_normals`] which does not need to
1218 /// greedily normalize each triangle's normal or calculate corner angles.
1219 ///
1220 /// If you would rather have the computed normals be weighted by triangle area, see
1221 /// [`Mesh::compute_area_weighted_normals`] instead. If you need to weight them in some other
1222 /// way, see [`Mesh::compute_custom_smooth_normals`].
1223 ///
1224 /// # Panics
1225 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1226 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1227 /// Panics if the mesh does not have indices defined.
1228 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1229 /// this as an error use [`Mesh::try_compute_smooth_normals`]
1230 pub fn compute_smooth_normals(&mut self) {
1231 self.try_compute_smooth_normals()
1232 .expect(MESH_EXTRACTED_ERROR);
1233 }
1234
1235 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1236 /// vertices.
1237 ///
1238 /// This method weights normals by the angles of the corners of connected triangles, thus
1239 /// eliminating triangle area and count as factors in the final normal. This does make it
1240 /// somewhat slower than [`Mesh::compute_area_weighted_normals`] which does not need to
1241 /// greedily normalize each triangle's normal or calculate corner angles.
1242 ///
1243 /// If you would rather have the computed normals be weighted by triangle area, see
1244 /// [`Mesh::compute_area_weighted_normals`] instead. If you need to weight them in some other
1245 /// way, see [`Mesh::compute_custom_smooth_normals`].
1246 ///
1247 /// # Panics
1248 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1249 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1250 /// Panics if the mesh does not have indices defined.
1251 pub fn try_compute_smooth_normals(&mut self) -> Result<(), MeshAccessError> {
1252 self.try_compute_custom_smooth_normals(|[a, b, c], positions, normals| {
1253 let pa = Vec3::from(positions[a]);
1254 let pb = Vec3::from(positions[b]);
1255 let pc = Vec3::from(positions[c]);
1256
1257 let ab = pb - pa;
1258 let ba = pa - pb;
1259 let bc = pc - pb;
1260 let cb = pb - pc;
1261 let ca = pa - pc;
1262 let ac = pc - pa;
1263
1264 const EPS: f32 = f32::EPSILON;
1265 let weight_a = if ab.length_squared() * ac.length_squared() > EPS {
1266 ab.angle_between(ac)
1267 } else {
1268 0.0
1269 };
1270 let weight_b = if ba.length_squared() * bc.length_squared() > EPS {
1271 ba.angle_between(bc)
1272 } else {
1273 0.0
1274 };
1275 let weight_c = if ca.length_squared() * cb.length_squared() > EPS {
1276 ca.angle_between(cb)
1277 } else {
1278 0.0
1279 };
1280
1281 let normal = Vec3::from(triangle_normal(positions[a], positions[b], positions[c]));
1282
1283 normals[a] += normal * weight_a;
1284 normals[b] += normal * weight_b;
1285 normals[c] += normal * weight_c;
1286 })
1287 }
1288
1289 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1290 /// vertices.
1291 ///
1292 /// This method weights normals by the area of each triangle containing the vertex. Thus,
1293 /// larger triangles will skew the normals of their vertices towards their own normal more
1294 /// than smaller triangles will.
1295 ///
1296 /// This method is actually somewhat faster than [`Mesh::compute_smooth_normals`] because an
1297 /// intermediate result of triangle normal calculation is already scaled by the triangle's area.
1298 ///
1299 /// If you would rather have the computed normals be influenced only by the angles of connected
1300 /// edges, see [`Mesh::compute_smooth_normals`] instead. If you need to weight them in some
1301 /// other way, see [`Mesh::compute_custom_smooth_normals`].
1302 ///
1303 /// # Panics
1304 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1305 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1306 /// Panics if the mesh does not have indices defined.
1307 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1308 /// this as an error use [`Mesh::try_compute_area_weighted_normals`]
1309 pub fn compute_area_weighted_normals(&mut self) {
1310 self.try_compute_area_weighted_normals()
1311 .expect(MESH_EXTRACTED_ERROR);
1312 }
1313
1314 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1315 /// vertices.
1316 ///
1317 /// This method weights normals by the area of each triangle containing the vertex. Thus,
1318 /// larger triangles will skew the normals of their vertices towards their own normal more
1319 /// than smaller triangles will.
1320 ///
1321 /// This method is actually somewhat faster than [`Mesh::compute_smooth_normals`] because an
1322 /// intermediate result of triangle normal calculation is already scaled by the triangle's area.
1323 ///
1324 /// If you would rather have the computed normals be influenced only by the angles of connected
1325 /// edges, see [`Mesh::compute_smooth_normals`] instead. If you need to weight them in some
1326 /// other way, see [`Mesh::compute_custom_smooth_normals`].
1327 ///
1328 /// # Panics
1329 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1330 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1331 /// Panics if the mesh does not have indices defined.
1332 pub fn try_compute_area_weighted_normals(&mut self) -> Result<(), MeshAccessError> {
1333 self.try_compute_custom_smooth_normals(|[a, b, c], positions, normals| {
1334 let normal = Vec3::from(triangle_area_normal(
1335 positions[a],
1336 positions[b],
1337 positions[c],
1338 ));
1339 [a, b, c].into_iter().for_each(|pos| {
1340 normals[pos] += normal;
1341 });
1342 })
1343 }
1344
1345 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1346 /// vertices.
1347 ///
1348 /// This method allows you to customize how normals are weighted via the `per_triangle` parameter,
1349 /// which must be a function or closure that accepts 3 parameters:
1350 /// - The indices of the three vertices of the triangle as a `[usize; 3]`.
1351 /// - A reference to the values of the [`Mesh::ATTRIBUTE_POSITION`] of the mesh (`&[[f32; 3]]`).
1352 /// - A mutable reference to the sums of all normals so far.
1353 ///
1354 /// See also the standard methods included in Bevy for calculating smooth normals:
1355 /// - [`Mesh::compute_smooth_normals`]
1356 /// - [`Mesh::compute_area_weighted_normals`]
1357 ///
1358 /// An example that would weight each connected triangle's normal equally, thus skewing normals
1359 /// towards the planes divided into the most triangles:
1360 /// ```
1361 /// # use bevy_asset::RenderAssetUsages;
1362 /// # use bevy_mesh::{Mesh, PrimitiveTopology, Meshable, MeshBuilder};
1363 /// # use bevy_math::{Vec3, primitives::Cuboid};
1364 /// # let mut mesh = Cuboid::default().mesh().build();
1365 /// mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| {
1366 /// let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c]));
1367 /// for idx in [a, b, c] {
1368 /// normals[idx] += normal;
1369 /// }
1370 /// });
1371 /// ```
1372 ///
1373 /// # Panics
1374 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1375 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1376 /// Panics if the mesh does not have indices defined.
1377 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1378 /// this as an error use [`Mesh::try_compute_custom_smooth_normals`]
1379 //
1380 // FIXME: This should handle more cases since this is called as a part of gltf
1381 // mesh loading where we can't really blame users for loading meshes that might
1382 // not conform to the limitations here!
1383 //
1384 // When fixed, also update "Panics" sections of
1385 // - [Mesh::compute_smooth_normals]
1386 // - [Mesh::with_computed_smooth_normals]
1387 // - [Mesh::compute_area_weighted_normals]
1388 // - [Mesh::with_computed_area_weighted_normals]
1389 pub fn compute_custom_smooth_normals(
1390 &mut self,
1391 per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]),
1392 ) {
1393 self.try_compute_custom_smooth_normals(per_triangle)
1394 .expect(MESH_EXTRACTED_ERROR);
1395 }
1396
1397 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
1398 /// vertices.
1399 ///
1400 /// This method allows you to customize how normals are weighted via the `per_triangle` parameter,
1401 /// which must be a function or closure that accepts 3 parameters:
1402 /// - The indices of the three vertices of the triangle as a `[usize; 3]`.
1403 /// - A reference to the values of the [`Mesh::ATTRIBUTE_POSITION`] of the mesh (`&[[f32; 3]]`).
1404 /// - A mutable reference to the sums of all normals so far.
1405 ///
1406 /// See also the standard methods included in Bevy for calculating smooth normals:
1407 /// - [`Mesh::compute_smooth_normals`]
1408 /// - [`Mesh::compute_area_weighted_normals`]
1409 ///
1410 /// An example that would weight each connected triangle's normal equally, thus skewing normals
1411 /// towards the planes divided into the most triangles:
1412 /// ```
1413 /// # use bevy_asset::RenderAssetUsages;
1414 /// # use bevy_mesh::{Mesh, PrimitiveTopology, Meshable, MeshBuilder};
1415 /// # use bevy_math::{Vec3, primitives::Cuboid};
1416 /// # let mut mesh = Cuboid::default().mesh().build();
1417 /// mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| {
1418 /// let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c]));
1419 /// for idx in [a, b, c] {
1420 /// normals[idx] += normal;
1421 /// }
1422 /// });
1423 /// ```
1424 ///
1425 /// # Panics
1426 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1427 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1428 /// Panics if the mesh does not have indices defined.
1429 //
1430 // FIXME: This should handle more cases since this is called as a part of gltf
1431 // mesh loading where we can't really blame users for loading meshes that might
1432 // not conform to the limitations here!
1433 //
1434 // When fixed, also update "Panics" sections of
1435 // - [Mesh::compute_smooth_normals]
1436 // - [Mesh::with_computed_smooth_normals]
1437 // - [Mesh::compute_area_weighted_normals]
1438 // - [Mesh::with_computed_area_weighted_normals]
1439 pub fn try_compute_custom_smooth_normals(
1440 &mut self,
1441 mut per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]),
1442 ) -> Result<(), MeshAccessError> {
1443 assert!(
1444 matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
1445 "smooth normals can only be computed on `TriangleList`s"
1446 );
1447 assert!(
1448 self.try_indices_option()?.is_some(),
1449 "smooth normals can only be computed on indexed meshes"
1450 );
1451
1452 let positions = self
1453 .try_attribute(Mesh::ATTRIBUTE_POSITION)?
1454 .as_float3()
1455 .expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");
1456
1457 let mut normals = vec![Vec3::ZERO; positions.len()];
1458
1459 self.try_indices()?
1460 .iter()
1461 .collect::<Vec<usize>>()
1462 .chunks_exact(3)
1463 .for_each(|face| per_triangle([face[0], face[1], face[2]], positions, &mut normals));
1464
1465 for normal in &mut normals {
1466 *normal = normal.try_normalize().unwrap_or(Vec3::ZERO);
1467 }
1468
1469 self.try_insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1470 }
1471
1472 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
1473 /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
1474 /// normals.
1475 ///
1476 /// (Alternatively, you can use [`Mesh::compute_normals`] to mutate an existing mesh in-place)
1477 ///
1478 /// # Panics
1479 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1480 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1481 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1482 /// this as an error use [`Mesh::try_with_computed_normals`]
1483 #[must_use]
1484 pub fn with_computed_normals(self) -> Self {
1485 self.try_with_computed_normals()
1486 .expect(MESH_EXTRACTED_ERROR)
1487 }
1488
1489 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
1490 /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
1491 /// normals.
1492 ///
1493 /// (Alternatively, you can use [`Mesh::compute_normals`] to mutate an existing mesh in-place)
1494 ///
1495 /// # Panics
1496 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1497 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1498 pub fn try_with_computed_normals(mut self) -> Result<Self, MeshAccessError> {
1499 self.try_compute_normals()?;
1500 Ok(self)
1501 }
1502
1503 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
1504 ///
1505 /// (Alternatively, you can use [`Mesh::compute_flat_normals`] to mutate an existing mesh in-place)
1506 ///
1507 /// # Panics
1508 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1509 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1510 /// Panics if the mesh has indices defined
1511 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1512 /// this as an error use [`Mesh::try_with_computed_flat_normals`]
1513 pub fn with_computed_flat_normals(mut self) -> Self {
1514 self.compute_flat_normals();
1515 self
1516 }
1517
1518 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
1519 ///
1520 /// (Alternatively, you can use [`Mesh::compute_flat_normals`] to mutate an existing mesh in-place)
1521 ///
1522 /// # Panics
1523 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1524 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1525 /// Panics if the mesh has indices defined
1526 pub fn try_with_computed_flat_normals(mut self) -> Result<Self, MeshAccessError> {
1527 self.try_compute_flat_normals()?;
1528 Ok(self)
1529 }
1530
1531 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
1532 ///
1533 /// (Alternatively, you can use [`Mesh::compute_smooth_normals`] to mutate an existing mesh in-place)
1534 ///
1535 /// This method weights normals by the angles of triangle corners connected to each vertex. If
1536 /// you would rather have the computed normals be weighted by triangle area, see
1537 /// [`Mesh::with_computed_area_weighted_normals`] instead.
1538 ///
1539 /// # Panics
1540 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1541 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1542 /// Panics if the mesh does not have indices defined.
1543 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1544 /// this as an error use [`Mesh::try_with_computed_smooth_normals`]
1545 pub fn with_computed_smooth_normals(mut self) -> Self {
1546 self.compute_smooth_normals();
1547 self
1548 }
1549 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
1550 ///
1551 /// (Alternatively, you can use [`Mesh::compute_smooth_normals`] to mutate an existing mesh in-place)
1552 ///
1553 /// This method weights normals by the angles of triangle corners connected to each vertex. If
1554 /// you would rather have the computed normals be weighted by triangle area, see
1555 /// [`Mesh::with_computed_area_weighted_normals`] instead.
1556 ///
1557 /// # Panics
1558 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1559 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1560 /// Panics if the mesh does not have indices defined.
1561 pub fn try_with_computed_smooth_normals(mut self) -> Result<Self, MeshAccessError> {
1562 self.try_compute_smooth_normals()?;
1563 Ok(self)
1564 }
1565
1566 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
1567 ///
1568 /// (Alternatively, you can use [`Mesh::compute_area_weighted_normals`] to mutate an existing mesh in-place)
1569 ///
1570 /// This method weights normals by the area of each triangle containing the vertex. Thus,
1571 /// larger triangles will skew the normals of their vertices towards their own normal more
1572 /// than smaller triangles will. If you would rather have the computed normals be influenced
1573 /// only by the angles of connected edges, see [`Mesh::with_computed_smooth_normals`] instead.
1574 ///
1575 /// # Panics
1576 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1577 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1578 /// Panics if the mesh does not have indices defined.
1579 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1580 /// this as an error use [`Mesh::try_with_computed_area_weighted_normals`]
1581 pub fn with_computed_area_weighted_normals(mut self) -> Self {
1582 self.compute_area_weighted_normals();
1583 self
1584 }
1585
1586 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
1587 ///
1588 /// (Alternatively, you can use [`Mesh::compute_area_weighted_normals`] to mutate an existing mesh in-place)
1589 ///
1590 /// This method weights normals by the area of each triangle containing the vertex. Thus,
1591 /// larger triangles will skew the normals of their vertices towards their own normal more
1592 /// than smaller triangles will. If you would rather have the computed normals be influenced
1593 /// only by the angles of connected edges, see [`Mesh::with_computed_smooth_normals`] instead.
1594 ///
1595 /// # Panics
1596 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
1597 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
1598 /// Panics if the mesh does not have indices defined.
1599 pub fn try_with_computed_area_weighted_normals(mut self) -> Result<Self, MeshAccessError> {
1600 self.try_compute_area_weighted_normals()?;
1601 Ok(self)
1602 }
1603
1604 /// Generate tangents for the mesh using the `mikktspace` algorithm.
1605 ///
1606 /// Sets the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful.
1607 /// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set.
1608 #[cfg(feature = "bevy_mikktspace")]
1609 pub fn generate_tangents(&mut self) -> Result<(), super::GenerateTangentsError> {
1610 let tangents = super::generate_tangents_for_mesh(self)?;
1611 self.try_insert_attribute(Mesh::ATTRIBUTE_TANGENT, tangents)?;
1612 Ok(())
1613 }
1614
1615 /// Consumes the mesh and returns a mesh with tangents generated using the `mikktspace` algorithm.
1616 ///
1617 /// The resulting mesh will have the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful.
1618 ///
1619 /// (Alternatively, you can use [`Mesh::generate_tangents`] to mutate an existing mesh in-place)
1620 ///
1621 /// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set.
1622 #[cfg(feature = "bevy_mikktspace")]
1623 pub fn with_generated_tangents(mut self) -> Result<Mesh, super::GenerateTangentsError> {
1624 self.generate_tangents()?;
1625 Ok(self)
1626 }
1627
1628 /// Merges the [`Mesh`] data of `other` with `self`. The attributes and indices of `other` will be appended to `self`.
1629 ///
1630 /// Note that attributes of `other` that don't exist on `self` will be ignored.
1631 ///
1632 /// `Aabb` of entities with modified mesh are not updated automatically.
1633 ///
1634 /// # Errors
1635 ///
1636 /// If any of the following conditions are not met, this function errors:
1637 /// * All of the vertex attributes that have the same attribute id, must also
1638 /// have the same attribute type.
1639 /// For example two attributes with the same id, but where one is a
1640 /// [`VertexAttributeValues::Float32`] and the other is a
1641 /// [`VertexAttributeValues::Float32x3`], would be invalid.
1642 /// * Both meshes must have the same primitive topology.
1643 pub fn merge(&mut self, other: &Mesh) -> Result<(), MeshMergeError> {
1644 use VertexAttributeValues::*;
1645
1646 // Check if the meshes `primitive_topology` field is the same,
1647 // as if that is not the case, the resulting mesh could (and most likely would)
1648 // be invalid.
1649 if self.primitive_topology != other.primitive_topology {
1650 return Err(MeshMergeError::IncompatiblePrimitiveTopology {
1651 self_primitive_topology: self.primitive_topology,
1652 other_primitive_topology: other.primitive_topology,
1653 });
1654 }
1655
1656 // The indices of `other` should start after the last vertex of `self`.
1657 let index_offset = self.count_vertices();
1658
1659 // Extend attributes of `self` with attributes of `other`.
1660 for (attribute, values) in self.try_attributes_mut()? {
1661 if let Some(other_values) = other.try_attribute_option(attribute.id)? {
1662 #[expect(
1663 clippy::match_same_arms,
1664 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."
1665 )]
1666 match (values, other_values) {
1667 (Float32(vec1), Float32(vec2)) => vec1.extend(vec2),
1668 (Sint32(vec1), Sint32(vec2)) => vec1.extend(vec2),
1669 (Uint32(vec1), Uint32(vec2)) => vec1.extend(vec2),
1670 (Float32x2(vec1), Float32x2(vec2)) => vec1.extend(vec2),
1671 (Sint32x2(vec1), Sint32x2(vec2)) => vec1.extend(vec2),
1672 (Uint32x2(vec1), Uint32x2(vec2)) => vec1.extend(vec2),
1673 (Float32x3(vec1), Float32x3(vec2)) => vec1.extend(vec2),
1674 (Sint32x3(vec1), Sint32x3(vec2)) => vec1.extend(vec2),
1675 (Uint32x3(vec1), Uint32x3(vec2)) => vec1.extend(vec2),
1676 (Sint32x4(vec1), Sint32x4(vec2)) => vec1.extend(vec2),
1677 (Uint32x4(vec1), Uint32x4(vec2)) => vec1.extend(vec2),
1678 (Float32x4(vec1), Float32x4(vec2)) => vec1.extend(vec2),
1679 (Sint16x2(vec1), Sint16x2(vec2)) => vec1.extend(vec2),
1680 (Snorm16x2(vec1), Snorm16x2(vec2)) => vec1.extend(vec2),
1681 (Uint16x2(vec1), Uint16x2(vec2)) => vec1.extend(vec2),
1682 (Unorm16x2(vec1), Unorm16x2(vec2)) => vec1.extend(vec2),
1683 (Sint16x4(vec1), Sint16x4(vec2)) => vec1.extend(vec2),
1684 (Snorm16x4(vec1), Snorm16x4(vec2)) => vec1.extend(vec2),
1685 (Uint16x4(vec1), Uint16x4(vec2)) => vec1.extend(vec2),
1686 (Unorm16x4(vec1), Unorm16x4(vec2)) => vec1.extend(vec2),
1687 (Sint8x2(vec1), Sint8x2(vec2)) => vec1.extend(vec2),
1688 (Snorm8x2(vec1), Snorm8x2(vec2)) => vec1.extend(vec2),
1689 (Uint8x2(vec1), Uint8x2(vec2)) => vec1.extend(vec2),
1690 (Unorm8x2(vec1), Unorm8x2(vec2)) => vec1.extend(vec2),
1691 (Sint8x4(vec1), Sint8x4(vec2)) => vec1.extend(vec2),
1692 (Snorm8x4(vec1), Snorm8x4(vec2)) => vec1.extend(vec2),
1693 (Uint8x4(vec1), Uint8x4(vec2)) => vec1.extend(vec2),
1694 (Unorm8x4(vec1), Unorm8x4(vec2)) => vec1.extend(vec2),
1695 _ => {
1696 return Err(MeshMergeError::IncompatibleVertexAttributes {
1697 self_attribute: *attribute,
1698 other_attribute: other
1699 .try_attribute_data(attribute.id)?
1700 .map(|data| data.attribute),
1701 })
1702 }
1703 }
1704 }
1705 }
1706
1707 // Extend indices of `self` with indices of `other`.
1708 if let (Some(indices), Some(other_indices)) =
1709 (self.try_indices_mut_option()?, other.try_indices_option()?)
1710 {
1711 indices.extend(other_indices.iter().map(|i| (i + index_offset) as u32));
1712 }
1713 Ok(())
1714 }
1715
1716 /// Transforms the vertex positions, normals, and tangents of the mesh by the given [`Transform`].
1717 ///
1718 /// `Aabb` of entities with modified mesh are not updated automatically.
1719 ///
1720 /// # Panics
1721 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1722 /// this as an error use [`Mesh::try_transformed_by`]
1723 pub fn transformed_by(mut self, transform: Transform) -> Self {
1724 self.transform_by(transform);
1725 self
1726 }
1727
1728 /// Transforms the vertex positions, normals, and tangents of the mesh by the given [`Transform`].
1729 ///
1730 /// `Aabb` of entities with modified mesh are not updated automatically.
1731 pub fn try_transformed_by(mut self, transform: Transform) -> Result<Self, MeshAccessError> {
1732 self.try_transform_by(transform)?;
1733 Ok(self)
1734 }
1735
1736 /// Transforms the vertex positions, normals, and tangents of the mesh in place by the given [`Transform`].
1737 ///
1738 /// `Aabb` of entities with modified mesh are not updated automatically.
1739 ///
1740 /// # Panics
1741 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1742 /// this as an error use [`Mesh::try_transform_by`]
1743 pub fn transform_by(&mut self, transform: Transform) {
1744 self.try_transform_by(transform)
1745 .expect(MESH_EXTRACTED_ERROR);
1746 }
1747
1748 /// Transforms the vertex positions, normals, and tangents of the mesh in place by the given [`Transform`].
1749 ///
1750 /// `Aabb` of entities with modified mesh are not updated automatically.
1751 pub fn try_transform_by(&mut self, transform: Transform) -> Result<(), MeshAccessError> {
1752 // Needed when transforming normals and tangents
1753 let scale_recip = 1. / transform.scale;
1754 debug_assert!(
1755 transform.scale.yzx() * transform.scale.zxy() != Vec3::ZERO,
1756 "mesh transform scale cannot be zero on more than one axis"
1757 );
1758
1759 if let Some(VertexAttributeValues::Float32x3(positions)) =
1760 self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?
1761 {
1762 // Apply scale, rotation, and translation to vertex positions
1763 positions
1764 .iter_mut()
1765 .for_each(|pos| *pos = transform.transform_point(Vec3::from_slice(pos)).to_array());
1766 }
1767
1768 // No need to transform normals or tangents if rotation is near identity and scale is uniform
1769 if transform.rotation.is_near_identity()
1770 && transform.scale.x == transform.scale.y
1771 && transform.scale.y == transform.scale.z
1772 {
1773 return Ok(());
1774 }
1775
1776 if let Some(VertexAttributeValues::Float32x3(normals)) =
1777 self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)?
1778 {
1779 // Transform normals, taking into account non-uniform scaling and rotation
1780 normals.iter_mut().for_each(|normal| {
1781 *normal = (transform.rotation
1782 * scale_normal(Vec3::from_array(*normal), scale_recip))
1783 .to_array();
1784 });
1785 }
1786
1787 if let Some(VertexAttributeValues::Float32x4(tangents)) =
1788 self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)?
1789 {
1790 // Transform tangents, taking into account non-uniform scaling and rotation
1791 tangents.iter_mut().for_each(|tangent| {
1792 let handedness = tangent[3];
1793 let scaled_tangent = Vec3::from_slice(tangent) * transform.scale;
1794 *tangent = (transform.rotation * scaled_tangent.normalize_or_zero())
1795 .extend(handedness)
1796 .to_array();
1797 });
1798 }
1799
1800 Ok(())
1801 }
1802
1803 /// Translates the vertex positions of the mesh by the given [`Vec3`].
1804 ///
1805 /// `Aabb` of entities with modified mesh are not updated automatically.
1806 ///
1807 /// # Panics
1808 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1809 /// this as an error use [`Mesh::try_translated_by`]
1810 pub fn translated_by(mut self, translation: Vec3) -> Self {
1811 self.translate_by(translation);
1812 self
1813 }
1814
1815 /// Translates the vertex positions of the mesh by the given [`Vec3`].
1816 ///
1817 /// `Aabb` of entities with modified mesh are not updated automatically.
1818 pub fn try_translated_by(mut self, translation: Vec3) -> Result<Self, MeshAccessError> {
1819 self.try_translate_by(translation)?;
1820 Ok(self)
1821 }
1822
1823 /// Translates the vertex positions of the mesh in place by the given [`Vec3`].
1824 ///
1825 /// `Aabb` of entities with modified mesh are not updated automatically.
1826 ///
1827 /// # Panics
1828 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1829 /// this as an error use [`Mesh::try_translate_by`]
1830 pub fn translate_by(&mut self, translation: Vec3) {
1831 self.try_translate_by(translation)
1832 .expect(MESH_EXTRACTED_ERROR);
1833 }
1834
1835 /// Translates the vertex positions of the mesh in place by the given [`Vec3`].
1836 ///
1837 /// `Aabb` of entities with modified mesh are not updated automatically.
1838 pub fn try_translate_by(&mut self, translation: Vec3) -> Result<(), MeshAccessError> {
1839 if translation == Vec3::ZERO {
1840 return Ok(());
1841 }
1842
1843 if let Some(VertexAttributeValues::Float32x3(positions)) =
1844 self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?
1845 {
1846 // Apply translation to vertex positions
1847 positions
1848 .iter_mut()
1849 .for_each(|pos| *pos = (Vec3::from_slice(pos) + translation).to_array());
1850 }
1851
1852 Ok(())
1853 }
1854
1855 /// Rotates the vertex positions, normals, and tangents of the mesh by the given [`Quat`].
1856 ///
1857 /// `Aabb` of entities with modified mesh are not updated automatically.
1858 ///
1859 /// # Panics
1860 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1861 /// this as an error use [`Mesh::try_rotated_by`]
1862 pub fn rotated_by(mut self, rotation: Quat) -> Self {
1863 self.try_rotate_by(rotation).expect(MESH_EXTRACTED_ERROR);
1864 self
1865 }
1866
1867 /// Rotates the vertex positions, normals, and tangents of the mesh by the given [`Quat`].
1868 ///
1869 /// `Aabb` of entities with modified mesh are not updated automatically.
1870 pub fn try_rotated_by(mut self, rotation: Quat) -> Result<Self, MeshAccessError> {
1871 self.try_rotate_by(rotation)?;
1872 Ok(self)
1873 }
1874
1875 /// Rotates the vertex positions, normals, and tangents of the mesh in place by the given [`Quat`].
1876 ///
1877 /// `Aabb` of entities with modified mesh are not updated automatically.
1878 ///
1879 /// # Panics
1880 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1881 /// this as an error use [`Mesh::try_rotate_by`]
1882 pub fn rotate_by(&mut self, rotation: Quat) {
1883 self.try_rotate_by(rotation).expect(MESH_EXTRACTED_ERROR);
1884 }
1885
1886 /// Rotates the vertex positions, normals, and tangents of the mesh in place by the given [`Quat`].
1887 ///
1888 /// `Aabb` of entities with modified mesh are not updated automatically.
1889 pub fn try_rotate_by(&mut self, rotation: Quat) -> Result<(), MeshAccessError> {
1890 if let Some(VertexAttributeValues::Float32x3(positions)) =
1891 self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?
1892 {
1893 // Apply rotation to vertex positions
1894 positions
1895 .iter_mut()
1896 .for_each(|pos| *pos = (rotation * Vec3::from_slice(pos)).to_array());
1897 }
1898
1899 // No need to transform normals or tangents if rotation is near identity
1900 if rotation.is_near_identity() {
1901 return Ok(());
1902 }
1903
1904 if let Some(VertexAttributeValues::Float32x3(normals)) =
1905 self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)?
1906 {
1907 // Transform normals
1908 normals.iter_mut().for_each(|normal| {
1909 *normal = (rotation * Vec3::from_slice(normal).normalize_or_zero()).to_array();
1910 });
1911 }
1912
1913 if let Some(VertexAttributeValues::Float32x4(tangents)) =
1914 self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)?
1915 {
1916 // Transform tangents
1917 tangents.iter_mut().for_each(|tangent| {
1918 let handedness = tangent[3];
1919 *tangent = (rotation * Vec3::from_slice(tangent).normalize_or_zero())
1920 .extend(handedness)
1921 .to_array();
1922 });
1923 }
1924
1925 Ok(())
1926 }
1927
1928 /// Scales the vertex positions, normals, and tangents of the mesh by the given [`Vec3`].
1929 ///
1930 /// `Aabb` of entities with modified mesh are not updated automatically.
1931 ///
1932 /// # Panics
1933 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1934 /// this as an error use [`Mesh::try_scaled_by`]
1935 pub fn scaled_by(mut self, scale: Vec3) -> Self {
1936 self.scale_by(scale);
1937 self
1938 }
1939
1940 /// Scales the vertex positions, normals, and tangents of the mesh by the given [`Vec3`].
1941 ///
1942 /// `Aabb` of entities with modified mesh are not updated automatically.
1943 pub fn try_scaled_by(mut self, scale: Vec3) -> Result<Self, MeshAccessError> {
1944 self.try_scale_by(scale)?;
1945 Ok(self)
1946 }
1947
1948 /// Scales the vertex positions, normals, and tangents of the mesh in place by the given [`Vec3`].
1949 ///
1950 /// `Aabb` of entities with modified mesh are not updated automatically.
1951 ///
1952 /// # Panics
1953 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
1954 /// this as an error use [`Mesh::try_scale_by`]
1955 pub fn scale_by(&mut self, scale: Vec3) {
1956 self.try_scale_by(scale).expect(MESH_EXTRACTED_ERROR);
1957 }
1958
1959 /// Scales the vertex positions, normals, and tangents of the mesh in place by the given [`Vec3`].
1960 ///
1961 /// `Aabb` of entities with modified mesh are not updated automatically.
1962 pub fn try_scale_by(&mut self, scale: Vec3) -> Result<(), MeshAccessError> {
1963 // Needed when transforming normals and tangents
1964 let scale_recip = 1. / scale;
1965 debug_assert!(
1966 scale.yzx() * scale.zxy() != Vec3::ZERO,
1967 "mesh transform scale cannot be zero on more than one axis"
1968 );
1969
1970 if let Some(VertexAttributeValues::Float32x3(positions)) =
1971 self.try_attribute_mut_option(Mesh::ATTRIBUTE_POSITION)?
1972 {
1973 // Apply scale to vertex positions
1974 positions
1975 .iter_mut()
1976 .for_each(|pos| *pos = (scale * Vec3::from_slice(pos)).to_array());
1977 }
1978
1979 // No need to transform normals or tangents if scale is uniform
1980 if scale.x == scale.y && scale.y == scale.z {
1981 return Ok(());
1982 }
1983
1984 if let Some(VertexAttributeValues::Float32x3(normals)) =
1985 self.try_attribute_mut_option(Mesh::ATTRIBUTE_NORMAL)?
1986 {
1987 // Transform normals, taking into account non-uniform scaling
1988 normals.iter_mut().for_each(|normal| {
1989 *normal = scale_normal(Vec3::from_array(*normal), scale_recip).to_array();
1990 });
1991 }
1992
1993 if let Some(VertexAttributeValues::Float32x4(tangents)) =
1994 self.try_attribute_mut_option(Mesh::ATTRIBUTE_TANGENT)?
1995 {
1996 // Transform tangents, taking into account non-uniform scaling
1997 tangents.iter_mut().for_each(|tangent| {
1998 let handedness = tangent[3];
1999 let scaled_tangent = Vec3::from_slice(tangent) * scale;
2000 *tangent = scaled_tangent
2001 .normalize_or_zero()
2002 .extend(handedness)
2003 .to_array();
2004 });
2005 }
2006
2007 Ok(())
2008 }
2009
2010 /// Normalize joint weights so they sum to 1.
2011 ///
2012 /// # Panics
2013 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2014 /// this as an error use [`Mesh::try_normalize_joint_weights`]
2015 pub fn normalize_joint_weights(&mut self) {
2016 self.try_normalize_joint_weights()
2017 .expect(MESH_EXTRACTED_ERROR);
2018 }
2019
2020 /// Normalize joint weights so they sum to 1.
2021 pub fn try_normalize_joint_weights(&mut self) -> Result<(), MeshAccessError> {
2022 if let Some(VertexAttributeValues::Float32x4(joints)) =
2023 self.try_attribute_mut_option(Self::ATTRIBUTE_JOINT_WEIGHT)?
2024 {
2025 for weights in joints.iter_mut() {
2026 // force negative weights to zero
2027 weights.iter_mut().for_each(|w| *w = w.max(0.0));
2028
2029 let sum: f32 = weights.iter().sum();
2030 if sum == 0.0 {
2031 // all-zero weights are invalid
2032 weights[0] = 1.0;
2033 } else {
2034 let recip = sum.recip();
2035 for weight in weights.iter_mut() {
2036 *weight *= recip;
2037 }
2038 }
2039 }
2040 }
2041
2042 Ok(())
2043 }
2044
2045 /// Get a list of this Mesh's [triangles] as an iterator if possible.
2046 ///
2047 /// Returns an error if any of the following conditions are met (see [`MeshTrianglesError`]):
2048 /// * The Mesh's [primitive topology] is not `TriangleList` or `TriangleStrip`.
2049 /// * The Mesh is missing position or index data.
2050 /// * The Mesh's position data has the wrong format (not `Float32x3`).
2051 ///
2052 /// [primitive topology]: PrimitiveTopology
2053 /// [triangles]: Triangle3d
2054 pub fn triangles(&self) -> Result<impl Iterator<Item = Triangle3d> + '_, MeshTrianglesError> {
2055 let position_data = self.try_attribute(Mesh::ATTRIBUTE_POSITION)?;
2056
2057 let Some(vertices) = position_data.as_float3() else {
2058 return Err(MeshTrianglesError::PositionsFormat);
2059 };
2060
2061 let indices = self.try_indices()?;
2062
2063 match self.primitive_topology {
2064 PrimitiveTopology::TriangleList => {
2065 // When indices reference out-of-bounds vertex data, the triangle is omitted.
2066 // This implicitly truncates the indices to a multiple of 3.
2067 let iterator = match indices {
2068 Indices::U16(vec) => FourIterators::First(
2069 vec.as_slice()
2070 .chunks_exact(3)
2071 .flat_map(move |indices| indices_to_triangle(vertices, indices)),
2072 ),
2073 Indices::U32(vec) => FourIterators::Second(
2074 vec.as_slice()
2075 .chunks_exact(3)
2076 .flat_map(move |indices| indices_to_triangle(vertices, indices)),
2077 ),
2078 };
2079
2080 return Ok(iterator);
2081 }
2082
2083 PrimitiveTopology::TriangleStrip => {
2084 // When indices reference out-of-bounds vertex data, the triangle is omitted.
2085 // If there aren't enough indices to make a triangle, then an empty vector will be
2086 // returned.
2087 let iterator = match indices {
2088 Indices::U16(vec) => {
2089 FourIterators::Third(vec.as_slice().windows(3).enumerate().flat_map(
2090 move |(i, indices)| {
2091 if i % 2 == 0 {
2092 indices_to_triangle(vertices, indices)
2093 } else {
2094 indices_to_triangle(
2095 vertices,
2096 &[indices[1], indices[0], indices[2]],
2097 )
2098 }
2099 },
2100 ))
2101 }
2102 Indices::U32(vec) => {
2103 FourIterators::Fourth(vec.as_slice().windows(3).enumerate().flat_map(
2104 move |(i, indices)| {
2105 if i % 2 == 0 {
2106 indices_to_triangle(vertices, indices)
2107 } else {
2108 indices_to_triangle(
2109 vertices,
2110 &[indices[1], indices[0], indices[2]],
2111 )
2112 }
2113 },
2114 ))
2115 }
2116 };
2117
2118 return Ok(iterator);
2119 }
2120
2121 _ => {
2122 return Err(MeshTrianglesError::WrongTopology);
2123 }
2124 };
2125
2126 fn indices_to_triangle<T: TryInto<usize> + Copy>(
2127 vertices: &[[f32; 3]],
2128 indices: &[T],
2129 ) -> Option<Triangle3d> {
2130 let vert0: Vec3 = Vec3::from(*vertices.get(indices[0].try_into().ok()?)?);
2131 let vert1: Vec3 = Vec3::from(*vertices.get(indices[1].try_into().ok()?)?);
2132 let vert2: Vec3 = Vec3::from(*vertices.get(indices[2].try_into().ok()?)?);
2133 Some(Triangle3d {
2134 vertices: [vert0, vert1, vert2],
2135 })
2136 }
2137 }
2138
2139 /// Extracts the mesh vertex, index and morph target data for GPU upload.
2140 /// This function is called internally in render world extraction, it is
2141 /// unlikely to be useful outside of that context.
2142 ///
2143 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
2144 pub fn take_gpu_data(&mut self) -> Result<Self, MeshAccessError> {
2145 let attributes = self.attributes.extract()?;
2146 let indices = self.indices.extract()?;
2147 #[cfg(feature = "morph")]
2148 let morph_targets = self.morph_targets.extract()?;
2149 #[cfg(feature = "morph")]
2150 let morph_target_names = self.morph_target_names.extract()?;
2151
2152 // store the aabb extents as they cannot be computed after extraction
2153 if let Some(MeshAttributeData {
2154 values: VertexAttributeValues::Float32x3(position_values),
2155 ..
2156 }) = attributes
2157 .as_ref_option()?
2158 .and_then(|attrs| attrs.get(&Self::ATTRIBUTE_POSITION.id))
2159 && !position_values.is_empty()
2160 {
2161 let mut iter = position_values.iter().map(|p| Vec3::from_slice(p));
2162 let mut min = iter.next().unwrap();
2163 let mut max = min;
2164 for v in iter {
2165 min = Vec3::min(min, v);
2166 max = Vec3::max(max, v);
2167 }
2168 self.final_aabb = Some(Aabb3d::from_min_max(min, max));
2169 }
2170
2171 Ok(Self {
2172 attributes,
2173 indices,
2174 #[cfg(feature = "morph")]
2175 morph_targets,
2176 #[cfg(feature = "morph")]
2177 morph_target_names,
2178 ..self.clone()
2179 })
2180 }
2181}
2182
2183#[cfg(feature = "morph")]
2184impl Mesh {
2185 /// Whether this mesh has morph targets.
2186 ///
2187 /// # Panics
2188 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2189 /// this as an error use [`Mesh::try_has_morph_targets`]
2190 pub fn has_morph_targets(&self) -> bool {
2191 self.try_has_morph_targets().expect(MESH_EXTRACTED_ERROR)
2192 }
2193
2194 /// Whether this mesh has morph targets.
2195 pub fn try_has_morph_targets(&self) -> Result<bool, MeshAccessError> {
2196 Ok(self.morph_targets.as_ref_option()?.is_some())
2197 }
2198
2199 /// Set [morph targets] image for this mesh. This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.
2200 ///
2201 /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
2202 ///
2203 /// # Panics
2204 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2205 /// this as an error use [`Mesh::try_set_morph_targets`]
2206 pub fn set_morph_targets(&mut self, morph_targets: Handle<Image>) {
2207 self.try_set_morph_targets(morph_targets)
2208 .expect(MESH_EXTRACTED_ERROR);
2209 }
2210
2211 /// Set [morph targets] image for this mesh. This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.
2212 ///
2213 /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
2214 pub fn try_set_morph_targets(
2215 &mut self,
2216 morph_targets: Handle<Image>,
2217 ) -> Result<(), MeshAccessError> {
2218 self.morph_targets.replace(Some(morph_targets))?;
2219 Ok(())
2220 }
2221
2222 /// Retrieve the morph targets for this mesh, or None if there are no morph targets.
2223 /// # Panics
2224 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2225 /// this as an error use [`Mesh::try_morph_targets`]
2226 pub fn morph_targets(&self) -> Option<&Handle<Image>> {
2227 self.morph_targets
2228 .as_ref_option()
2229 .expect(MESH_EXTRACTED_ERROR)
2230 }
2231
2232 /// Retrieve the morph targets for this mesh, or None if there are no morph targets.
2233 ///
2234 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
2235 /// if the morph targets do not exist.
2236 pub fn try_morph_targets(&self) -> Result<&Handle<Image>, MeshAccessError> {
2237 self.morph_targets.as_ref()
2238 }
2239
2240 /// Consumes the mesh and returns a mesh with the given [morph targets].
2241 ///
2242 /// This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.
2243 ///
2244 /// (Alternatively, you can use [`Mesh::set_morph_targets`] to mutate an existing mesh in-place)
2245 ///
2246 /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
2247 ///
2248 /// # Panics
2249 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2250 /// this as an error use [`Mesh::try_with_morph_targets`]
2251 #[must_use]
2252 pub fn with_morph_targets(mut self, morph_targets: Handle<Image>) -> Self {
2253 self.set_morph_targets(morph_targets);
2254 self
2255 }
2256
2257 /// Consumes the mesh and returns a mesh with the given [morph targets].
2258 ///
2259 /// This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.
2260 ///
2261 /// (Alternatively, you can use [`Mesh::set_morph_targets`] to mutate an existing mesh in-place)
2262 ///
2263 /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
2264 ///
2265 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
2266 pub fn try_with_morph_targets(
2267 mut self,
2268 morph_targets: Handle<Image>,
2269 ) -> Result<Self, MeshAccessError> {
2270 self.try_set_morph_targets(morph_targets)?;
2271 Ok(self)
2272 }
2273
2274 /// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`.
2275 ///
2276 /// # Panics
2277 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2278 /// this as an error use [`Mesh::try_set_morph_target_names`]
2279 pub fn set_morph_target_names(&mut self, names: Vec<String>) {
2280 self.try_set_morph_target_names(names)
2281 .expect(MESH_EXTRACTED_ERROR);
2282 }
2283
2284 /// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`.
2285 ///
2286 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
2287 pub fn try_set_morph_target_names(
2288 &mut self,
2289 names: Vec<String>,
2290 ) -> Result<(), MeshAccessError> {
2291 self.morph_target_names.replace(Some(names))?;
2292 Ok(())
2293 }
2294
2295 /// Consumes the mesh and returns a mesh with morph target names.
2296 /// Names should correspond to the order of the morph targets in `set_morph_targets`.
2297 ///
2298 /// (Alternatively, you can use [`Mesh::set_morph_target_names`] to mutate an existing mesh in-place)
2299 ///
2300 /// # Panics
2301 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2302 /// this as an error use [`Mesh::try_set_morph_target_names`]
2303 #[must_use]
2304 pub fn with_morph_target_names(self, names: Vec<String>) -> Self {
2305 self.try_with_morph_target_names(names)
2306 .expect(MESH_EXTRACTED_ERROR)
2307 }
2308
2309 /// Consumes the mesh and returns a mesh with morph target names.
2310 /// Names should correspond to the order of the morph targets in `set_morph_targets`.
2311 ///
2312 /// (Alternatively, you can use [`Mesh::set_morph_target_names`] to mutate an existing mesh in-place)
2313 ///
2314 /// Returns an error if the mesh data has been extracted to `RenderWorld`.
2315 pub fn try_with_morph_target_names(
2316 mut self,
2317 names: Vec<String>,
2318 ) -> Result<Self, MeshAccessError> {
2319 self.try_set_morph_target_names(names)?;
2320 Ok(self)
2321 }
2322
2323 /// Gets a list of all morph target names, if they exist.
2324 ///
2325 /// # Panics
2326 /// Panics when the mesh data has already been extracted to `RenderWorld`. To handle
2327 /// this as an error use [`Mesh::try_morph_target_names`]
2328 pub fn morph_target_names(&self) -> Option<&[String]> {
2329 self.try_morph_target_names().expect(MESH_EXTRACTED_ERROR)
2330 }
2331
2332 /// Gets a list of all morph target names, if they exist.
2333 ///
2334 /// Returns an error if the mesh data has been extracted to `RenderWorld`or
2335 /// if the morph targets do not exist.
2336 pub fn try_morph_target_names(&self) -> Result<Option<&[String]>, MeshAccessError> {
2337 Ok(self
2338 .morph_target_names
2339 .as_ref_option()?
2340 .map(core::ops::Deref::deref))
2341 }
2342}
2343
2344/// Correctly scales and renormalizes an already normalized `normal` by the scale determined by its reciprocal `scale_recip`
2345pub(crate) fn scale_normal(normal: Vec3, scale_recip: Vec3) -> Vec3 {
2346 // This is basically just `normal * scale_recip` but with the added rule that `0. * anything == 0.`
2347 // This is necessary because components of `scale_recip` may be infinities, which do not multiply to zero
2348 let n = Vec3::select(normal.cmpeq(Vec3::ZERO), Vec3::ZERO, normal * scale_recip);
2349
2350 // If n is finite, no component of `scale_recip` was infinite or the normal was perpendicular to the scale
2351 // else the scale had at least one zero-component and the normal needs to point along the direction of that component
2352 if n.is_finite() {
2353 n.normalize_or_zero()
2354 } else {
2355 Vec3::select(n.abs().cmpeq(Vec3::INFINITY), n.signum(), Vec3::ZERO).normalize()
2356 }
2357}
2358
2359impl core::ops::Mul<Mesh> for Transform {
2360 type Output = Mesh;
2361
2362 fn mul(self, rhs: Mesh) -> Self::Output {
2363 rhs.transformed_by(self)
2364 }
2365}
2366
2367/// A version of [`Mesh`] suitable for serializing for short-term transfer.
2368///
2369/// [`Mesh`] does not implement [`Serialize`] / [`Deserialize`] because it is made with the renderer in mind.
2370/// It is not a general-purpose mesh implementation, and its internals are subject to frequent change.
2371/// As such, storing a [`Mesh`] on disk is highly discouraged.
2372///
2373/// But there are still some valid use cases for serializing a [`Mesh`], namely transferring meshes between processes.
2374/// To support this, you can create a [`SerializedMesh`] from a [`Mesh`] with [`SerializedMesh::from_mesh`],
2375/// and then deserialize it with [`SerializedMesh::deserialize`]. The caveats are:
2376/// - The mesh representation is not valid across different versions of Bevy.
2377/// - This conversion is lossy. Only the following information is preserved:
2378/// - Primitive topology
2379/// - Vertex attributes
2380/// - Indices
2381/// - Custom attributes that were not specified with [`MeshDeserializer::add_custom_vertex_attribute`] will be ignored while deserializing.
2382#[cfg(feature = "serialize")]
2383#[derive(Debug, Clone, Serialize, Deserialize)]
2384pub struct SerializedMesh {
2385 primitive_topology: PrimitiveTopology,
2386 attributes: Vec<(MeshVertexAttributeId, SerializedMeshAttributeData)>,
2387 indices: Option<Indices>,
2388}
2389
2390#[cfg(feature = "serialize")]
2391impl SerializedMesh {
2392 /// Create a [`SerializedMesh`] from a [`Mesh`]. See the documentation for [`SerializedMesh`] for caveats.
2393 pub fn from_mesh(mut mesh: Mesh) -> Self {
2394 Self {
2395 primitive_topology: mesh.primitive_topology,
2396 attributes: mesh
2397 .attributes
2398 .replace(None)
2399 .expect(MESH_EXTRACTED_ERROR)
2400 .unwrap()
2401 .into_iter()
2402 .map(|(id, data)| {
2403 (
2404 id,
2405 SerializedMeshAttributeData::from_mesh_attribute_data(data),
2406 )
2407 })
2408 .collect(),
2409 indices: mesh.indices.replace(None).expect(MESH_EXTRACTED_ERROR),
2410 }
2411 }
2412
2413 /// Create a [`Mesh`] from a [`SerializedMesh`]. See the documentation for [`SerializedMesh`] for caveats.
2414 ///
2415 /// Use [`MeshDeserializer`] if you need to pass extra options to the deserialization process, such as specifying custom vertex attributes.
2416 pub fn into_mesh(self) -> Mesh {
2417 MeshDeserializer::default().deserialize(self)
2418 }
2419}
2420
2421/// Use to specify extra options when deserializing a [`SerializedMesh`] into a [`Mesh`].
2422#[cfg(feature = "serialize")]
2423pub struct MeshDeserializer {
2424 custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,
2425}
2426
2427#[cfg(feature = "serialize")]
2428impl Default for MeshDeserializer {
2429 fn default() -> Self {
2430 // Written like this so that the compiler can validate that we use all the built-in attributes.
2431 // If you just added a new attribute and got a compile error, please add it to this list :)
2432 const BUILTINS: [MeshVertexAttribute; Mesh::FIRST_AVAILABLE_CUSTOM_ATTRIBUTE as usize] = [
2433 Mesh::ATTRIBUTE_POSITION,
2434 Mesh::ATTRIBUTE_NORMAL,
2435 Mesh::ATTRIBUTE_UV_0,
2436 Mesh::ATTRIBUTE_UV_1,
2437 Mesh::ATTRIBUTE_TANGENT,
2438 Mesh::ATTRIBUTE_COLOR,
2439 Mesh::ATTRIBUTE_JOINT_WEIGHT,
2440 Mesh::ATTRIBUTE_JOINT_INDEX,
2441 ];
2442 Self {
2443 custom_vertex_attributes: BUILTINS
2444 .into_iter()
2445 .map(|attribute| (attribute.name.into(), attribute))
2446 .collect(),
2447 }
2448 }
2449}
2450
2451#[cfg(feature = "serialize")]
2452impl MeshDeserializer {
2453 /// Create a new [`MeshDeserializer`].
2454 pub fn new() -> Self {
2455 Self::default()
2456 }
2457
2458 /// Register a custom vertex attribute to the deserializer. Custom vertex attributes that were not added with this method will be ignored while deserializing.
2459 pub fn add_custom_vertex_attribute(
2460 &mut self,
2461 name: &str,
2462 attribute: MeshVertexAttribute,
2463 ) -> &mut Self {
2464 self.custom_vertex_attributes.insert(name.into(), attribute);
2465 self
2466 }
2467
2468 /// Deserialize a [`SerializedMesh`] into a [`Mesh`].
2469 ///
2470 /// See the documentation for [`SerializedMesh`] for caveats.
2471 pub fn deserialize(&self, serialized_mesh: SerializedMesh) -> Mesh {
2472 Mesh {
2473 attributes: MeshExtractableData::Data(
2474 serialized_mesh
2475 .attributes
2476 .into_iter()
2477 .filter_map(|(id, data)| {
2478 let attribute = data.attribute.clone();
2479 let Some(data) =
2480 data.try_into_mesh_attribute_data(&self.custom_vertex_attributes)
2481 else {
2482 warn!(
2483 "Deserialized mesh contains custom vertex attribute {attribute:?} that \
2484 was not specified with `MeshDeserializer::add_custom_vertex_attribute`. Ignoring."
2485 );
2486 return None;
2487 };
2488 Some((id, data))
2489 })
2490 .collect()),
2491 indices: serialized_mesh.indices.into(),
2492 ..Mesh::new(serialized_mesh.primitive_topology, RenderAssetUsages::default())
2493 }
2494 }
2495}
2496
2497/// Error that can occur when calling [`Mesh::merge`].
2498#[derive(Error, Debug, Clone)]
2499pub enum MeshMergeError {
2500 #[error("Incompatible vertex attribute types: {} and {}", self_attribute.name, other_attribute.map(|a| a.name).unwrap_or("None"))]
2501 IncompatibleVertexAttributes {
2502 self_attribute: MeshVertexAttribute,
2503 other_attribute: Option<MeshVertexAttribute>,
2504 },
2505 #[error(
2506 "Incompatible primitive topologies: {:?} and {:?}",
2507 self_primitive_topology,
2508 other_primitive_topology
2509 )]
2510 IncompatiblePrimitiveTopology {
2511 self_primitive_topology: PrimitiveTopology,
2512 other_primitive_topology: PrimitiveTopology,
2513 },
2514 #[error("Mesh access error: {0}")]
2515 MeshAccessError(#[from] MeshAccessError),
2516}
2517
2518#[cfg(test)]
2519mod tests {
2520 use super::Mesh;
2521 #[cfg(feature = "serialize")]
2522 use super::SerializedMesh;
2523 use crate::mesh::{Indices, MeshWindingInvertError, VertexAttributeValues};
2524 use crate::PrimitiveTopology;
2525 use bevy_asset::RenderAssetUsages;
2526 use bevy_math::bounding::Aabb3d;
2527 use bevy_math::primitives::Triangle3d;
2528 use bevy_math::Vec3;
2529 use bevy_transform::components::Transform;
2530
2531 #[test]
2532 #[should_panic]
2533 fn panic_invalid_format() {
2534 let _mesh = Mesh::new(
2535 PrimitiveTopology::TriangleList,
2536 RenderAssetUsages::default(),
2537 )
2538 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0, 0.0]]);
2539 }
2540
2541 #[test]
2542 fn transform_mesh() {
2543 let mesh = Mesh::new(
2544 PrimitiveTopology::TriangleList,
2545 RenderAssetUsages::default(),
2546 )
2547 .with_inserted_attribute(
2548 Mesh::ATTRIBUTE_POSITION,
2549 vec![[-1., -1., 2.], [1., -1., 2.], [0., 1., 2.]],
2550 )
2551 .with_inserted_attribute(
2552 Mesh::ATTRIBUTE_NORMAL,
2553 vec![
2554 Vec3::new(-1., -1., 1.).normalize().to_array(),
2555 Vec3::new(1., -1., 1.).normalize().to_array(),
2556 [0., 0., 1.],
2557 ],
2558 )
2559 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0., 0.], [1., 0.], [0.5, 1.]]);
2560
2561 let mesh = mesh.transformed_by(
2562 Transform::from_translation(Vec3::splat(-2.)).with_scale(Vec3::new(2., 0., -1.)),
2563 );
2564
2565 if let Some(VertexAttributeValues::Float32x3(positions)) =
2566 mesh.attribute(Mesh::ATTRIBUTE_POSITION)
2567 {
2568 // All positions are first scaled resulting in `vec![[-2, 0., -2.], [2., 0., -2.], [0., 0., -2.]]`
2569 // and then shifted by `-2.` along each axis
2570 assert_eq!(
2571 positions,
2572 &vec![[-4.0, -2.0, -4.0], [0.0, -2.0, -4.0], [-2.0, -2.0, -4.0]]
2573 );
2574 } else {
2575 panic!("Mesh does not have a position attribute");
2576 }
2577
2578 if let Some(VertexAttributeValues::Float32x3(normals)) =
2579 mesh.attribute(Mesh::ATTRIBUTE_NORMAL)
2580 {
2581 assert_eq!(normals, &vec![[0., -1., 0.], [0., -1., 0.], [0., 0., -1.]]);
2582 } else {
2583 panic!("Mesh does not have a normal attribute");
2584 }
2585
2586 if let Some(VertexAttributeValues::Float32x2(uvs)) = mesh.attribute(Mesh::ATTRIBUTE_UV_0) {
2587 assert_eq!(uvs, &vec![[0., 0.], [1., 0.], [0.5, 1.]]);
2588 } else {
2589 panic!("Mesh does not have a uv attribute");
2590 }
2591 }
2592
2593 #[test]
2594 fn point_list_mesh_invert_winding() {
2595 let mesh = Mesh::new(PrimitiveTopology::PointList, RenderAssetUsages::default())
2596 .with_inserted_indices(Indices::U32(vec![]));
2597 assert!(matches!(
2598 mesh.with_inverted_winding(),
2599 Err(MeshWindingInvertError::WrongTopology)
2600 ));
2601 }
2602
2603 #[test]
2604 fn line_list_mesh_invert_winding() {
2605 let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
2606 .with_inserted_indices(Indices::U32(vec![0, 1, 1, 2, 2, 3]));
2607 let mesh = mesh.with_inverted_winding().unwrap();
2608 assert_eq!(
2609 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
2610 vec![3, 2, 2, 1, 1, 0]
2611 );
2612 }
2613
2614 #[test]
2615 fn line_list_mesh_invert_winding_fail() {
2616 let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
2617 .with_inserted_indices(Indices::U32(vec![0, 1, 1]));
2618 assert!(matches!(
2619 mesh.with_inverted_winding(),
2620 Err(MeshWindingInvertError::AbruptIndicesEnd)
2621 ));
2622 }
2623
2624 #[test]
2625 fn line_strip_mesh_invert_winding() {
2626 let mesh = Mesh::new(PrimitiveTopology::LineStrip, RenderAssetUsages::default())
2627 .with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));
2628 let mesh = mesh.with_inverted_winding().unwrap();
2629 assert_eq!(
2630 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
2631 vec![3, 2, 1, 0]
2632 );
2633 }
2634
2635 #[test]
2636 fn triangle_list_mesh_invert_winding() {
2637 let mesh = Mesh::new(
2638 PrimitiveTopology::TriangleList,
2639 RenderAssetUsages::default(),
2640 )
2641 .with_inserted_indices(Indices::U32(vec![
2642 0, 3, 1, // First triangle
2643 1, 3, 2, // Second triangle
2644 ]));
2645 let mesh = mesh.with_inverted_winding().unwrap();
2646 assert_eq!(
2647 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
2648 vec![
2649 0, 1, 3, // First triangle
2650 1, 2, 3, // Second triangle
2651 ]
2652 );
2653 }
2654
2655 #[test]
2656 fn triangle_list_mesh_invert_winding_fail() {
2657 let mesh = Mesh::new(
2658 PrimitiveTopology::TriangleList,
2659 RenderAssetUsages::default(),
2660 )
2661 .with_inserted_indices(Indices::U32(vec![0, 3, 1, 2]));
2662 assert!(matches!(
2663 mesh.with_inverted_winding(),
2664 Err(MeshWindingInvertError::AbruptIndicesEnd)
2665 ));
2666 }
2667
2668 #[test]
2669 fn triangle_strip_mesh_invert_winding() {
2670 let mesh = Mesh::new(
2671 PrimitiveTopology::TriangleStrip,
2672 RenderAssetUsages::default(),
2673 )
2674 .with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));
2675 let mesh = mesh.with_inverted_winding().unwrap();
2676 assert_eq!(
2677 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
2678 vec![3, 2, 1, 0]
2679 );
2680 }
2681
2682 #[test]
2683 fn compute_area_weighted_normals() {
2684 let mut mesh = Mesh::new(
2685 PrimitiveTopology::TriangleList,
2686 RenderAssetUsages::default(),
2687 );
2688
2689 // z y
2690 // | /
2691 // 3---2
2692 // | / \
2693 // 0-----1--x
2694
2695 mesh.insert_attribute(
2696 Mesh::ATTRIBUTE_POSITION,
2697 vec![[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
2698 );
2699 mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));
2700 mesh.compute_area_weighted_normals();
2701 let normals = mesh
2702 .attribute(Mesh::ATTRIBUTE_NORMAL)
2703 .unwrap()
2704 .as_float3()
2705 .unwrap();
2706 assert_eq!(4, normals.len());
2707 // 0
2708 assert_eq!(Vec3::new(1., 0., 1.).normalize().to_array(), normals[0]);
2709 // 1
2710 assert_eq!([0., 0., 1.], normals[1]);
2711 // 2
2712 assert_eq!(Vec3::new(1., 0., 1.).normalize().to_array(), normals[2]);
2713 // 3
2714 assert_eq!([1., 0., 0.], normals[3]);
2715 }
2716
2717 #[test]
2718 fn compute_area_weighted_normals_proportionate() {
2719 let mut mesh = Mesh::new(
2720 PrimitiveTopology::TriangleList,
2721 RenderAssetUsages::default(),
2722 );
2723
2724 // z y
2725 // | /
2726 // 3---2..
2727 // | / \
2728 // 0-------1---x
2729
2730 mesh.insert_attribute(
2731 Mesh::ATTRIBUTE_POSITION,
2732 vec![[0., 0., 0.], [2., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
2733 );
2734 mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));
2735 mesh.compute_area_weighted_normals();
2736 let normals = mesh
2737 .attribute(Mesh::ATTRIBUTE_NORMAL)
2738 .unwrap()
2739 .as_float3()
2740 .unwrap();
2741 assert_eq!(4, normals.len());
2742 // 0
2743 assert_eq!(Vec3::new(1., 0., 2.).normalize().to_array(), normals[0]);
2744 // 1
2745 assert_eq!([0., 0., 1.], normals[1]);
2746 // 2
2747 assert_eq!(Vec3::new(1., 0., 2.).normalize().to_array(), normals[2]);
2748 // 3
2749 assert_eq!([1., 0., 0.], normals[3]);
2750 }
2751
2752 #[test]
2753 fn compute_angle_weighted_normals() {
2754 // CuboidMeshBuilder duplicates vertices (even though it is indexed)
2755
2756 // 5---------4
2757 // /| /|
2758 // 1-+-------0 |
2759 // | 6-------|-7
2760 // |/ |/
2761 // 2---------3
2762 let verts = vec![
2763 [1.0, 1.0, 1.0],
2764 [-1.0, 1.0, 1.0],
2765 [-1.0, -1.0, 1.0],
2766 [1.0, -1.0, 1.0],
2767 [1.0, 1.0, -1.0],
2768 [-1.0, 1.0, -1.0],
2769 [-1.0, -1.0, -1.0],
2770 [1.0, -1.0, -1.0],
2771 ];
2772
2773 let indices = Indices::U16(vec![
2774 0, 1, 2, 2, 3, 0, // front
2775 5, 4, 7, 7, 6, 5, // back
2776 1, 5, 6, 6, 2, 1, // left
2777 4, 0, 3, 3, 7, 4, // right
2778 4, 5, 1, 1, 0, 4, // top
2779 3, 2, 6, 6, 7, 3, // bottom
2780 ]);
2781 let mut mesh = Mesh::new(
2782 PrimitiveTopology::TriangleList,
2783 RenderAssetUsages::default(),
2784 );
2785 mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, verts);
2786 mesh.insert_indices(indices);
2787 mesh.compute_smooth_normals();
2788
2789 let normals = mesh
2790 .attribute(Mesh::ATTRIBUTE_NORMAL)
2791 .unwrap()
2792 .as_float3()
2793 .unwrap();
2794
2795 for new in normals.iter().copied().flatten() {
2796 // std impl is unstable
2797 const FRAC_1_SQRT_3: f32 = 0.57735026;
2798 const MIN: f32 = FRAC_1_SQRT_3 - f32::EPSILON;
2799 const MAX: f32 = FRAC_1_SQRT_3 + f32::EPSILON;
2800 assert!(new.abs() >= MIN, "{new} < {MIN}");
2801 assert!(new.abs() <= MAX, "{new} > {MAX}");
2802 }
2803 }
2804
2805 #[test]
2806 fn triangles_from_triangle_list() {
2807 let mut mesh = Mesh::new(
2808 PrimitiveTopology::TriangleList,
2809 RenderAssetUsages::default(),
2810 );
2811 mesh.insert_attribute(
2812 Mesh::ATTRIBUTE_POSITION,
2813 vec![[0., 0., 0.], [1., 0., 0.], [1., 1., 0.], [0., 1., 0.]],
2814 );
2815 mesh.insert_indices(Indices::U32(vec![0, 1, 2, 2, 3, 0]));
2816 assert_eq!(
2817 vec![
2818 Triangle3d {
2819 vertices: [
2820 Vec3::new(0., 0., 0.),
2821 Vec3::new(1., 0., 0.),
2822 Vec3::new(1., 1., 0.),
2823 ]
2824 },
2825 Triangle3d {
2826 vertices: [
2827 Vec3::new(1., 1., 0.),
2828 Vec3::new(0., 1., 0.),
2829 Vec3::new(0., 0., 0.),
2830 ]
2831 }
2832 ],
2833 mesh.triangles().unwrap().collect::<Vec<Triangle3d>>()
2834 );
2835 }
2836
2837 #[test]
2838 fn triangles_from_triangle_strip() {
2839 let mut mesh = Mesh::new(
2840 PrimitiveTopology::TriangleStrip,
2841 RenderAssetUsages::default(),
2842 );
2843 // Triangles: (0, 1, 2), (2, 1, 3), (2, 3, 4), (4, 3, 5)
2844 //
2845 // 4 - 5
2846 // | \ |
2847 // 2 - 3
2848 // | \ |
2849 // 0 - 1
2850 let positions: Vec<Vec3> = [
2851 [0., 0., 0.],
2852 [1., 0., 0.],
2853 [0., 1., 0.],
2854 [1., 1., 0.],
2855 [0., 2., 0.],
2856 [1., 2., 0.],
2857 ]
2858 .into_iter()
2859 .map(Vec3::from_array)
2860 .collect();
2861 mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions.clone());
2862 mesh.insert_indices(Indices::U32(vec![0, 1, 2, 3, 4, 5]));
2863 assert_eq!(
2864 vec![
2865 Triangle3d {
2866 vertices: [positions[0], positions[1], positions[2]]
2867 },
2868 Triangle3d {
2869 vertices: [positions[2], positions[1], positions[3]]
2870 },
2871 Triangle3d {
2872 vertices: [positions[2], positions[3], positions[4]]
2873 },
2874 Triangle3d {
2875 vertices: [positions[4], positions[3], positions[5]]
2876 },
2877 ],
2878 mesh.triangles().unwrap().collect::<Vec<Triangle3d>>()
2879 );
2880 }
2881
2882 #[test]
2883 fn take_gpu_data_calculates_aabb() {
2884 let mut mesh = Mesh::new(
2885 PrimitiveTopology::TriangleList,
2886 RenderAssetUsages::default(),
2887 );
2888 mesh.insert_attribute(
2889 Mesh::ATTRIBUTE_POSITION,
2890 vec![
2891 [-0.5, 0., 0.],
2892 [-1., 0., 0.],
2893 [-1., -1., 0.],
2894 [-0.5, -1., 0.],
2895 ],
2896 );
2897 mesh.insert_indices(Indices::U32(vec![0, 1, 2, 2, 3, 0]));
2898 mesh = mesh.take_gpu_data().unwrap();
2899 assert_eq!(
2900 mesh.final_aabb,
2901 Some(Aabb3d::from_min_max([-1., -1., 0.], [-0.5, 0., 0.]))
2902 );
2903 }
2904
2905 #[cfg(feature = "serialize")]
2906 #[test]
2907 fn serialize_deserialize_mesh() {
2908 let mut mesh = Mesh::new(
2909 PrimitiveTopology::TriangleList,
2910 RenderAssetUsages::default(),
2911 );
2912
2913 mesh.insert_attribute(
2914 Mesh::ATTRIBUTE_POSITION,
2915 vec![[0., 0., 0.], [2., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
2916 );
2917 mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));
2918
2919 let serialized_mesh = SerializedMesh::from_mesh(mesh.clone());
2920 let serialized_string = serde_json::to_string(&serialized_mesh).unwrap();
2921 let serialized_mesh_from_string: SerializedMesh =
2922 serde_json::from_str(&serialized_string).unwrap();
2923 let deserialized_mesh = serialized_mesh_from_string.into_mesh();
2924 assert_eq!(mesh, deserialized_mesh);
2925 }
2926}