Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_mesh/src/mesh.rs
6595 views
1
use bevy_transform::components::Transform;
2
pub use wgpu_types::PrimitiveTopology;
3
4
use 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")]
11
use crate::SerializedMeshAttributeData;
12
use alloc::collections::BTreeMap;
13
use bevy_asset::{Asset, Handle, RenderAssetUsages};
14
use bevy_image::Image;
15
use bevy_math::{primitives::Triangle3d, *};
16
#[cfg(feature = "serialize")]
17
use bevy_platform::collections::HashMap;
18
use bevy_reflect::Reflect;
19
use bytemuck::cast_slice;
20
#[cfg(feature = "serialize")]
21
use serde::{Deserialize, Serialize};
22
use thiserror::Error;
23
use tracing::warn;
24
use wgpu_types::{VertexAttribute, VertexFormat, VertexStepMode};
25
26
pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
27
pub 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)]
120
pub 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
150
impl 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
1407
impl 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)]
1432
pub struct SerializedMesh {
1433
primitive_topology: PrimitiveTopology,
1434
attributes: Vec<(MeshVertexAttributeId, SerializedMeshAttributeData)>,
1435
indices: Option<Indices>,
1436
}
1437
1438
#[cfg(feature = "serialize")]
1439
impl 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")]
1468
pub struct MeshDeserializer {
1469
custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,
1470
}
1471
1472
#[cfg(feature = "serialize")]
1473
impl 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")]
1497
impl 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)]
1544
pub 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)]
1562
mod 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
}
1946
1947