bevy_mesh/mesh.rs
1use bevy_transform::components::Transform;
2pub use wgpu_types::PrimitiveTopology;
3
4use super::{
5 generate_tangents_for_mesh, scale_normal, triangle_area_normal, triangle_normal, FourIterators,
6 GenerateTangentsError, Indices, MeshAttributeData, MeshTrianglesError, MeshVertexAttribute,
7 MeshVertexAttributeId, MeshVertexBufferLayout, MeshVertexBufferLayoutRef,
8 MeshVertexBufferLayouts, MeshWindingInvertError, VertexAttributeValues, VertexBufferLayout,
9};
10#[cfg(feature = "serialize")]
11use crate::SerializedMeshAttributeData;
12use alloc::collections::BTreeMap;
13use bevy_asset::{Asset, Handle, RenderAssetUsages};
14use bevy_image::Image;
15use bevy_math::{primitives::Triangle3d, *};
16#[cfg(feature = "serialize")]
17use bevy_platform::collections::HashMap;
18use bevy_reflect::Reflect;
19use bytemuck::cast_slice;
20#[cfg(feature = "serialize")]
21use serde::{Deserialize, Serialize};
22use thiserror::Error;
23use tracing::warn;
24use wgpu_types::{VertexAttribute, VertexFormat, VertexStepMode};
25
26pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
27pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
28
29/// A 3D object made out of vertices representing triangles, lines, or points,
30/// with "attribute" values for each vertex.
31///
32/// Meshes can be automatically generated by a bevy `AssetLoader` (generally by loading a `Gltf` file),
33/// or by converting a [primitive](bevy_math::primitives) using [`into`](Into).
34/// It is also possible to create one manually. They can be edited after creation.
35///
36/// Meshes can be rendered with a [`Mesh2d`](crate::Mesh2d) and `MeshMaterial2d`
37/// or [`Mesh3d`](crate::Mesh3d) and `MeshMaterial3d` for 2D and 3D respectively.
38///
39/// A [`Mesh`] in Bevy is equivalent to a "primitive" in the glTF format, for a
40/// glTF Mesh representation, see `GltfMesh`.
41///
42/// ## Manual creation
43///
44/// The following function will construct a flat mesh, to be rendered with a
45/// `StandardMaterial` or `ColorMaterial`:
46///
47/// ```
48/// # use bevy_mesh::{Mesh, Indices, PrimitiveTopology};
49/// # use bevy_asset::RenderAssetUsages;
50/// fn create_simple_parallelogram() -> Mesh {
51/// // Create a new mesh using a triangle list topology, where each set of 3 vertices composes a triangle.
52/// Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default())
53/// // Add 4 vertices, each with its own position attribute (coordinate in
54/// // 3D space), for each of the corners of the parallelogram.
55/// .with_inserted_attribute(
56/// Mesh::ATTRIBUTE_POSITION,
57/// 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]]
58/// )
59/// // Assign a UV coordinate to each vertex.
60/// .with_inserted_attribute(
61/// Mesh::ATTRIBUTE_UV_0,
62/// vec![[0.0, 1.0], [0.5, 0.0], [1.0, 0.0], [0.5, 1.0]]
63/// )
64/// // Assign normals (everything points outwards)
65/// .with_inserted_attribute(
66/// Mesh::ATTRIBUTE_NORMAL,
67/// 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]]
68/// )
69/// // After defining all the vertices and their attributes, build each triangle using the
70/// // indices of the vertices that make it up in a counter-clockwise order.
71/// .with_inserted_indices(Indices::U32(vec![
72/// // First triangle
73/// 0, 3, 1,
74/// // Second triangle
75/// 1, 3, 2
76/// ]))
77/// }
78/// ```
79///
80/// You can see how it looks like [here](https://github.com/bevyengine/bevy/blob/main/assets/docs/Mesh.png),
81/// used in a [`Mesh3d`](crate::Mesh3d) with a square bevy logo texture, with added axis, points,
82/// lines and text for clarity.
83///
84/// ## Other examples
85///
86/// For further visualization, explanation, and examples, see the built-in Bevy examples,
87/// and the [implementation of the built-in shapes](https://github.com/bevyengine/bevy/tree/main/crates/bevy_mesh/src/primitives).
88/// In particular, [generate_custom_mesh](https://github.com/bevyengine/bevy/blob/main/examples/3d/generate_custom_mesh.rs)
89/// teaches you to access and modify the attributes of a [`Mesh`] after creating it.
90///
91/// ## Common points of confusion
92///
93/// - UV maps in Bevy start at the top-left, see [`ATTRIBUTE_UV_0`](Mesh::ATTRIBUTE_UV_0),
94/// other APIs can have other conventions, `OpenGL` starts at bottom-left.
95/// - It is possible and sometimes useful for multiple vertices to have the same
96/// [position attribute](Mesh::ATTRIBUTE_POSITION) value,
97/// it's a common technique in 3D modeling for complex UV mapping or other calculations.
98/// - Bevy performs frustum culling based on the `Aabb` of meshes, which is calculated
99/// and added automatically for new meshes only. If a mesh is modified, the entity's `Aabb`
100/// needs to be updated manually or deleted so that it is re-calculated.
101///
102/// ## Use with `StandardMaterial`
103///
104/// To render correctly with `StandardMaterial`, a mesh needs to have properly defined:
105/// - [`UVs`](Mesh::ATTRIBUTE_UV_0): Bevy needs to know how to map a texture onto the mesh
106/// (also true for `ColorMaterial`).
107/// - [`Normals`](Mesh::ATTRIBUTE_NORMAL): Bevy needs to know how light interacts with your mesh.
108/// [0.0, 0.0, 1.0] is very common for simple flat meshes on the XY plane,
109/// because simple meshes are smooth and they don't require complex light calculations.
110/// - Vertex winding order: by default, `StandardMaterial.cull_mode` is `Some(Face::Back)`,
111/// which means that Bevy would *only* render the "front" of each triangle, which
112/// is the side of the triangle from where the vertices appear in a *counter-clockwise* order.
113///
114/// ## Remote Inspection
115///
116/// To transmit a [`Mesh`] between two running Bevy apps, e.g. through BRP, use [`SerializedMesh`].
117/// This type is only meant for short-term transmission between same versions and should not be stored anywhere.
118#[derive(Asset, Debug, Clone, Reflect, PartialEq)]
119#[reflect(Clone)]
120pub struct Mesh {
121 #[reflect(ignore, clone)]
122 primitive_topology: PrimitiveTopology,
123 /// `std::collections::BTreeMap` with all defined vertex attributes (Positions, Normals, ...)
124 /// for this mesh. Attribute ids to attribute values.
125 /// Uses a [`BTreeMap`] because, unlike `HashMap`, it has a defined iteration order,
126 /// which allows easy stable `VertexBuffers` (i.e. same buffer order)
127 #[reflect(ignore, clone)]
128 attributes: BTreeMap<MeshVertexAttributeId, MeshAttributeData>,
129 indices: Option<Indices>,
130 morph_targets: Option<Handle<Image>>,
131 morph_target_names: Option<Vec<String>>,
132 pub asset_usage: RenderAssetUsages,
133 /// Whether or not to build a BLAS for use with `bevy_solari` raytracing.
134 ///
135 /// Note that this is _not_ whether the mesh is _compatible_ with `bevy_solari` raytracing.
136 /// This field just controls whether or not a BLAS gets built for this mesh, assuming that
137 /// the mesh is compatible.
138 ///
139 /// The use case for this field is using lower-resolution proxy meshes for raytracing (to save on BLAS memory usage),
140 /// while using higher-resolution meshes for raster. You can set this field to true for the lower-resolution proxy mesh,
141 /// and to false for the high-resolution raster mesh.
142 ///
143 /// Alternatively, you can use the same mesh for both raster and raytracing, with this field set to true.
144 ///
145 /// Does nothing if not used with `bevy_solari`, or if the mesh is not compatible
146 /// with `bevy_solari` (see `bevy_solari`'s docs).
147 pub enable_raytracing: bool,
148}
149
150impl Mesh {
151 /// Where the vertex is located in space. Use in conjunction with [`Mesh::insert_attribute`]
152 /// or [`Mesh::with_inserted_attribute`].
153 ///
154 /// The format of this attribute is [`VertexFormat::Float32x3`].
155 pub const ATTRIBUTE_POSITION: MeshVertexAttribute =
156 MeshVertexAttribute::new("Vertex_Position", 0, VertexFormat::Float32x3);
157
158 /// The direction the vertex normal is facing in.
159 /// Use in conjunction with [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`].
160 ///
161 /// The format of this attribute is [`VertexFormat::Float32x3`].
162 pub const ATTRIBUTE_NORMAL: MeshVertexAttribute =
163 MeshVertexAttribute::new("Vertex_Normal", 1, VertexFormat::Float32x3);
164
165 /// Texture coordinates for the vertex. Use in conjunction with [`Mesh::insert_attribute`]
166 /// or [`Mesh::with_inserted_attribute`].
167 ///
168 /// Generally `[0.,0.]` is mapped to the top left of the texture, and `[1.,1.]` to the bottom-right.
169 ///
170 /// By default values outside will be clamped per pixel not for the vertex,
171 /// "stretching" the borders of the texture.
172 /// This behavior can be useful in some cases, usually when the borders have only
173 /// one color, for example a logo, and you want to "extend" those borders.
174 ///
175 /// For different mapping outside of `0..=1` range,
176 /// see [`ImageAddressMode`](bevy_image::ImageAddressMode).
177 ///
178 /// The format of this attribute is [`VertexFormat::Float32x2`].
179 pub const ATTRIBUTE_UV_0: MeshVertexAttribute =
180 MeshVertexAttribute::new("Vertex_Uv", 2, VertexFormat::Float32x2);
181
182 /// Alternate texture coordinates for the vertex. Use in conjunction with
183 /// [`Mesh::insert_attribute`] or [`Mesh::with_inserted_attribute`].
184 ///
185 /// Typically, these are used for lightmaps, textures that provide
186 /// precomputed illumination.
187 ///
188 /// The format of this attribute is [`VertexFormat::Float32x2`].
189 pub const ATTRIBUTE_UV_1: MeshVertexAttribute =
190 MeshVertexAttribute::new("Vertex_Uv_1", 3, VertexFormat::Float32x2);
191
192 /// The direction of the vertex tangent. Used for normal mapping.
193 /// Usually generated with [`generate_tangents`](Mesh::generate_tangents) or
194 /// [`with_generated_tangents`](Mesh::with_generated_tangents).
195 ///
196 /// The format of this attribute is [`VertexFormat::Float32x4`].
197 pub const ATTRIBUTE_TANGENT: MeshVertexAttribute =
198 MeshVertexAttribute::new("Vertex_Tangent", 4, VertexFormat::Float32x4);
199
200 /// Per vertex coloring. Use in conjunction with [`Mesh::insert_attribute`]
201 /// or [`Mesh::with_inserted_attribute`].
202 ///
203 /// The format of this attribute is [`VertexFormat::Float32x4`].
204 pub const ATTRIBUTE_COLOR: MeshVertexAttribute =
205 MeshVertexAttribute::new("Vertex_Color", 5, VertexFormat::Float32x4);
206
207 /// Per vertex joint transform matrix weight. Use in conjunction with [`Mesh::insert_attribute`]
208 /// or [`Mesh::with_inserted_attribute`].
209 ///
210 /// The format of this attribute is [`VertexFormat::Float32x4`].
211 pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute =
212 MeshVertexAttribute::new("Vertex_JointWeight", 6, VertexFormat::Float32x4);
213
214 /// Per vertex joint transform matrix index. Use in conjunction with [`Mesh::insert_attribute`]
215 /// or [`Mesh::with_inserted_attribute`].
216 ///
217 /// The format of this attribute is [`VertexFormat::Uint16x4`].
218 pub const ATTRIBUTE_JOINT_INDEX: MeshVertexAttribute =
219 MeshVertexAttribute::new("Vertex_JointIndex", 7, VertexFormat::Uint16x4);
220
221 /// The first index that can be used for custom vertex attributes.
222 /// Only the attributes with an index below this are used by Bevy.
223 pub const FIRST_AVAILABLE_CUSTOM_ATTRIBUTE: u64 = 8;
224
225 /// Construct a new mesh. You need to provide a [`PrimitiveTopology`] so that the
226 /// renderer knows how to treat the vertex data. Most of the time this will be
227 /// [`PrimitiveTopology::TriangleList`].
228 pub fn new(primitive_topology: PrimitiveTopology, asset_usage: RenderAssetUsages) -> Self {
229 Mesh {
230 primitive_topology,
231 attributes: Default::default(),
232 indices: None,
233 morph_targets: None,
234 morph_target_names: None,
235 asset_usage,
236 enable_raytracing: true,
237 }
238 }
239
240 /// Returns the topology of the mesh.
241 pub fn primitive_topology(&self) -> PrimitiveTopology {
242 self.primitive_topology
243 }
244
245 /// Sets the data for a vertex attribute (position, normal, etc.). The name will
246 /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
247 ///
248 /// `Aabb` of entities with modified mesh are not updated automatically.
249 ///
250 /// # Panics
251 /// Panics when the format of the values does not match the attribute's format.
252 #[inline]
253 pub fn insert_attribute(
254 &mut self,
255 attribute: MeshVertexAttribute,
256 values: impl Into<VertexAttributeValues>,
257 ) {
258 let values = values.into();
259 let values_format = VertexFormat::from(&values);
260 if values_format != attribute.format {
261 panic!(
262 "Failed to insert attribute. Invalid attribute format for {}. Given format is {values_format:?} but expected {:?}",
263 attribute.name, attribute.format
264 );
265 }
266
267 self.attributes
268 .insert(attribute.id, MeshAttributeData { attribute, values });
269 }
270
271 /// Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.).
272 /// The name will often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
273 ///
274 /// (Alternatively, you can use [`Mesh::insert_attribute`] to mutate an existing mesh in-place)
275 ///
276 /// `Aabb` of entities with modified mesh are not updated automatically.
277 ///
278 /// # Panics
279 /// Panics when the format of the values does not match the attribute's format.
280 #[must_use]
281 #[inline]
282 pub fn with_inserted_attribute(
283 mut self,
284 attribute: MeshVertexAttribute,
285 values: impl Into<VertexAttributeValues>,
286 ) -> Self {
287 self.insert_attribute(attribute, values);
288 self
289 }
290
291 /// Removes the data for a vertex attribute
292 pub fn remove_attribute(
293 &mut self,
294 attribute: impl Into<MeshVertexAttributeId>,
295 ) -> Option<VertexAttributeValues> {
296 self.attributes
297 .remove(&attribute.into())
298 .map(|data| data.values)
299 }
300
301 /// Consumes the mesh and returns a mesh without the data for a vertex attribute
302 ///
303 /// (Alternatively, you can use [`Mesh::remove_attribute`] to mutate an existing mesh in-place)
304 #[must_use]
305 pub fn with_removed_attribute(mut self, attribute: impl Into<MeshVertexAttributeId>) -> Self {
306 self.remove_attribute(attribute);
307 self
308 }
309
310 #[inline]
311 pub fn contains_attribute(&self, id: impl Into<MeshVertexAttributeId>) -> bool {
312 self.attributes.contains_key(&id.into())
313 }
314
315 /// Retrieves the data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].
316 #[inline]
317 pub fn attribute(
318 &self,
319 id: impl Into<MeshVertexAttributeId>,
320 ) -> Option<&VertexAttributeValues> {
321 self.attributes.get(&id.into()).map(|data| &data.values)
322 }
323
324 /// Retrieves the full data currently set to the vertex attribute with the specified [`MeshVertexAttributeId`].
325 #[inline]
326 pub(crate) fn attribute_data(
327 &self,
328 id: impl Into<MeshVertexAttributeId>,
329 ) -> Option<&MeshAttributeData> {
330 self.attributes.get(&id.into())
331 }
332
333 /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.
334 #[inline]
335 pub fn attribute_mut(
336 &mut self,
337 id: impl Into<MeshVertexAttributeId>,
338 ) -> Option<&mut VertexAttributeValues> {
339 self.attributes
340 .get_mut(&id.into())
341 .map(|data| &mut data.values)
342 }
343
344 /// Returns an iterator that yields references to the data of each vertex attribute.
345 pub fn attributes(
346 &self,
347 ) -> impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)> {
348 self.attributes
349 .values()
350 .map(|data| (&data.attribute, &data.values))
351 }
352
353 /// Returns an iterator that yields mutable references to the data of each vertex attribute.
354 pub fn attributes_mut(
355 &mut self,
356 ) -> impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)> {
357 self.attributes
358 .values_mut()
359 .map(|data| (&data.attribute, &mut data.values))
360 }
361
362 /// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the
363 /// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants
364 /// that use triangles.
365 #[inline]
366 pub fn insert_indices(&mut self, indices: Indices) {
367 self.indices = Some(indices);
368 }
369
370 /// Consumes the mesh and returns a mesh with the given vertex indices. They describe how triangles
371 /// are constructed out of the vertex attributes and are therefore only useful for the
372 /// [`PrimitiveTopology`] variants that use triangles.
373 ///
374 /// (Alternatively, you can use [`Mesh::insert_indices`] to mutate an existing mesh in-place)
375 #[must_use]
376 #[inline]
377 pub fn with_inserted_indices(mut self, indices: Indices) -> Self {
378 self.insert_indices(indices);
379 self
380 }
381
382 /// Retrieves the vertex `indices` of the mesh.
383 #[inline]
384 pub fn indices(&self) -> Option<&Indices> {
385 self.indices.as_ref()
386 }
387
388 /// Retrieves the vertex `indices` of the mesh mutably.
389 #[inline]
390 pub fn indices_mut(&mut self) -> Option<&mut Indices> {
391 self.indices.as_mut()
392 }
393
394 /// Removes the vertex `indices` from the mesh and returns them.
395 #[inline]
396 pub fn remove_indices(&mut self) -> Option<Indices> {
397 core::mem::take(&mut self.indices)
398 }
399
400 /// Consumes the mesh and returns a mesh without the vertex `indices` of the mesh.
401 ///
402 /// (Alternatively, you can use [`Mesh::remove_indices`] to mutate an existing mesh in-place)
403 #[must_use]
404 pub fn with_removed_indices(mut self) -> Self {
405 self.remove_indices();
406 self
407 }
408
409 /// Returns the size of a vertex in bytes.
410 pub fn get_vertex_size(&self) -> u64 {
411 self.attributes
412 .values()
413 .map(|data| data.attribute.format.size())
414 .sum()
415 }
416
417 /// Returns the size required for the vertex buffer in bytes.
418 pub fn get_vertex_buffer_size(&self) -> usize {
419 let vertex_size = self.get_vertex_size() as usize;
420 let vertex_count = self.count_vertices();
421 vertex_count * vertex_size
422 }
423
424 /// Computes and returns the index data of the mesh as bytes.
425 /// This is used to transform the index data into a GPU friendly format.
426 pub fn get_index_buffer_bytes(&self) -> Option<&[u8]> {
427 self.indices.as_ref().map(|indices| match &indices {
428 Indices::U16(indices) => cast_slice(&indices[..]),
429 Indices::U32(indices) => cast_slice(&indices[..]),
430 })
431 }
432
433 /// Get this `Mesh`'s [`MeshVertexBufferLayout`], used in `SpecializedMeshPipeline`.
434 pub fn get_mesh_vertex_buffer_layout(
435 &self,
436 mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,
437 ) -> MeshVertexBufferLayoutRef {
438 let mut attributes = Vec::with_capacity(self.attributes.len());
439 let mut attribute_ids = Vec::with_capacity(self.attributes.len());
440 let mut accumulated_offset = 0;
441 for (index, data) in self.attributes.values().enumerate() {
442 attribute_ids.push(data.attribute.id);
443 attributes.push(VertexAttribute {
444 offset: accumulated_offset,
445 format: data.attribute.format,
446 shader_location: index as u32,
447 });
448 accumulated_offset += data.attribute.format.size();
449 }
450
451 let layout = MeshVertexBufferLayout {
452 layout: VertexBufferLayout {
453 array_stride: accumulated_offset,
454 step_mode: VertexStepMode::Vertex,
455 attributes,
456 },
457 attribute_ids,
458 };
459 mesh_vertex_buffer_layouts.insert(layout)
460 }
461
462 /// Counts all vertices of the mesh.
463 ///
464 /// If the attributes have different vertex counts, the smallest is returned.
465 pub fn count_vertices(&self) -> usize {
466 let mut vertex_count: Option<usize> = None;
467 for (attribute_id, attribute_data) in &self.attributes {
468 let attribute_len = attribute_data.values.len();
469 if let Some(previous_vertex_count) = vertex_count {
470 if previous_vertex_count != attribute_len {
471 let name = self
472 .attributes
473 .get(attribute_id)
474 .map(|data| data.attribute.name.to_string())
475 .unwrap_or_else(|| format!("{attribute_id:?}"));
476
477 warn!("{name} has a different vertex count ({attribute_len}) than other attributes ({previous_vertex_count}) in this mesh, \
478 all attributes will be truncated to match the smallest.");
479 vertex_count = Some(core::cmp::min(previous_vertex_count, attribute_len));
480 }
481 } else {
482 vertex_count = Some(attribute_len);
483 }
484 }
485
486 vertex_count.unwrap_or(0)
487 }
488
489 /// Computes and returns the vertex data of the mesh as bytes.
490 /// Therefore the attributes are located in the order of their [`MeshVertexAttribute::id`].
491 /// This is used to transform the vertex data into a GPU friendly format.
492 ///
493 /// If the vertex attributes have different lengths, they are all truncated to
494 /// the length of the smallest.
495 ///
496 /// This is a convenience method which allocates a Vec.
497 /// Prefer pre-allocating and using [`Mesh::write_packed_vertex_buffer_data`] when possible.
498 pub fn create_packed_vertex_buffer_data(&self) -> Vec<u8> {
499 let mut attributes_interleaved_buffer = vec![0; self.get_vertex_buffer_size()];
500 self.write_packed_vertex_buffer_data(&mut attributes_interleaved_buffer);
501 attributes_interleaved_buffer
502 }
503
504 /// Computes and write the vertex data of the mesh into a mutable byte slice.
505 /// The attributes are located in the order of their [`MeshVertexAttribute::id`].
506 /// This is used to transform the vertex data into a GPU friendly format.
507 ///
508 /// If the vertex attributes have different lengths, they are all truncated to
509 /// the length of the smallest.
510 pub fn write_packed_vertex_buffer_data(&self, slice: &mut [u8]) {
511 let vertex_size = self.get_vertex_size() as usize;
512 let vertex_count = self.count_vertices();
513 // bundle into interleaved buffers
514 let mut attribute_offset = 0;
515 for attribute_data in self.attributes.values() {
516 let attribute_size = attribute_data.attribute.format.size() as usize;
517 let attributes_bytes = attribute_data.values.get_bytes();
518 for (vertex_index, attribute_bytes) in attributes_bytes
519 .chunks_exact(attribute_size)
520 .take(vertex_count)
521 .enumerate()
522 {
523 let offset = vertex_index * vertex_size + attribute_offset;
524 slice[offset..offset + attribute_size].copy_from_slice(attribute_bytes);
525 }
526
527 attribute_offset += attribute_size;
528 }
529 }
530
531 /// Duplicates the vertex attributes so that no vertices are shared.
532 ///
533 /// This can dramatically increase the vertex count, so make sure this is what you want.
534 /// Does nothing if no [Indices] are set.
535 pub fn duplicate_vertices(&mut self) {
536 fn duplicate<T: Copy>(values: &[T], indices: impl Iterator<Item = usize>) -> Vec<T> {
537 indices.map(|i| values[i]).collect()
538 }
539
540 let Some(indices) = self.indices.take() else {
541 return;
542 };
543
544 for attributes in self.attributes.values_mut() {
545 let indices = indices.iter();
546 #[expect(
547 clippy::match_same_arms,
548 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."
549 )]
550 match &mut attributes.values {
551 VertexAttributeValues::Float32(vec) => *vec = duplicate(vec, indices),
552 VertexAttributeValues::Sint32(vec) => *vec = duplicate(vec, indices),
553 VertexAttributeValues::Uint32(vec) => *vec = duplicate(vec, indices),
554 VertexAttributeValues::Float32x2(vec) => *vec = duplicate(vec, indices),
555 VertexAttributeValues::Sint32x2(vec) => *vec = duplicate(vec, indices),
556 VertexAttributeValues::Uint32x2(vec) => *vec = duplicate(vec, indices),
557 VertexAttributeValues::Float32x3(vec) => *vec = duplicate(vec, indices),
558 VertexAttributeValues::Sint32x3(vec) => *vec = duplicate(vec, indices),
559 VertexAttributeValues::Uint32x3(vec) => *vec = duplicate(vec, indices),
560 VertexAttributeValues::Sint32x4(vec) => *vec = duplicate(vec, indices),
561 VertexAttributeValues::Uint32x4(vec) => *vec = duplicate(vec, indices),
562 VertexAttributeValues::Float32x4(vec) => *vec = duplicate(vec, indices),
563 VertexAttributeValues::Sint16x2(vec) => *vec = duplicate(vec, indices),
564 VertexAttributeValues::Snorm16x2(vec) => *vec = duplicate(vec, indices),
565 VertexAttributeValues::Uint16x2(vec) => *vec = duplicate(vec, indices),
566 VertexAttributeValues::Unorm16x2(vec) => *vec = duplicate(vec, indices),
567 VertexAttributeValues::Sint16x4(vec) => *vec = duplicate(vec, indices),
568 VertexAttributeValues::Snorm16x4(vec) => *vec = duplicate(vec, indices),
569 VertexAttributeValues::Uint16x4(vec) => *vec = duplicate(vec, indices),
570 VertexAttributeValues::Unorm16x4(vec) => *vec = duplicate(vec, indices),
571 VertexAttributeValues::Sint8x2(vec) => *vec = duplicate(vec, indices),
572 VertexAttributeValues::Snorm8x2(vec) => *vec = duplicate(vec, indices),
573 VertexAttributeValues::Uint8x2(vec) => *vec = duplicate(vec, indices),
574 VertexAttributeValues::Unorm8x2(vec) => *vec = duplicate(vec, indices),
575 VertexAttributeValues::Sint8x4(vec) => *vec = duplicate(vec, indices),
576 VertexAttributeValues::Snorm8x4(vec) => *vec = duplicate(vec, indices),
577 VertexAttributeValues::Uint8x4(vec) => *vec = duplicate(vec, indices),
578 VertexAttributeValues::Unorm8x4(vec) => *vec = duplicate(vec, indices),
579 }
580 }
581 }
582
583 /// Consumes the mesh and returns a mesh with no shared vertices.
584 ///
585 /// This can dramatically increase the vertex count, so make sure this is what you want.
586 /// Does nothing if no [`Indices`] are set.
587 ///
588 /// (Alternatively, you can use [`Mesh::duplicate_vertices`] to mutate an existing mesh in-place)
589 #[must_use]
590 pub fn with_duplicated_vertices(mut self) -> Self {
591 self.duplicate_vertices();
592 self
593 }
594
595 /// Inverts the winding of the indices such that all counter-clockwise triangles are now
596 /// clockwise and vice versa.
597 /// For lines, their start and end indices are flipped.
598 ///
599 /// Does nothing if no [`Indices`] are set.
600 /// If this operation succeeded, an [`Ok`] result is returned.
601 pub fn invert_winding(&mut self) -> Result<(), MeshWindingInvertError> {
602 fn invert<I>(
603 indices: &mut [I],
604 topology: PrimitiveTopology,
605 ) -> Result<(), MeshWindingInvertError> {
606 match topology {
607 PrimitiveTopology::TriangleList => {
608 // Early return if the index count doesn't match
609 if !indices.len().is_multiple_of(3) {
610 return Err(MeshWindingInvertError::AbruptIndicesEnd);
611 }
612 for chunk in indices.chunks_mut(3) {
613 // This currently can only be optimized away with unsafe, rework this when `feature(slice_as_chunks)` gets stable.
614 let [_, b, c] = chunk else {
615 return Err(MeshWindingInvertError::AbruptIndicesEnd);
616 };
617 core::mem::swap(b, c);
618 }
619 Ok(())
620 }
621 PrimitiveTopology::LineList => {
622 // Early return if the index count doesn't match
623 if !indices.len().is_multiple_of(2) {
624 return Err(MeshWindingInvertError::AbruptIndicesEnd);
625 }
626 indices.reverse();
627 Ok(())
628 }
629 PrimitiveTopology::TriangleStrip | PrimitiveTopology::LineStrip => {
630 indices.reverse();
631 Ok(())
632 }
633 _ => Err(MeshWindingInvertError::WrongTopology),
634 }
635 }
636 match &mut self.indices {
637 Some(Indices::U16(vec)) => invert(vec, self.primitive_topology),
638 Some(Indices::U32(vec)) => invert(vec, self.primitive_topology),
639 None => Ok(()),
640 }
641 }
642
643 /// Consumes the mesh and returns a mesh with inverted winding of the indices such
644 /// that all counter-clockwise triangles are now clockwise and vice versa.
645 ///
646 /// Does nothing if no [`Indices`] are set.
647 pub fn with_inverted_winding(mut self) -> Result<Self, MeshWindingInvertError> {
648 self.invert_winding().map(|_| self)
649 }
650
651 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
652 /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
653 /// normals.
654 ///
655 /// # Panics
656 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
657 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].=
658 pub fn compute_normals(&mut self) {
659 assert!(
660 matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
661 "`compute_normals` can only work on `TriangleList`s"
662 );
663 if self.indices().is_none() {
664 self.compute_flat_normals();
665 } else {
666 self.compute_smooth_normals();
667 }
668 }
669
670 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
671 ///
672 /// # Panics
673 /// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
674 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
675 /// Consider calling [`Mesh::duplicate_vertices`] or exporting your mesh with normal
676 /// attributes.
677 ///
678 /// FIXME: This should handle more cases since this is called as a part of gltf
679 /// mesh loading where we can't really blame users for loading meshes that might
680 /// not conform to the limitations here!
681 pub fn compute_flat_normals(&mut self) {
682 assert!(
683 self.indices().is_none(),
684 "`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`."
685 );
686 assert!(
687 matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
688 "`compute_flat_normals` can only work on `TriangleList`s"
689 );
690
691 let positions = self
692 .attribute(Mesh::ATTRIBUTE_POSITION)
693 .unwrap()
694 .as_float3()
695 .expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");
696
697 let normals: Vec<_> = positions
698 .chunks_exact(3)
699 .map(|p| triangle_normal(p[0], p[1], p[2]))
700 .flat_map(|normal| [normal; 3])
701 .collect();
702
703 self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
704 }
705
706 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
707 /// vertices.
708 ///
709 /// This method weights normals by the angles of the corners of connected triangles, thus
710 /// eliminating triangle area and count as factors in the final normal. This does make it
711 /// somewhat slower than [`Mesh::compute_area_weighted_normals`] which does not need to
712 /// greedily normalize each triangle's normal or calculate corner angles.
713 ///
714 /// If you would rather have the computed normals be weighted by triangle area, see
715 /// [`Mesh::compute_area_weighted_normals`] instead. If you need to weight them in some other
716 /// way, see [`Mesh::compute_custom_smooth_normals`].
717 ///
718 /// # Panics
719 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
720 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
721 /// Panics if the mesh does not have indices defined.
722 pub fn compute_smooth_normals(&mut self) {
723 self.compute_custom_smooth_normals(|[a, b, c], positions, normals| {
724 let pa = Vec3::from(positions[a]);
725 let pb = Vec3::from(positions[b]);
726 let pc = Vec3::from(positions[c]);
727
728 let ab = pb - pa;
729 let ba = pa - pb;
730 let bc = pc - pb;
731 let cb = pb - pc;
732 let ca = pa - pc;
733 let ac = pc - pa;
734
735 const EPS: f32 = f32::EPSILON;
736 let weight_a = if ab.length_squared() * ac.length_squared() > EPS {
737 ab.angle_between(ac)
738 } else {
739 0.0
740 };
741 let weight_b = if ba.length_squared() * bc.length_squared() > EPS {
742 ba.angle_between(bc)
743 } else {
744 0.0
745 };
746 let weight_c = if ca.length_squared() * cb.length_squared() > EPS {
747 ca.angle_between(cb)
748 } else {
749 0.0
750 };
751
752 let normal = Vec3::from(triangle_normal(positions[a], positions[b], positions[c]));
753
754 normals[a] += normal * weight_a;
755 normals[b] += normal * weight_b;
756 normals[c] += normal * weight_c;
757 });
758 }
759
760 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
761 /// vertices.
762 ///
763 /// This method weights normals by the area of each triangle containing the vertex. Thus,
764 /// larger triangles will skew the normals of their vertices towards their own normal more
765 /// than smaller triangles will.
766 ///
767 /// This method is actually somewhat faster than [`Mesh::compute_smooth_normals`] because an
768 /// intermediate result of triangle normal calculation is already scaled by the triangle's area.
769 ///
770 /// If you would rather have the computed normals be influenced only by the angles of connected
771 /// edges, see [`Mesh::compute_smooth_normals`] instead. If you need to weight them in some
772 /// other way, see [`Mesh::compute_custom_smooth_normals`].
773 ///
774 /// # Panics
775 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
776 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
777 /// Panics if the mesh does not have indices defined.
778 pub fn compute_area_weighted_normals(&mut self) {
779 self.compute_custom_smooth_normals(|[a, b, c], positions, normals| {
780 let normal = Vec3::from(triangle_area_normal(
781 positions[a],
782 positions[b],
783 positions[c],
784 ));
785 [a, b, c].into_iter().for_each(|pos| {
786 normals[pos] += normal;
787 });
788 });
789 }
790
791 /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared
792 /// vertices.
793 ///
794 /// This method allows you to customize how normals are weighted via the `per_triangle` parameter,
795 /// which must be a function or closure that accepts 3 parameters:
796 /// - The indices of the three vertices of the triangle as a `[usize; 3]`.
797 /// - A reference to the values of the [`Mesh::ATTRIBUTE_POSITION`] of the mesh (`&[[f32; 3]]`).
798 /// - A mutable reference to the sums of all normals so far.
799 ///
800 /// See also the standard methods included in Bevy for calculating smooth normals:
801 /// - [`Mesh::compute_smooth_normals`]
802 /// - [`Mesh::compute_area_weighted_normals`]
803 ///
804 /// An example that would weight each connected triangle's normal equally, thus skewing normals
805 /// towards the planes divided into the most triangles:
806 /// ```
807 /// # use bevy_asset::RenderAssetUsages;
808 /// # use bevy_mesh::{Mesh, PrimitiveTopology, Meshable, MeshBuilder};
809 /// # use bevy_math::{Vec3, primitives::Cuboid};
810 /// # let mut mesh = Cuboid::default().mesh().build();
811 /// mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| {
812 /// let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c]));
813 /// for idx in [a, b, c] {
814 /// normals[idx] += normal;
815 /// }
816 /// });
817 /// ```
818 ///
819 /// # Panics
820 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
821 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
822 /// Panics if the mesh does not have indices defined.
823 //
824 // FIXME: This should handle more cases since this is called as a part of gltf
825 // mesh loading where we can't really blame users for loading meshes that might
826 // not conform to the limitations here!
827 //
828 // When fixed, also update "Panics" sections of
829 // - [Mesh::compute_smooth_normals]
830 // - [Mesh::with_computed_smooth_normals]
831 // - [Mesh::compute_area_weighted_normals]
832 // - [Mesh::with_computed_area_weighted_normals]
833 pub fn compute_custom_smooth_normals(
834 &mut self,
835 mut per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]),
836 ) {
837 assert!(
838 matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
839 "smooth normals can only be computed on `TriangleList`s"
840 );
841 assert!(
842 self.indices().is_some(),
843 "smooth normals can only be computed on indexed meshes"
844 );
845
846 let positions = self
847 .attribute(Mesh::ATTRIBUTE_POSITION)
848 .unwrap()
849 .as_float3()
850 .expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");
851
852 let mut normals = vec![Vec3::ZERO; positions.len()];
853
854 self.indices()
855 .unwrap()
856 .iter()
857 .collect::<Vec<usize>>()
858 .chunks_exact(3)
859 .for_each(|face| per_triangle([face[0], face[1], face[2]], positions, &mut normals));
860
861 for normal in &mut normals {
862 *normal = normal.try_normalize().unwrap_or(Vec3::ZERO);
863 }
864
865 self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
866 }
867
868 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
869 /// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
870 /// normals.
871 ///
872 /// (Alternatively, you can use [`Mesh::compute_normals`] to mutate an existing mesh in-place)
873 ///
874 /// # Panics
875 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
876 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
877 #[must_use]
878 pub fn with_computed_normals(mut self) -> Self {
879 self.compute_normals();
880 self
881 }
882
883 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
884 ///
885 /// (Alternatively, you can use [`Mesh::compute_flat_normals`] to mutate an existing mesh in-place)
886 ///
887 /// # Panics
888 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
889 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
890 /// Panics if the mesh has indices defined
891 #[must_use]
892 pub fn with_computed_flat_normals(mut self) -> Self {
893 self.compute_flat_normals();
894 self
895 }
896
897 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
898 ///
899 /// (Alternatively, you can use [`Mesh::compute_smooth_normals`] to mutate an existing mesh in-place)
900 ///
901 /// This method weights normals by the angles of triangle corners connected to each vertex. If
902 /// you would rather have the computed normals be weighted by triangle area, see
903 /// [`Mesh::with_computed_area_weighted_normals`] instead.
904 ///
905 /// # Panics
906 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
907 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
908 /// Panics if the mesh does not have indices defined.
909 #[must_use]
910 pub fn with_computed_smooth_normals(mut self) -> Self {
911 self.compute_smooth_normals();
912 self
913 }
914
915 /// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
916 ///
917 /// (Alternatively, you can use [`Mesh::compute_area_weighted_normals`] to mutate an existing mesh in-place)
918 ///
919 /// This method weights normals by the area of each triangle containing the vertex. Thus,
920 /// larger triangles will skew the normals of their vertices towards their own normal more
921 /// than smaller triangles will. If you would rather have the computed normals be influenced
922 /// only by the angles of connected edges, see [`Mesh::with_computed_smooth_normals`] instead.
923 ///
924 /// # Panics
925 /// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
926 /// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
927 /// Panics if the mesh does not have indices defined.
928 #[must_use]
929 pub fn with_computed_area_weighted_normals(mut self) -> Self {
930 self.compute_area_weighted_normals();
931 self
932 }
933
934 /// Generate tangents for the mesh using the `mikktspace` algorithm.
935 ///
936 /// Sets the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful.
937 /// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set.
938 pub fn generate_tangents(&mut self) -> Result<(), GenerateTangentsError> {
939 let tangents = generate_tangents_for_mesh(self)?;
940 self.insert_attribute(Mesh::ATTRIBUTE_TANGENT, tangents);
941 Ok(())
942 }
943
944 /// Consumes the mesh and returns a mesh with tangents generated using the `mikktspace` algorithm.
945 ///
946 /// The resulting mesh will have the [`Mesh::ATTRIBUTE_TANGENT`] attribute if successful.
947 ///
948 /// (Alternatively, you can use [`Mesh::generate_tangents`] to mutate an existing mesh in-place)
949 ///
950 /// Requires a [`PrimitiveTopology::TriangleList`] topology and the [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes set.
951 pub fn with_generated_tangents(mut self) -> Result<Mesh, GenerateTangentsError> {
952 self.generate_tangents()?;
953 Ok(self)
954 }
955
956 /// Merges the [`Mesh`] data of `other` with `self`. The attributes and indices of `other` will be appended to `self`.
957 ///
958 /// Note that attributes of `other` that don't exist on `self` will be ignored.
959 ///
960 /// `Aabb` of entities with modified mesh are not updated automatically.
961 ///
962 /// # Errors
963 ///
964 /// If any of the following conditions are not met, this function errors:
965 /// * All of the vertex attributes that have the same attribute id, must also
966 /// have the same attribute type.
967 /// For example two attributes with the same id, but where one is a
968 /// [`VertexAttributeValues::Float32`] and the other is a
969 /// [`VertexAttributeValues::Float32x3`], would be invalid.
970 /// * Both meshes must have the same primitive topology.
971 pub fn merge(&mut self, other: &Mesh) -> Result<(), MeshMergeError> {
972 use VertexAttributeValues::*;
973
974 // Check if the meshes `primitive_topology` field is the same,
975 // as if that is not the case, the resulting mesh could (and most likely would)
976 // be invalid.
977 if self.primitive_topology != other.primitive_topology {
978 return Err(MeshMergeError::IncompatiblePrimitiveTopology {
979 self_primitive_topology: self.primitive_topology,
980 other_primitive_topology: other.primitive_topology,
981 });
982 }
983
984 // The indices of `other` should start after the last vertex of `self`.
985 let index_offset = self.count_vertices();
986
987 // Extend attributes of `self` with attributes of `other`.
988 for (attribute, values) in self.attributes_mut() {
989 if let Some(other_values) = other.attribute(attribute.id) {
990 #[expect(
991 clippy::match_same_arms,
992 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."
993 )]
994 match (values, other_values) {
995 (Float32(vec1), Float32(vec2)) => vec1.extend(vec2),
996 (Sint32(vec1), Sint32(vec2)) => vec1.extend(vec2),
997 (Uint32(vec1), Uint32(vec2)) => vec1.extend(vec2),
998 (Float32x2(vec1), Float32x2(vec2)) => vec1.extend(vec2),
999 (Sint32x2(vec1), Sint32x2(vec2)) => vec1.extend(vec2),
1000 (Uint32x2(vec1), Uint32x2(vec2)) => vec1.extend(vec2),
1001 (Float32x3(vec1), Float32x3(vec2)) => vec1.extend(vec2),
1002 (Sint32x3(vec1), Sint32x3(vec2)) => vec1.extend(vec2),
1003 (Uint32x3(vec1), Uint32x3(vec2)) => vec1.extend(vec2),
1004 (Sint32x4(vec1), Sint32x4(vec2)) => vec1.extend(vec2),
1005 (Uint32x4(vec1), Uint32x4(vec2)) => vec1.extend(vec2),
1006 (Float32x4(vec1), Float32x4(vec2)) => vec1.extend(vec2),
1007 (Sint16x2(vec1), Sint16x2(vec2)) => vec1.extend(vec2),
1008 (Snorm16x2(vec1), Snorm16x2(vec2)) => vec1.extend(vec2),
1009 (Uint16x2(vec1), Uint16x2(vec2)) => vec1.extend(vec2),
1010 (Unorm16x2(vec1), Unorm16x2(vec2)) => vec1.extend(vec2),
1011 (Sint16x4(vec1), Sint16x4(vec2)) => vec1.extend(vec2),
1012 (Snorm16x4(vec1), Snorm16x4(vec2)) => vec1.extend(vec2),
1013 (Uint16x4(vec1), Uint16x4(vec2)) => vec1.extend(vec2),
1014 (Unorm16x4(vec1), Unorm16x4(vec2)) => vec1.extend(vec2),
1015 (Sint8x2(vec1), Sint8x2(vec2)) => vec1.extend(vec2),
1016 (Snorm8x2(vec1), Snorm8x2(vec2)) => vec1.extend(vec2),
1017 (Uint8x2(vec1), Uint8x2(vec2)) => vec1.extend(vec2),
1018 (Unorm8x2(vec1), Unorm8x2(vec2)) => vec1.extend(vec2),
1019 (Sint8x4(vec1), Sint8x4(vec2)) => vec1.extend(vec2),
1020 (Snorm8x4(vec1), Snorm8x4(vec2)) => vec1.extend(vec2),
1021 (Uint8x4(vec1), Uint8x4(vec2)) => vec1.extend(vec2),
1022 (Unorm8x4(vec1), Unorm8x4(vec2)) => vec1.extend(vec2),
1023 _ => {
1024 return Err(MeshMergeError::IncompatibleVertexAttributes {
1025 self_attribute: *attribute,
1026 other_attribute: other
1027 .attribute_data(attribute.id)
1028 .map(|data| data.attribute),
1029 })
1030 }
1031 }
1032 }
1033 }
1034
1035 // Extend indices of `self` with indices of `other`.
1036 if let (Some(indices), Some(other_indices)) = (self.indices_mut(), other.indices()) {
1037 indices.extend(other_indices.iter().map(|i| (i + index_offset) as u32));
1038 }
1039 Ok(())
1040 }
1041
1042 /// Transforms the vertex positions, normals, and tangents of the mesh by the given [`Transform`].
1043 ///
1044 /// `Aabb` of entities with modified mesh are not updated automatically.
1045 pub fn transformed_by(mut self, transform: Transform) -> Self {
1046 self.transform_by(transform);
1047 self
1048 }
1049
1050 /// Transforms the vertex positions, normals, and tangents of the mesh in place by the given [`Transform`].
1051 ///
1052 /// `Aabb` of entities with modified mesh are not updated automatically.
1053 pub fn transform_by(&mut self, transform: Transform) {
1054 // Needed when transforming normals and tangents
1055 let scale_recip = 1. / transform.scale;
1056 debug_assert!(
1057 transform.scale.yzx() * transform.scale.zxy() != Vec3::ZERO,
1058 "mesh transform scale cannot be zero on more than one axis"
1059 );
1060
1061 if let Some(VertexAttributeValues::Float32x3(positions)) =
1062 self.attribute_mut(Mesh::ATTRIBUTE_POSITION)
1063 {
1064 // Apply scale, rotation, and translation to vertex positions
1065 positions
1066 .iter_mut()
1067 .for_each(|pos| *pos = transform.transform_point(Vec3::from_slice(pos)).to_array());
1068 }
1069
1070 // No need to transform normals or tangents if rotation is near identity and scale is uniform
1071 if transform.rotation.is_near_identity()
1072 && transform.scale.x == transform.scale.y
1073 && transform.scale.y == transform.scale.z
1074 {
1075 return;
1076 }
1077
1078 if let Some(VertexAttributeValues::Float32x3(normals)) =
1079 self.attribute_mut(Mesh::ATTRIBUTE_NORMAL)
1080 {
1081 // Transform normals, taking into account non-uniform scaling and rotation
1082 normals.iter_mut().for_each(|normal| {
1083 *normal = (transform.rotation
1084 * scale_normal(Vec3::from_array(*normal), scale_recip))
1085 .to_array();
1086 });
1087 }
1088
1089 if let Some(VertexAttributeValues::Float32x4(tangents)) =
1090 self.attribute_mut(Mesh::ATTRIBUTE_TANGENT)
1091 {
1092 // Transform tangents, taking into account non-uniform scaling and rotation
1093 tangents.iter_mut().for_each(|tangent| {
1094 let handedness = tangent[3];
1095 let scaled_tangent = Vec3::from_slice(tangent) * transform.scale;
1096 *tangent = (transform.rotation * scaled_tangent.normalize_or_zero())
1097 .extend(handedness)
1098 .to_array();
1099 });
1100 }
1101 }
1102
1103 /// Translates the vertex positions of the mesh by the given [`Vec3`].
1104 ///
1105 /// `Aabb` of entities with modified mesh are not updated automatically.
1106 pub fn translated_by(mut self, translation: Vec3) -> Self {
1107 self.translate_by(translation);
1108 self
1109 }
1110
1111 /// Translates the vertex positions of the mesh in place by the given [`Vec3`].
1112 ///
1113 /// `Aabb` of entities with modified mesh are not updated automatically.
1114 pub fn translate_by(&mut self, translation: Vec3) {
1115 if translation == Vec3::ZERO {
1116 return;
1117 }
1118
1119 if let Some(VertexAttributeValues::Float32x3(positions)) =
1120 self.attribute_mut(Mesh::ATTRIBUTE_POSITION)
1121 {
1122 // Apply translation to vertex positions
1123 positions
1124 .iter_mut()
1125 .for_each(|pos| *pos = (Vec3::from_slice(pos) + translation).to_array());
1126 }
1127 }
1128
1129 /// Rotates the vertex positions, normals, and tangents of the mesh by the given [`Quat`].
1130 ///
1131 /// `Aabb` of entities with modified mesh are not updated automatically.
1132 pub fn rotated_by(mut self, rotation: Quat) -> Self {
1133 self.rotate_by(rotation);
1134 self
1135 }
1136
1137 /// Rotates the vertex positions, normals, and tangents of the mesh in place by the given [`Quat`].
1138 ///
1139 /// `Aabb` of entities with modified mesh are not updated automatically.
1140 pub fn rotate_by(&mut self, rotation: Quat) {
1141 if let Some(VertexAttributeValues::Float32x3(positions)) =
1142 self.attribute_mut(Mesh::ATTRIBUTE_POSITION)
1143 {
1144 // Apply rotation to vertex positions
1145 positions
1146 .iter_mut()
1147 .for_each(|pos| *pos = (rotation * Vec3::from_slice(pos)).to_array());
1148 }
1149
1150 // No need to transform normals or tangents if rotation is near identity
1151 if rotation.is_near_identity() {
1152 return;
1153 }
1154
1155 if let Some(VertexAttributeValues::Float32x3(normals)) =
1156 self.attribute_mut(Mesh::ATTRIBUTE_NORMAL)
1157 {
1158 // Transform normals
1159 normals.iter_mut().for_each(|normal| {
1160 *normal = (rotation * Vec3::from_slice(normal).normalize_or_zero()).to_array();
1161 });
1162 }
1163
1164 if let Some(VertexAttributeValues::Float32x4(tangents)) =
1165 self.attribute_mut(Mesh::ATTRIBUTE_TANGENT)
1166 {
1167 // Transform tangents
1168 tangents.iter_mut().for_each(|tangent| {
1169 let handedness = tangent[3];
1170 *tangent = (rotation * Vec3::from_slice(tangent).normalize_or_zero())
1171 .extend(handedness)
1172 .to_array();
1173 });
1174 }
1175 }
1176
1177 /// Scales the vertex positions, normals, and tangents of the mesh by the given [`Vec3`].
1178 ///
1179 /// `Aabb` of entities with modified mesh are not updated automatically.
1180 pub fn scaled_by(mut self, scale: Vec3) -> Self {
1181 self.scale_by(scale);
1182 self
1183 }
1184
1185 /// Scales the vertex positions, normals, and tangents of the mesh in place by the given [`Vec3`].
1186 ///
1187 /// `Aabb` of entities with modified mesh are not updated automatically.
1188 pub fn scale_by(&mut self, scale: Vec3) {
1189 // Needed when transforming normals and tangents
1190 let scale_recip = 1. / scale;
1191 debug_assert!(
1192 scale.yzx() * scale.zxy() != Vec3::ZERO,
1193 "mesh transform scale cannot be zero on more than one axis"
1194 );
1195
1196 if let Some(VertexAttributeValues::Float32x3(positions)) =
1197 self.attribute_mut(Mesh::ATTRIBUTE_POSITION)
1198 {
1199 // Apply scale to vertex positions
1200 positions
1201 .iter_mut()
1202 .for_each(|pos| *pos = (scale * Vec3::from_slice(pos)).to_array());
1203 }
1204
1205 // No need to transform normals or tangents if scale is uniform
1206 if scale.x == scale.y && scale.y == scale.z {
1207 return;
1208 }
1209
1210 if let Some(VertexAttributeValues::Float32x3(normals)) =
1211 self.attribute_mut(Mesh::ATTRIBUTE_NORMAL)
1212 {
1213 // Transform normals, taking into account non-uniform scaling
1214 normals.iter_mut().for_each(|normal| {
1215 *normal = scale_normal(Vec3::from_array(*normal), scale_recip).to_array();
1216 });
1217 }
1218
1219 if let Some(VertexAttributeValues::Float32x4(tangents)) =
1220 self.attribute_mut(Mesh::ATTRIBUTE_TANGENT)
1221 {
1222 // Transform tangents, taking into account non-uniform scaling
1223 tangents.iter_mut().for_each(|tangent| {
1224 let handedness = tangent[3];
1225 let scaled_tangent = Vec3::from_slice(tangent) * scale;
1226 *tangent = scaled_tangent
1227 .normalize_or_zero()
1228 .extend(handedness)
1229 .to_array();
1230 });
1231 }
1232 }
1233
1234 /// Whether this mesh has morph targets.
1235 pub fn has_morph_targets(&self) -> bool {
1236 self.morph_targets.is_some()
1237 }
1238
1239 /// Set [morph targets] image for this mesh. This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.
1240 ///
1241 /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
1242 pub fn set_morph_targets(&mut self, morph_targets: Handle<Image>) {
1243 self.morph_targets = Some(morph_targets);
1244 }
1245
1246 pub fn morph_targets(&self) -> Option<&Handle<Image>> {
1247 self.morph_targets.as_ref()
1248 }
1249
1250 /// Consumes the mesh and returns a mesh with the given [morph targets].
1251 ///
1252 /// This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.
1253 ///
1254 /// (Alternatively, you can use [`Mesh::set_morph_targets`] to mutate an existing mesh in-place)
1255 ///
1256 /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
1257 #[must_use]
1258 pub fn with_morph_targets(mut self, morph_targets: Handle<Image>) -> Self {
1259 self.set_morph_targets(morph_targets);
1260 self
1261 }
1262
1263 /// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`.
1264 pub fn set_morph_target_names(&mut self, names: Vec<String>) {
1265 self.morph_target_names = Some(names);
1266 }
1267
1268 /// Consumes the mesh and returns a mesh with morph target names.
1269 /// Names should correspond to the order of the morph targets in `set_morph_targets`.
1270 ///
1271 /// (Alternatively, you can use [`Mesh::set_morph_target_names`] to mutate an existing mesh in-place)
1272 #[must_use]
1273 pub fn with_morph_target_names(mut self, names: Vec<String>) -> Self {
1274 self.set_morph_target_names(names);
1275 self
1276 }
1277
1278 /// Gets a list of all morph target names, if they exist.
1279 pub fn morph_target_names(&self) -> Option<&[String]> {
1280 self.morph_target_names.as_deref()
1281 }
1282
1283 /// Normalize joint weights so they sum to 1.
1284 pub fn normalize_joint_weights(&mut self) {
1285 if let Some(joints) = self.attribute_mut(Self::ATTRIBUTE_JOINT_WEIGHT) {
1286 let VertexAttributeValues::Float32x4(joints) = joints else {
1287 panic!("unexpected joint weight format");
1288 };
1289
1290 for weights in joints.iter_mut() {
1291 // force negative weights to zero
1292 weights.iter_mut().for_each(|w| *w = w.max(0.0));
1293
1294 let sum: f32 = weights.iter().sum();
1295 if sum == 0.0 {
1296 // all-zero weights are invalid
1297 weights[0] = 1.0;
1298 } else {
1299 let recip = sum.recip();
1300 for weight in weights.iter_mut() {
1301 *weight *= recip;
1302 }
1303 }
1304 }
1305 }
1306 }
1307
1308 /// Get a list of this Mesh's [triangles] as an iterator if possible.
1309 ///
1310 /// Returns an error if any of the following conditions are met (see [`MeshTrianglesError`]):
1311 /// * The Mesh's [primitive topology] is not `TriangleList` or `TriangleStrip`.
1312 /// * The Mesh is missing position or index data.
1313 /// * The Mesh's position data has the wrong format (not `Float32x3`).
1314 ///
1315 /// [primitive topology]: PrimitiveTopology
1316 /// [triangles]: Triangle3d
1317 pub fn triangles(&self) -> Result<impl Iterator<Item = Triangle3d> + '_, MeshTrianglesError> {
1318 let Some(position_data) = self.attribute(Mesh::ATTRIBUTE_POSITION) else {
1319 return Err(MeshTrianglesError::MissingPositions);
1320 };
1321
1322 let Some(vertices) = position_data.as_float3() else {
1323 return Err(MeshTrianglesError::PositionsFormat);
1324 };
1325
1326 let Some(indices) = self.indices() else {
1327 return Err(MeshTrianglesError::MissingIndices);
1328 };
1329
1330 match self.primitive_topology {
1331 PrimitiveTopology::TriangleList => {
1332 // When indices reference out-of-bounds vertex data, the triangle is omitted.
1333 // This implicitly truncates the indices to a multiple of 3.
1334 let iterator = match indices {
1335 Indices::U16(vec) => FourIterators::First(
1336 vec.as_slice()
1337 .chunks_exact(3)
1338 .flat_map(move |indices| indices_to_triangle(vertices, indices)),
1339 ),
1340 Indices::U32(vec) => FourIterators::Second(
1341 vec.as_slice()
1342 .chunks_exact(3)
1343 .flat_map(move |indices| indices_to_triangle(vertices, indices)),
1344 ),
1345 };
1346
1347 return Ok(iterator);
1348 }
1349
1350 PrimitiveTopology::TriangleStrip => {
1351 // When indices reference out-of-bounds vertex data, the triangle is omitted.
1352 // If there aren't enough indices to make a triangle, then an empty vector will be
1353 // returned.
1354 let iterator = match indices {
1355 Indices::U16(vec) => {
1356 FourIterators::Third(vec.as_slice().windows(3).enumerate().flat_map(
1357 move |(i, indices)| {
1358 if i % 2 == 0 {
1359 indices_to_triangle(vertices, indices)
1360 } else {
1361 indices_to_triangle(
1362 vertices,
1363 &[indices[1], indices[0], indices[2]],
1364 )
1365 }
1366 },
1367 ))
1368 }
1369 Indices::U32(vec) => {
1370 FourIterators::Fourth(vec.as_slice().windows(3).enumerate().flat_map(
1371 move |(i, indices)| {
1372 if i % 2 == 0 {
1373 indices_to_triangle(vertices, indices)
1374 } else {
1375 indices_to_triangle(
1376 vertices,
1377 &[indices[1], indices[0], indices[2]],
1378 )
1379 }
1380 },
1381 ))
1382 }
1383 };
1384
1385 return Ok(iterator);
1386 }
1387
1388 _ => {
1389 return Err(MeshTrianglesError::WrongTopology);
1390 }
1391 };
1392
1393 fn indices_to_triangle<T: TryInto<usize> + Copy>(
1394 vertices: &[[f32; 3]],
1395 indices: &[T],
1396 ) -> Option<Triangle3d> {
1397 let vert0: Vec3 = Vec3::from(*vertices.get(indices[0].try_into().ok()?)?);
1398 let vert1: Vec3 = Vec3::from(*vertices.get(indices[1].try_into().ok()?)?);
1399 let vert2: Vec3 = Vec3::from(*vertices.get(indices[2].try_into().ok()?)?);
1400 Some(Triangle3d {
1401 vertices: [vert0, vert1, vert2],
1402 })
1403 }
1404 }
1405}
1406
1407impl core::ops::Mul<Mesh> for Transform {
1408 type Output = Mesh;
1409
1410 fn mul(self, rhs: Mesh) -> Self::Output {
1411 rhs.transformed_by(self)
1412 }
1413}
1414
1415/// A version of [`Mesh`] suitable for serializing for short-term transfer.
1416///
1417/// [`Mesh`] does not implement [`Serialize`] / [`Deserialize`] because it is made with the renderer in mind.
1418/// It is not a general-purpose mesh implementation, and its internals are subject to frequent change.
1419/// As such, storing a [`Mesh`] on disk is highly discouraged.
1420///
1421/// But there are still some valid use cases for serializing a [`Mesh`], namely transferring meshes between processes.
1422/// To support this, you can create a [`SerializedMesh`] from a [`Mesh`] with [`SerializedMesh::from_mesh`],
1423/// and then deserialize it with [`SerializedMesh::deserialize`]. The caveats are:
1424/// - The mesh representation is not valid across different versions of Bevy.
1425/// - This conversion is lossy. Only the following information is preserved:
1426/// - Primitive topology
1427/// - Vertex attributes
1428/// - Indices
1429/// - Custom attributes that were not specified with [`MeshDeserializer::add_custom_vertex_attribute`] will be ignored while deserializing.
1430#[cfg(feature = "serialize")]
1431#[derive(Debug, Clone, Serialize, Deserialize)]
1432pub struct SerializedMesh {
1433 primitive_topology: PrimitiveTopology,
1434 attributes: Vec<(MeshVertexAttributeId, SerializedMeshAttributeData)>,
1435 indices: Option<Indices>,
1436}
1437
1438#[cfg(feature = "serialize")]
1439impl SerializedMesh {
1440 /// Create a [`SerializedMesh`] from a [`Mesh`]. See the documentation for [`SerializedMesh`] for caveats.
1441 pub fn from_mesh(mesh: Mesh) -> Self {
1442 Self {
1443 primitive_topology: mesh.primitive_topology,
1444 attributes: mesh
1445 .attributes
1446 .into_iter()
1447 .map(|(id, data)| {
1448 (
1449 id,
1450 SerializedMeshAttributeData::from_mesh_attribute_data(data),
1451 )
1452 })
1453 .collect(),
1454 indices: mesh.indices,
1455 }
1456 }
1457
1458 /// Create a [`Mesh`] from a [`SerializedMesh`]. See the documentation for [`SerializedMesh`] for caveats.
1459 ///
1460 /// Use [`MeshDeserializer`] if you need to pass extra options to the deserialization process, such as specifying custom vertex attributes.
1461 pub fn into_mesh(self) -> Mesh {
1462 MeshDeserializer::default().deserialize(self)
1463 }
1464}
1465
1466/// Use to specify extra options when deserializing a [`SerializedMesh`] into a [`Mesh`].
1467#[cfg(feature = "serialize")]
1468pub struct MeshDeserializer {
1469 custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,
1470}
1471
1472#[cfg(feature = "serialize")]
1473impl Default for MeshDeserializer {
1474 fn default() -> Self {
1475 // Written like this so that the compiler can validate that we use all the built-in attributes.
1476 // If you just added a new attribute and got a compile error, please add it to this list :)
1477 const BUILTINS: [MeshVertexAttribute; Mesh::FIRST_AVAILABLE_CUSTOM_ATTRIBUTE as usize] = [
1478 Mesh::ATTRIBUTE_POSITION,
1479 Mesh::ATTRIBUTE_NORMAL,
1480 Mesh::ATTRIBUTE_UV_0,
1481 Mesh::ATTRIBUTE_UV_1,
1482 Mesh::ATTRIBUTE_TANGENT,
1483 Mesh::ATTRIBUTE_COLOR,
1484 Mesh::ATTRIBUTE_JOINT_WEIGHT,
1485 Mesh::ATTRIBUTE_JOINT_INDEX,
1486 ];
1487 Self {
1488 custom_vertex_attributes: BUILTINS
1489 .into_iter()
1490 .map(|attribute| (attribute.name.into(), attribute))
1491 .collect(),
1492 }
1493 }
1494}
1495
1496#[cfg(feature = "serialize")]
1497impl MeshDeserializer {
1498 /// Create a new [`MeshDeserializer`].
1499 pub fn new() -> Self {
1500 Self::default()
1501 }
1502
1503 /// Register a custom vertex attribute to the deserializer. Custom vertex attributes that were not added with this method will be ignored while deserializing.
1504 pub fn add_custom_vertex_attribute(
1505 &mut self,
1506 name: &str,
1507 attribute: MeshVertexAttribute,
1508 ) -> &mut Self {
1509 self.custom_vertex_attributes.insert(name.into(), attribute);
1510 self
1511 }
1512
1513 /// Deserialize a [`SerializedMesh`] into a [`Mesh`].
1514 ///
1515 /// See the documentation for [`SerializedMesh`] for caveats.
1516 pub fn deserialize(&self, serialized_mesh: SerializedMesh) -> Mesh {
1517 Mesh {
1518 attributes:
1519 serialized_mesh
1520 .attributes
1521 .into_iter()
1522 .filter_map(|(id, data)| {
1523 let attribute = data.attribute.clone();
1524 let Some(data) =
1525 data.try_into_mesh_attribute_data(&self.custom_vertex_attributes)
1526 else {
1527 warn!(
1528 "Deserialized mesh contains custom vertex attribute {attribute:?} that \
1529 was not specified with `MeshDeserializer::add_custom_vertex_attribute`. Ignoring."
1530 );
1531 return None;
1532 };
1533 Some((id, data))
1534 })
1535 .collect(),
1536 indices: serialized_mesh.indices,
1537 ..Mesh::new(serialized_mesh.primitive_topology, RenderAssetUsages::default())
1538 }
1539 }
1540}
1541
1542/// Error that can occur when calling [`Mesh::merge`].
1543#[derive(Error, Debug, Clone)]
1544pub enum MeshMergeError {
1545 #[error("Incompatible vertex attribute types: {} and {}", self_attribute.name, other_attribute.map(|a| a.name).unwrap_or("None"))]
1546 IncompatibleVertexAttributes {
1547 self_attribute: MeshVertexAttribute,
1548 other_attribute: Option<MeshVertexAttribute>,
1549 },
1550 #[error(
1551 "Incompatible primitive topologies: {:?} and {:?}",
1552 self_primitive_topology,
1553 other_primitive_topology
1554 )]
1555 IncompatiblePrimitiveTopology {
1556 self_primitive_topology: PrimitiveTopology,
1557 other_primitive_topology: PrimitiveTopology,
1558 },
1559}
1560
1561#[cfg(test)]
1562mod tests {
1563 use super::Mesh;
1564 #[cfg(feature = "serialize")]
1565 use super::SerializedMesh;
1566 use crate::mesh::{Indices, MeshWindingInvertError, VertexAttributeValues};
1567 use crate::PrimitiveTopology;
1568 use bevy_asset::RenderAssetUsages;
1569 use bevy_math::primitives::Triangle3d;
1570 use bevy_math::Vec3;
1571 use bevy_transform::components::Transform;
1572
1573 #[test]
1574 #[should_panic]
1575 fn panic_invalid_format() {
1576 let _mesh = Mesh::new(
1577 PrimitiveTopology::TriangleList,
1578 RenderAssetUsages::default(),
1579 )
1580 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0, 0.0]]);
1581 }
1582
1583 #[test]
1584 fn transform_mesh() {
1585 let mesh = Mesh::new(
1586 PrimitiveTopology::TriangleList,
1587 RenderAssetUsages::default(),
1588 )
1589 .with_inserted_attribute(
1590 Mesh::ATTRIBUTE_POSITION,
1591 vec![[-1., -1., 2.], [1., -1., 2.], [0., 1., 2.]],
1592 )
1593 .with_inserted_attribute(
1594 Mesh::ATTRIBUTE_NORMAL,
1595 vec![
1596 Vec3::new(-1., -1., 1.).normalize().to_array(),
1597 Vec3::new(1., -1., 1.).normalize().to_array(),
1598 [0., 0., 1.],
1599 ],
1600 )
1601 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0., 0.], [1., 0.], [0.5, 1.]]);
1602
1603 let mesh = mesh.transformed_by(
1604 Transform::from_translation(Vec3::splat(-2.)).with_scale(Vec3::new(2., 0., -1.)),
1605 );
1606
1607 if let Some(VertexAttributeValues::Float32x3(positions)) =
1608 mesh.attribute(Mesh::ATTRIBUTE_POSITION)
1609 {
1610 // All positions are first scaled resulting in `vec![[-2, 0., -2.], [2., 0., -2.], [0., 0., -2.]]`
1611 // and then shifted by `-2.` along each axis
1612 assert_eq!(
1613 positions,
1614 &vec![[-4.0, -2.0, -4.0], [0.0, -2.0, -4.0], [-2.0, -2.0, -4.0]]
1615 );
1616 } else {
1617 panic!("Mesh does not have a position attribute");
1618 }
1619
1620 if let Some(VertexAttributeValues::Float32x3(normals)) =
1621 mesh.attribute(Mesh::ATTRIBUTE_NORMAL)
1622 {
1623 assert_eq!(normals, &vec![[0., -1., 0.], [0., -1., 0.], [0., 0., -1.]]);
1624 } else {
1625 panic!("Mesh does not have a normal attribute");
1626 }
1627
1628 if let Some(VertexAttributeValues::Float32x2(uvs)) = mesh.attribute(Mesh::ATTRIBUTE_UV_0) {
1629 assert_eq!(uvs, &vec![[0., 0.], [1., 0.], [0.5, 1.]]);
1630 } else {
1631 panic!("Mesh does not have a uv attribute");
1632 }
1633 }
1634
1635 #[test]
1636 fn point_list_mesh_invert_winding() {
1637 let mesh = Mesh::new(PrimitiveTopology::PointList, RenderAssetUsages::default())
1638 .with_inserted_indices(Indices::U32(vec![]));
1639 assert!(matches!(
1640 mesh.with_inverted_winding(),
1641 Err(MeshWindingInvertError::WrongTopology)
1642 ));
1643 }
1644
1645 #[test]
1646 fn line_list_mesh_invert_winding() {
1647 let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
1648 .with_inserted_indices(Indices::U32(vec![0, 1, 1, 2, 2, 3]));
1649 let mesh = mesh.with_inverted_winding().unwrap();
1650 assert_eq!(
1651 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
1652 vec![3, 2, 2, 1, 1, 0]
1653 );
1654 }
1655
1656 #[test]
1657 fn line_list_mesh_invert_winding_fail() {
1658 let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
1659 .with_inserted_indices(Indices::U32(vec![0, 1, 1]));
1660 assert!(matches!(
1661 mesh.with_inverted_winding(),
1662 Err(MeshWindingInvertError::AbruptIndicesEnd)
1663 ));
1664 }
1665
1666 #[test]
1667 fn line_strip_mesh_invert_winding() {
1668 let mesh = Mesh::new(PrimitiveTopology::LineStrip, RenderAssetUsages::default())
1669 .with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));
1670 let mesh = mesh.with_inverted_winding().unwrap();
1671 assert_eq!(
1672 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
1673 vec![3, 2, 1, 0]
1674 );
1675 }
1676
1677 #[test]
1678 fn triangle_list_mesh_invert_winding() {
1679 let mesh = Mesh::new(
1680 PrimitiveTopology::TriangleList,
1681 RenderAssetUsages::default(),
1682 )
1683 .with_inserted_indices(Indices::U32(vec![
1684 0, 3, 1, // First triangle
1685 1, 3, 2, // Second triangle
1686 ]));
1687 let mesh = mesh.with_inverted_winding().unwrap();
1688 assert_eq!(
1689 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
1690 vec![
1691 0, 1, 3, // First triangle
1692 1, 2, 3, // Second triangle
1693 ]
1694 );
1695 }
1696
1697 #[test]
1698 fn triangle_list_mesh_invert_winding_fail() {
1699 let mesh = Mesh::new(
1700 PrimitiveTopology::TriangleList,
1701 RenderAssetUsages::default(),
1702 )
1703 .with_inserted_indices(Indices::U32(vec![0, 3, 1, 2]));
1704 assert!(matches!(
1705 mesh.with_inverted_winding(),
1706 Err(MeshWindingInvertError::AbruptIndicesEnd)
1707 ));
1708 }
1709
1710 #[test]
1711 fn triangle_strip_mesh_invert_winding() {
1712 let mesh = Mesh::new(
1713 PrimitiveTopology::TriangleStrip,
1714 RenderAssetUsages::default(),
1715 )
1716 .with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));
1717 let mesh = mesh.with_inverted_winding().unwrap();
1718 assert_eq!(
1719 mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
1720 vec![3, 2, 1, 0]
1721 );
1722 }
1723
1724 #[test]
1725 fn compute_area_weighted_normals() {
1726 let mut mesh = Mesh::new(
1727 PrimitiveTopology::TriangleList,
1728 RenderAssetUsages::default(),
1729 );
1730
1731 // z y
1732 // | /
1733 // 3---2
1734 // | / \
1735 // 0-----1--x
1736
1737 mesh.insert_attribute(
1738 Mesh::ATTRIBUTE_POSITION,
1739 vec![[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
1740 );
1741 mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));
1742 mesh.compute_area_weighted_normals();
1743 let normals = mesh
1744 .attribute(Mesh::ATTRIBUTE_NORMAL)
1745 .unwrap()
1746 .as_float3()
1747 .unwrap();
1748 assert_eq!(4, normals.len());
1749 // 0
1750 assert_eq!(Vec3::new(1., 0., 1.).normalize().to_array(), normals[0]);
1751 // 1
1752 assert_eq!([0., 0., 1.], normals[1]);
1753 // 2
1754 assert_eq!(Vec3::new(1., 0., 1.).normalize().to_array(), normals[2]);
1755 // 3
1756 assert_eq!([1., 0., 0.], normals[3]);
1757 }
1758
1759 #[test]
1760 fn compute_area_weighted_normals_proportionate() {
1761 let mut mesh = Mesh::new(
1762 PrimitiveTopology::TriangleList,
1763 RenderAssetUsages::default(),
1764 );
1765
1766 // z y
1767 // | /
1768 // 3---2..
1769 // | / \
1770 // 0-------1---x
1771
1772 mesh.insert_attribute(
1773 Mesh::ATTRIBUTE_POSITION,
1774 vec![[0., 0., 0.], [2., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
1775 );
1776 mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));
1777 mesh.compute_area_weighted_normals();
1778 let normals = mesh
1779 .attribute(Mesh::ATTRIBUTE_NORMAL)
1780 .unwrap()
1781 .as_float3()
1782 .unwrap();
1783 assert_eq!(4, normals.len());
1784 // 0
1785 assert_eq!(Vec3::new(1., 0., 2.).normalize().to_array(), normals[0]);
1786 // 1
1787 assert_eq!([0., 0., 1.], normals[1]);
1788 // 2
1789 assert_eq!(Vec3::new(1., 0., 2.).normalize().to_array(), normals[2]);
1790 // 3
1791 assert_eq!([1., 0., 0.], normals[3]);
1792 }
1793
1794 #[test]
1795 fn compute_angle_weighted_normals() {
1796 // CuboidMeshBuilder duplicates vertices (even though it is indexed)
1797
1798 // 5---------4
1799 // /| /|
1800 // 1-+-------0 |
1801 // | 6-------|-7
1802 // |/ |/
1803 // 2---------3
1804 let verts = vec![
1805 [1.0, 1.0, 1.0],
1806 [-1.0, 1.0, 1.0],
1807 [-1.0, -1.0, 1.0],
1808 [1.0, -1.0, 1.0],
1809 [1.0, 1.0, -1.0],
1810 [-1.0, 1.0, -1.0],
1811 [-1.0, -1.0, -1.0],
1812 [1.0, -1.0, -1.0],
1813 ];
1814
1815 let indices = Indices::U16(vec![
1816 0, 1, 2, 2, 3, 0, // front
1817 5, 4, 7, 7, 6, 5, // back
1818 1, 5, 6, 6, 2, 1, // left
1819 4, 0, 3, 3, 7, 4, // right
1820 4, 5, 1, 1, 0, 4, // top
1821 3, 2, 6, 6, 7, 3, // bottom
1822 ]);
1823 let mut mesh = Mesh::new(
1824 PrimitiveTopology::TriangleList,
1825 RenderAssetUsages::default(),
1826 );
1827 mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, verts);
1828 mesh.insert_indices(indices);
1829 mesh.compute_smooth_normals();
1830
1831 let normals = mesh
1832 .attribute(Mesh::ATTRIBUTE_NORMAL)
1833 .unwrap()
1834 .as_float3()
1835 .unwrap();
1836
1837 for new in normals.iter().copied().flatten() {
1838 // std impl is unstable
1839 const FRAC_1_SQRT_3: f32 = 0.57735026;
1840 const MIN: f32 = FRAC_1_SQRT_3 - f32::EPSILON;
1841 const MAX: f32 = FRAC_1_SQRT_3 + f32::EPSILON;
1842 assert!(new.abs() >= MIN, "{new} < {MIN}");
1843 assert!(new.abs() <= MAX, "{new} > {MAX}");
1844 }
1845 }
1846
1847 #[test]
1848 fn triangles_from_triangle_list() {
1849 let mut mesh = Mesh::new(
1850 PrimitiveTopology::TriangleList,
1851 RenderAssetUsages::default(),
1852 );
1853 mesh.insert_attribute(
1854 Mesh::ATTRIBUTE_POSITION,
1855 vec![[0., 0., 0.], [1., 0., 0.], [1., 1., 0.], [0., 1., 0.]],
1856 );
1857 mesh.insert_indices(Indices::U32(vec![0, 1, 2, 2, 3, 0]));
1858 assert_eq!(
1859 vec![
1860 Triangle3d {
1861 vertices: [
1862 Vec3::new(0., 0., 0.),
1863 Vec3::new(1., 0., 0.),
1864 Vec3::new(1., 1., 0.),
1865 ]
1866 },
1867 Triangle3d {
1868 vertices: [
1869 Vec3::new(1., 1., 0.),
1870 Vec3::new(0., 1., 0.),
1871 Vec3::new(0., 0., 0.),
1872 ]
1873 }
1874 ],
1875 mesh.triangles().unwrap().collect::<Vec<Triangle3d>>()
1876 );
1877 }
1878
1879 #[test]
1880 fn triangles_from_triangle_strip() {
1881 let mut mesh = Mesh::new(
1882 PrimitiveTopology::TriangleStrip,
1883 RenderAssetUsages::default(),
1884 );
1885 // Triangles: (0, 1, 2), (2, 1, 3), (2, 3, 4), (4, 3, 5)
1886 //
1887 // 4 - 5
1888 // | \ |
1889 // 2 - 3
1890 // | \ |
1891 // 0 - 1
1892 let positions: Vec<Vec3> = [
1893 [0., 0., 0.],
1894 [1., 0., 0.],
1895 [0., 1., 0.],
1896 [1., 1., 0.],
1897 [0., 2., 0.],
1898 [1., 2., 0.],
1899 ]
1900 .into_iter()
1901 .map(Vec3::from_array)
1902 .collect();
1903 mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions.clone());
1904 mesh.insert_indices(Indices::U32(vec![0, 1, 2, 3, 4, 5]));
1905 assert_eq!(
1906 vec![
1907 Triangle3d {
1908 vertices: [positions[0], positions[1], positions[2]]
1909 },
1910 Triangle3d {
1911 vertices: [positions[2], positions[1], positions[3]]
1912 },
1913 Triangle3d {
1914 vertices: [positions[2], positions[3], positions[4]]
1915 },
1916 Triangle3d {
1917 vertices: [positions[4], positions[3], positions[5]]
1918 },
1919 ],
1920 mesh.triangles().unwrap().collect::<Vec<Triangle3d>>()
1921 );
1922 }
1923
1924 #[cfg(feature = "serialize")]
1925 #[test]
1926 fn serialize_deserialize_mesh() {
1927 let mut mesh = Mesh::new(
1928 PrimitiveTopology::TriangleList,
1929 RenderAssetUsages::default(),
1930 );
1931
1932 mesh.insert_attribute(
1933 Mesh::ATTRIBUTE_POSITION,
1934 vec![[0., 0., 0.], [2., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
1935 );
1936 mesh.insert_indices(Indices::U16(vec![0, 1, 2, 0, 2, 3]));
1937
1938 let serialized_mesh = SerializedMesh::from_mesh(mesh.clone());
1939 let serialized_string = serde_json::to_string(&serialized_mesh).unwrap();
1940 let serialized_mesh_from_string: SerializedMesh =
1941 serde_json::from_str(&serialized_string).unwrap();
1942 let deserialized_mesh = serialized_mesh_from_string.into_mesh();
1943 assert_eq!(mesh, deserialized_mesh);
1944 }
1945}