pub struct Mesh {
pub asset_usage: RenderAssetUsages,
pub enable_raytracing: bool,
pub final_aabb: Option<Aabb3d>,
/* private fields */
}Expand description
A 3D object made out of vertices representing triangles, lines, or points, with “attribute” values for each vertex.
Meshes can be automatically generated by a bevy AssetLoader (generally by loading a Gltf file),
or by converting a primitive using into.
It is also possible to create one manually. They can be edited after creation.
Meshes can be rendered with a Mesh2d and MeshMaterial2d
or Mesh3d and MeshMaterial3d for 2D and 3D respectively.
A Mesh in Bevy is equivalent to a “primitive” in the glTF format, for a
glTF Mesh representation, see GltfMesh.
§Manual creation
The following function will construct a flat mesh, to be rendered with a
StandardMaterial or ColorMaterial:
fn create_simple_parallelogram() -> Mesh {
// Create a new mesh using a triangle list topology, where each set of 3 vertices composes a triangle.
Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default())
// Add 4 vertices, each with its own position attribute (coordinate in
// 3D space), for each of the corners of the parallelogram.
.with_inserted_attribute(
Mesh::ATTRIBUTE_POSITION,
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]]
)
// Assign a UV coordinate to each vertex.
.with_inserted_attribute(
Mesh::ATTRIBUTE_UV_0,
vec![[0.0, 1.0], [0.5, 0.0], [1.0, 0.0], [0.5, 1.0]]
)
// Assign normals (everything points outwards)
.with_inserted_attribute(
Mesh::ATTRIBUTE_NORMAL,
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]]
)
// After defining all the vertices and their attributes, build each triangle using the
// indices of the vertices that make it up in a counter-clockwise order.
.with_inserted_indices(Indices::U32(vec![
// First triangle
0, 3, 1,
// Second triangle
1, 3, 2
]))
}You can see how it looks like here,
used in a Mesh3d with a square bevy logo texture, with added axis, points,
lines and text for clarity.
§Other examples
For further visualization, explanation, and examples, see the built-in Bevy examples,
and the implementation of the built-in shapes.
In particular, generate_custom_mesh
teaches you to access and modify the attributes of a Mesh after creating it.
§Common points of confusion
- UV maps in Bevy start at the top-left, see
ATTRIBUTE_UV_0, other APIs can have other conventions,OpenGLstarts at bottom-left. - It is possible and sometimes useful for multiple vertices to have the same position attribute value, it’s a common technique in 3D modeling for complex UV mapping or other calculations.
- Bevy performs frustum culling based on the
Aabbof meshes, which is calculated and added automatically for new meshes only. If a mesh is modified, the entity’sAabbneeds to be updated manually or deleted so that it is re-calculated.
§Use with StandardMaterial
To render correctly with StandardMaterial, a mesh needs to have properly defined:
UVs: Bevy needs to know how to map a texture onto the mesh (also true forColorMaterial).Normals: Bevy needs to know how light interacts with your mesh. [0.0, 0.0, 1.0] is very common for simple flat meshes on the XY plane, because simple meshes are smooth and they don’t require complex light calculations.- Vertex winding order: by default,
StandardMaterial.cull_modeisSome(Face::Back), which means that Bevy would only render the “front” of each triangle, which is the side of the triangle from where the vertices appear in a counter-clockwise order.
§Remote Inspection
To transmit a Mesh between two running Bevy apps, e.g. through BRP, use SerializedMesh.
This type is only meant for short-term transmission between same versions and should not be stored anywhere.
Fields§
§asset_usage: RenderAssetUsages§enable_raytracing: boolWhether or not to build a BLAS for use with bevy_solari raytracing.
Note that this is not whether the mesh is compatible with bevy_solari raytracing.
This field just controls whether or not a BLAS gets built for this mesh, assuming that
the mesh is compatible.
The use case for this field is using lower-resolution proxy meshes for raytracing (to save on BLAS memory usage), while using higher-resolution meshes for raster. You can set this field to true for the lower-resolution proxy mesh, and to false for the high-resolution raster mesh.
Alternatively, you can use the same mesh for both raster and raytracing, with this field set to true.
Does nothing if not used with bevy_solari, or if the mesh is not compatible
with bevy_solari (see bevy_solari’s docs).
final_aabb: Option<Aabb3d>Precomputed min and max extents of the mesh position data. Used mainly for constructing Aabbs for frustum culling.
This data will be set if/when a mesh is extracted to the GPU
Implementations§
Source§impl Mesh
impl Mesh
Sourcepub const ATTRIBUTE_POSITION: MeshVertexAttribute
pub const ATTRIBUTE_POSITION: MeshVertexAttribute
Where the vertex is located in space. Use in conjunction with Mesh::insert_attribute
or Mesh::with_inserted_attribute.
The format of this attribute is VertexFormat::Float32x3.
Sourcepub const ATTRIBUTE_NORMAL: MeshVertexAttribute
pub const ATTRIBUTE_NORMAL: MeshVertexAttribute
The direction the vertex normal is facing in.
Use in conjunction with Mesh::insert_attribute or Mesh::with_inserted_attribute.
The format of this attribute is VertexFormat::Float32x3.
Sourcepub const ATTRIBUTE_UV_0: MeshVertexAttribute
pub const ATTRIBUTE_UV_0: MeshVertexAttribute
Texture coordinates for the vertex. Use in conjunction with Mesh::insert_attribute
or Mesh::with_inserted_attribute.
Generally [0.,0.] is mapped to the top left of the texture, and [1.,1.] to the bottom-right.
By default values outside will be clamped per pixel not for the vertex, “stretching” the borders of the texture. This behavior can be useful in some cases, usually when the borders have only one color, for example a logo, and you want to “extend” those borders.
For different mapping outside of 0..=1 range,
see ImageAddressMode.
The format of this attribute is VertexFormat::Float32x2.
Sourcepub const ATTRIBUTE_UV_1: MeshVertexAttribute
pub const ATTRIBUTE_UV_1: MeshVertexAttribute
Alternate texture coordinates for the vertex. Use in conjunction with
Mesh::insert_attribute or Mesh::with_inserted_attribute.
Typically, these are used for lightmaps, textures that provide precomputed illumination.
The format of this attribute is VertexFormat::Float32x2.
Sourcepub const ATTRIBUTE_TANGENT: MeshVertexAttribute
pub const ATTRIBUTE_TANGENT: MeshVertexAttribute
The direction of the vertex tangent. Used for normal mapping.
Usually generated with generate_tangents or
with_generated_tangents.
The format of this attribute is VertexFormat::Float32x4.
Sourcepub const ATTRIBUTE_COLOR: MeshVertexAttribute
pub const ATTRIBUTE_COLOR: MeshVertexAttribute
Per vertex coloring. Use in conjunction with Mesh::insert_attribute
or Mesh::with_inserted_attribute.
The format of this attribute is VertexFormat::Float32x4.
Sourcepub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute
pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute
Per vertex joint transform matrix weight. Use in conjunction with Mesh::insert_attribute
or Mesh::with_inserted_attribute.
The format of this attribute is VertexFormat::Float32x4.
Sourcepub const ATTRIBUTE_JOINT_INDEX: MeshVertexAttribute
pub const ATTRIBUTE_JOINT_INDEX: MeshVertexAttribute
Per vertex joint transform matrix index. Use in conjunction with Mesh::insert_attribute
or Mesh::with_inserted_attribute.
The format of this attribute is VertexFormat::Uint16x4.
Sourcepub const FIRST_AVAILABLE_CUSTOM_ATTRIBUTE: u64 = 8
pub const FIRST_AVAILABLE_CUSTOM_ATTRIBUTE: u64 = 8
The first index that can be used for custom vertex attributes. Only the attributes with an index below this are used by Bevy.
Sourcepub fn new(
primitive_topology: PrimitiveTopology,
asset_usage: RenderAssetUsages,
) -> Mesh
pub fn new( primitive_topology: PrimitiveTopology, asset_usage: RenderAssetUsages, ) -> Mesh
Construct a new mesh. You need to provide a PrimitiveTopology so that the
renderer knows how to treat the vertex data. Most of the time this will be
PrimitiveTopology::TriangleList.
Examples found in repository?
85 fn from(line: LineList) -> Self {
86 let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect();
87
88 Mesh::new(
89 // This tells wgpu that the positions are list of lines
90 // where every pair is a start and end point
91 PrimitiveTopology::LineList,
92 RenderAssetUsages::RENDER_WORLD,
93 )
94 // Add the vertices positions as an attribute
95 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
96 }
97}
98
99/// A list of points that will have a line drawn between each consecutive points
100#[derive(Debug, Clone)]
101struct LineStrip {
102 points: Vec<Vec3>,
103 indices: Indices,
104}
105
106impl From<LineStrip> for Mesh {
107 fn from(line: LineStrip) -> Self {
108 Mesh::new(
109 // This tells wgpu that the positions are a list of points
110 // where a line will be drawn between each consecutive point
111 PrimitiveTopology::LineStrip,
112 RenderAssetUsages::RENDER_WORLD,
113 )
114 // Add the point positions as an attribute
115 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, line.points)
116 .with_inserted_indices(line.indices)
117 }More examples
54fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
55 // Build a custom triangle mesh with colors
56 // We define a custom mesh because the examples only uses a limited
57 // set of vertex attributes for simplicity
58 let mesh = Mesh::new(
59 PrimitiveTopology::TriangleList,
60 RenderAssetUsages::default(),
61 )
62 .with_inserted_indices(Indices::U32(vec![0, 1, 2]))
63 .with_inserted_attribute(
64 Mesh::ATTRIBUTE_POSITION,
65 vec![
66 vec3(-0.5, -0.5, 0.0),
67 vec3(0.5, -0.5, 0.0),
68 vec3(0.0, 0.25, 0.0),
69 ],
70 )
71 .with_inserted_attribute(
72 Mesh::ATTRIBUTE_COLOR,
73 vec![
74 vec4(1.0, 0.0, 0.0, 1.0),
75 vec4(0.0, 1.0, 0.0, 1.0),
76 vec4(0.0, 0.0, 1.0, 1.0),
77 ],
78 );
79
80 // spawn 3 triangles to show that batching works
81 for (x, y) in [-0.5, 0.0, 0.5].into_iter().zip([-0.25, 0.5, -0.25]) {
82 // Spawn an entity with all the required components for it to be rendered with our custom pipeline
83 commands.spawn((
84 // We use a marker component to identify the mesh that will be rendered
85 // with our specialized pipeline
86 CustomRenderedEntity,
87 // We need to add the mesh handle to the entity
88 Mesh3d(meshes.add(mesh.clone())),
89 Transform::from_xyz(x, y, 0.0),
90 ));
91 }
92
93 // Spawn the camera.
94 commands.spawn((
95 Camera3d::default(),
96 // Move the camera back a bit to see all the triangles
97 Transform::from_xyz(0.0, 0.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
98 ));
99}514 fn build(&self) -> Mesh {
515 let radius = self.heart.radius;
516 // The curved parts of each wing (half) of the heart have an angle of `PI * 1.25` or 225°
517 let wing_angle = PI * 1.25;
518
519 // We create buffers for the vertices, their normals and UVs, as well as the indices used to connect the vertices.
520 let mut vertices = Vec::with_capacity(2 * self.resolution);
521 let mut uvs = Vec::with_capacity(2 * self.resolution);
522 let mut indices = Vec::with_capacity(6 * self.resolution - 9);
523 // Since the heart is flat, we know all the normals are identical already.
524 let normals = vec![[0f32, 0f32, 1f32]; 2 * self.resolution];
525
526 // The point in the middle of the two curved parts of the heart
527 vertices.push([0.0; 3]);
528 uvs.push([0.5, 0.5]);
529
530 // The left wing of the heart, starting from the point in the middle.
531 for i in 1..self.resolution {
532 let angle = (i as f32 / self.resolution as f32) * wing_angle;
533 let (sin, cos) = ops::sin_cos(angle);
534 vertices.push([radius * (cos - 1.0), radius * sin, 0.0]);
535 uvs.push([0.5 - (cos - 1.0) / 4., 0.5 - sin / 2.]);
536 }
537
538 // The bottom tip of the heart
539 vertices.push([0.0, radius * (-1. - SQRT_2), 0.0]);
540 uvs.push([0.5, 1.]);
541
542 // The right wing of the heart, starting from the bottom most point and going towards the middle point.
543 for i in 0..self.resolution - 1 {
544 let angle = (i as f32 / self.resolution as f32) * wing_angle - PI / 4.;
545 let (sin, cos) = ops::sin_cos(angle);
546 vertices.push([radius * (cos + 1.0), radius * sin, 0.0]);
547 uvs.push([0.5 - (cos + 1.0) / 4., 0.5 - sin / 2.]);
548 }
549
550 // This is where we build all the triangles from the points created above.
551 // Each triangle has one corner on the middle point with the other two being adjacent points on the perimeter of the heart.
552 for i in 2..2 * self.resolution as u32 {
553 indices.extend_from_slice(&[i - 1, i, 0]);
554 }
555
556 // Here, the actual `Mesh` is created. We set the indices, vertices, normals and UVs created above and specify the topology of the mesh.
557 Mesh::new(
558 bevy::mesh::PrimitiveTopology::TriangleList,
559 RenderAssetUsages::default(),
560 )
561 .with_inserted_indices(bevy::mesh::Indices::U32(indices))
562 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
563 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
564 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
565 }82fn setup(
83 mut commands: Commands,
84 mut meshes: ResMut<Assets<Mesh>>,
85 mut materials: ResMut<Assets<StandardMaterial>>,
86) {
87 // a truly empty mesh will error if used in Mesh3d
88 // so we set up the data to be what we want the compute shader to output
89 // We're using 36 indices and 24 vertices which is directly taken from
90 // the Bevy Cuboid mesh implementation.
91 //
92 // We allocate 50 spots for each attribute here because
93 // it is *very important* that the amount of data allocated here is
94 // *bigger* than (or exactly equal to) the amount of data we intend to
95 // write from the compute shader. This amount of data defines how big
96 // the buffer we get from the mesh_allocator will be, which in turn
97 // defines how big the buffer is when we're in the compute shader.
98 //
99 // If it turns out you don't need all of the space when the compute shader
100 // is writing data, you can write NaN to the rest of the data.
101 let empty_mesh = {
102 let mut mesh = Mesh::new(
103 PrimitiveTopology::TriangleList,
104 RenderAssetUsages::RENDER_WORLD,
105 )
106 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vec![[0.; 3]; 50])
107 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.; 3]; 50])
108 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.; 2]; 50])
109 .with_inserted_indices(Indices::U32(vec![0; 50]));
110
111 mesh.asset_usage = RenderAssetUsages::RENDER_WORLD;
112 mesh
113 };
114
115 let handle = meshes.add(empty_mesh);
116
117 // we spawn two "users" of the mesh handle,
118 // but only insert `GenerateMesh` on one of them
119 // to show that the mesh handle works as usual
120 commands.spawn((
121 GenerateMesh(handle.clone()),
122 Mesh3d(handle.clone()),
123 MeshMaterial3d(materials.add(StandardMaterial {
124 base_color: RED_400.into(),
125 ..default()
126 })),
127 Transform::from_xyz(-2.5, 1.5, 0.),
128 ));
129
130 commands.spawn((
131 Mesh3d(handle),
132 MeshMaterial3d(materials.add(StandardMaterial {
133 base_color: SKY_400.into(),
134 ..default()
135 })),
136 Transform::from_xyz(2.5, 1.5, 0.),
137 ));
138
139 // some additional scene elements.
140 // This mesh specifically is here so that we don't assume
141 // mesh_allocator offsets that would only work if we had
142 // one mesh in the scene.
143 commands.spawn((
144 Mesh3d(meshes.add(Circle::new(4.0))),
145 MeshMaterial3d(materials.add(Color::WHITE)),
146 Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
147 ));
148 commands.spawn((
149 PointLight {
150 shadow_maps_enabled: true,
151 ..default()
152 },
153 Transform::from_xyz(4.0, 8.0, 4.0),
154 ));
155 // camera
156 commands.spawn((
157 Camera3d::default(),
158 Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
159 ));
160}49fn star(
50 mut commands: Commands,
51 // We will add a new Mesh for the star being created
52 mut meshes: ResMut<Assets<Mesh>>,
53) {
54 // Let's define the mesh for the object we want to draw: a nice star.
55 // We will specify here what kind of topology is used to define the mesh,
56 // that is, how triangles are built from the vertices. We will use a
57 // triangle list, meaning that each vertex of the triangle has to be
58 // specified. We set `RenderAssetUsages::RENDER_WORLD`, meaning this mesh
59 // will not be accessible in future frames from the `meshes` resource, in
60 // order to save on memory once it has been uploaded to the GPU.
61 let mut star = Mesh::new(
62 PrimitiveTopology::TriangleList,
63 RenderAssetUsages::RENDER_WORLD,
64 );
65
66 // Vertices need to have a position attribute. We will use the following
67 // vertices (I hope you can spot the star in the schema).
68 //
69 // 1
70 //
71 // 10 2
72 // 9 0 3
73 // 8 4
74 // 6
75 // 7 5
76 //
77 // These vertices are specified in 3D space.
78 let mut v_pos = vec![[0.0, 0.0, 0.0]];
79 for i in 0..10 {
80 // The angle between each vertex is 1/10 of a full rotation.
81 let a = i as f32 * PI / 5.0;
82 // The radius of inner vertices (even indices) is 100. For outer vertices (odd indices) it's 200.
83 let r = (1 - i % 2) as f32 * 100.0 + 100.0;
84 // Add the vertex position.
85 v_pos.push([r * ops::sin(a), r * ops::cos(a), 0.0]);
86 }
87 // Set the position attribute
88 star.insert_attribute(Mesh::ATTRIBUTE_POSITION, v_pos);
89 // And a RGB color attribute as well. A built-in `Mesh::ATTRIBUTE_COLOR` exists, but we
90 // use a custom vertex attribute here for demonstration purposes.
91 let mut v_color: Vec<u32> = vec![LinearRgba::BLACK.as_u32()];
92 v_color.extend_from_slice(&[LinearRgba::from(YELLOW).as_u32(); 10]);
93 star.insert_attribute(
94 MeshVertexAttribute::new("Vertex_Color", 1, VertexFormat::Uint32),
95 v_color,
96 );
97
98 // Now, we specify the indices of the vertex that are going to compose the
99 // triangles in our star. Vertices in triangles have to be specified in CCW
100 // winding (that will be the front face, colored). Since we are using
101 // triangle list, we will specify each triangle as 3 vertices
102 // First triangle: 0, 2, 1
103 // Second triangle: 0, 3, 2
104 // Third triangle: 0, 4, 3
105 // etc
106 // Last triangle: 0, 1, 10
107 let mut indices = vec![0, 1, 10];
108 for i in 2..=10 {
109 indices.extend_from_slice(&[0, i, i - 1]);
110 }
111 star.insert_indices(Indices::U32(indices));
112
113 // We can now spawn the entities for the star and the camera
114 commands.spawn((
115 // We use a marker component to identify the custom colored meshes
116 ColoredMesh2d,
117 // The `Handle<Mesh>` needs to be wrapped in a `Mesh2d` for 2D rendering
118 Mesh2d(meshes.add(star)),
119 ));
120
121 commands.spawn(Camera2d);
122}92fn setup_meshes(
93 mut commands: Commands,
94 mut mesh_assets: ResMut<Assets<Mesh>>,
95 mut material_assets: ResMut<Assets<StandardMaterial>>,
96 mut inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
97) {
98 // Create a mesh with two rectangles.
99 let unskinned_mesh = Mesh::new(
100 PrimitiveTopology::TriangleList,
101 RenderAssetUsages::default(),
102 )
103 .with_inserted_attribute(
104 Mesh::ATTRIBUTE_POSITION,
105 vec![
106 [-0.3, -0.3, 0.0],
107 [0.3, -0.3, 0.0],
108 [-0.3, 0.3, 0.0],
109 [0.3, 0.3, 0.0],
110 [-0.4, 0.8, 0.0],
111 [0.4, 0.8, 0.0],
112 [-0.4, 1.8, 0.0],
113 [0.4, 1.8, 0.0],
114 ],
115 )
116 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 8])
117 .with_inserted_indices(Indices::U16(vec![0, 1, 3, 0, 3, 2, 4, 5, 7, 4, 7, 6]));
118
119 // Copy the mesh and add skinning attributes that bind each rectangle to a joint.
120 let skinned_mesh = unskinned_mesh
121 .clone()
122 .with_inserted_attribute(
123 Mesh::ATTRIBUTE_JOINT_INDEX,
124 VertexAttributeValues::Uint16x4(vec![
125 [0, 0, 0, 0],
126 [0, 0, 0, 0],
127 [0, 0, 0, 0],
128 [0, 0, 0, 0],
129 [1, 0, 0, 0],
130 [1, 0, 0, 0],
131 [1, 0, 0, 0],
132 [1, 0, 0, 0],
133 ]),
134 )
135 .with_inserted_attribute(
136 Mesh::ATTRIBUTE_JOINT_WEIGHT,
137 vec![[1.00, 0.00, 0.0, 0.0]; 8],
138 );
139
140 let unskinned_mesh_handle = mesh_assets.add(unskinned_mesh);
141 let skinned_mesh_handle = mesh_assets.add(skinned_mesh);
142
143 let inverse_bindposes_handle = inverse_bindposes_assets.add(vec![
144 Mat4::IDENTITY,
145 Mat4::from_translation(Vec3::new(0.0, -1.3, 0.0)),
146 ]);
147
148 let mesh_material_handle = material_assets.add(StandardMaterial::default());
149
150 let background_material_handle = material_assets.add(StandardMaterial {
151 base_color: Color::srgb(0.05, 0.15, 0.05),
152 reflectance: 0.2,
153 ..default()
154 });
155
156 #[derive(PartialEq)]
157 enum Variation {
158 Normal,
159 MissingMeshAttributes,
160 MissingJointEntity,
161 MissingSkinnedMeshComponent,
162 }
163
164 for (index, variation) in [
165 Variation::Normal,
166 Variation::MissingMeshAttributes,
167 Variation::MissingJointEntity,
168 Variation::MissingSkinnedMeshComponent,
169 ]
170 .into_iter()
171 .enumerate()
172 {
173 // Skip variations that are currently broken. See https://github.com/bevyengine/bevy/issues/16929,
174 // https://github.com/bevyengine/bevy/pull/18074.
175 if (variation == Variation::MissingSkinnedMeshComponent)
176 || (variation == Variation::MissingMeshAttributes)
177 {
178 continue;
179 }
180
181 let transform = Transform::from_xyz(((index as f32) - 1.5) * 4.5, 0.0, 0.0);
182
183 let joint_0 = commands.spawn(transform).id();
184
185 let joint_1 = commands
186 .spawn((ChildOf(joint_0), AnimatedJoint, Transform::IDENTITY))
187 .id();
188
189 if variation == Variation::MissingJointEntity {
190 commands.entity(joint_1).despawn();
191 }
192
193 let mesh_handle = match variation {
194 Variation::MissingMeshAttributes => &unskinned_mesh_handle,
195 _ => &skinned_mesh_handle,
196 };
197
198 let mut entity_commands = commands.spawn((
199 Mesh3d(mesh_handle.clone()),
200 MeshMaterial3d(mesh_material_handle.clone()),
201 transform,
202 ));
203
204 if variation != Variation::MissingSkinnedMeshComponent {
205 entity_commands.insert(SkinnedMesh {
206 inverse_bindposes: inverse_bindposes_handle.clone(),
207 joints: vec![joint_0, joint_1],
208 });
209 }
210
211 // Add a square behind the mesh to distinguish it from the other meshes.
212 commands.spawn((
213 Transform::from_xyz(transform.translation.x, transform.translation.y, -0.8),
214 Mesh3d(mesh_assets.add(Plane3d::default().mesh().size(4.3, 4.3).normal(Dir3::Z))),
215 MeshMaterial3d(background_material_handle.clone()),
216 ));
217 }
218}Sourcepub fn primitive_topology(&self) -> PrimitiveTopology
pub fn primitive_topology(&self) -> PrimitiveTopology
Returns the topology of the mesh.
Examples found in repository?
12fn setup(
13 mut commands: Commands,
14 asset_server: Res<AssetServer>,
15 meshes: Res<Assets<Mesh>>,
16 mut materials: ResMut<Assets<StandardMaterial>>,
17) {
18 // By default AssetServer will load assets from inside the "assets" folder.
19 // For example, the next line will load GltfAssetLabel::Primitive{mesh:0,primitive:0}.from_asset("ROOT/assets/models/cube/cube.gltf"),
20 // where "ROOT" is the directory of the Application.
21 //
22 // This can be overridden by setting [`AssetPlugin.file_path`].
23 let cube_handle = asset_server.load(
24 GltfAssetLabel::Primitive {
25 mesh: 0,
26 primitive: 0,
27 }
28 .from_asset("models/cube/cube.gltf"),
29 );
30 let sphere_handle = asset_server.load(
31 GltfAssetLabel::Primitive {
32 mesh: 0,
33 primitive: 0,
34 }
35 .from_asset("models/sphere/sphere.gltf"),
36 );
37
38 // All assets end up in their Assets<T> collection once they are done loading:
39 if let Some(sphere) = meshes.get(&sphere_handle) {
40 // You might notice that this doesn't run! This is because assets load in parallel without
41 // blocking. When an asset has loaded, it will appear in relevant Assets<T>
42 // collection.
43 info!("{:?}", sphere.primitive_topology());
44 } else {
45 info!("sphere hasn't loaded yet");
46 }
47
48 // You can load all assets in a folder like this. They will be loaded in parallel without
49 // blocking. The LoadedFolder asset holds handles to each asset in the folder. These are all
50 // dependencies of the LoadedFolder asset, meaning you can wait for the LoadedFolder asset to
51 // fire AssetEvent::LoadedWithDependencies if you want to wait for all assets in the folder
52 // to load.
53 // If you want to keep the assets in the folder alive, make sure you store the returned handle
54 // somewhere.
55 let _loaded_folder: Handle<LoadedFolder> = asset_server.load_folder("models/torus");
56
57 // If you want a handle to a specific asset in a loaded folder, the easiest way to get one is to call load.
58 // It will _not_ be loaded a second time.
59 // The LoadedFolder asset will ultimately also hold handles to the assets, but waiting for it to load
60 // and finding the right handle is more work!
61 let torus_handle = asset_server.load(
62 GltfAssetLabel::Primitive {
63 mesh: 0,
64 primitive: 0,
65 }
66 .from_asset("models/torus/torus.gltf"),
67 );
68
69 // You can also add assets directly to their Assets<T> storage:
70 let material_handle = materials.add(StandardMaterial {
71 base_color: Color::srgb(0.8, 0.7, 0.6),
72 ..default()
73 });
74
75 // torus
76 commands.spawn((
77 Mesh3d(torus_handle),
78 MeshMaterial3d(material_handle.clone()),
79 Transform::from_xyz(-3.0, 0.0, 0.0),
80 ));
81 // cube
82 commands.spawn((
83 Mesh3d(cube_handle),
84 MeshMaterial3d(material_handle.clone()),
85 Transform::from_xyz(0.0, 0.0, 0.0),
86 ));
87 // sphere
88 commands.spawn((
89 Mesh3d(sphere_handle),
90 MeshMaterial3d(material_handle),
91 Transform::from_xyz(3.0, 0.0, 0.0),
92 ));
93 // light
94 commands.spawn((PointLight::default(), Transform::from_xyz(4.0, 5.0, 4.0)));
95 // camera
96 commands.spawn((
97 Camera3d::default(),
98 Transform::from_xyz(0.0, 3.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
99 ));
100}Sourcepub fn insert_attribute(
&mut self,
attribute: MeshVertexAttribute,
values: impl Into<VertexAttributeValues>,
)
pub fn insert_attribute( &mut self, attribute: MeshVertexAttribute, values: impl Into<VertexAttributeValues>, )
Sets the data for a vertex attribute (position, normal, etc.). The name will
often be one of the associated constants such as Mesh::ATTRIBUTE_POSITION.
Aabb of entities with modified mesh are not updated automatically.
§Panics
Panics when the format of the values does not match the attribute’s format.
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_insert_attribute
Examples found in repository?
13fn setup(
14 mut commands: Commands,
15 mut meshes: ResMut<Assets<Mesh>>,
16 mut materials: ResMut<Assets<ColorMaterial>>,
17 asset_server: Res<AssetServer>,
18) {
19 // Load the Bevy logo as a texture
20 let texture_handle = asset_server.load("branding/banner.png");
21 // Build a default quad mesh
22 let mut mesh = Mesh::from(Rectangle::default());
23 // Build vertex colors for the quad. One entry per vertex (the corners of the quad)
24 let vertex_colors: Vec<[f32; 4]> = vec![
25 LinearRgba::RED.to_f32_array(),
26 LinearRgba::GREEN.to_f32_array(),
27 LinearRgba::BLUE.to_f32_array(),
28 LinearRgba::WHITE.to_f32_array(),
29 ];
30 // Insert the vertex colors as an attribute
31 mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vertex_colors);
32
33 let mesh_handle = meshes.add(mesh);
34
35 commands.spawn(Camera2d);
36
37 // Spawn the quad with vertex colors
38 commands.spawn((
39 Mesh2d(mesh_handle.clone()),
40 MeshMaterial2d(materials.add(ColorMaterial::default())),
41 Transform::from_translation(Vec3::new(-96., 0., 0.)).with_scale(Vec3::splat(128.)),
42 ));
43
44 // Spawning the quad with vertex colors and a texture results in tinting
45 commands.spawn((
46 Mesh2d(mesh_handle),
47 MeshMaterial2d(materials.add(texture_handle)),
48 Transform::from_translation(Vec3::new(96., 0., 0.)).with_scale(Vec3::splat(128.)),
49 ));
50}More examples
13fn setup(
14 mut commands: Commands,
15 mut meshes: ResMut<Assets<Mesh>>,
16 mut materials: ResMut<Assets<StandardMaterial>>,
17) {
18 // plane
19 commands.spawn((
20 Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))),
21 MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
22 ));
23 // cube
24 // Assign vertex colors based on vertex positions
25 let mut colorful_cube = Mesh::from(Cuboid::default());
26 if let Some(VertexAttributeValues::Float32x3(positions)) =
27 colorful_cube.attribute(Mesh::ATTRIBUTE_POSITION)
28 {
29 let colors: Vec<[f32; 4]> = positions
30 .iter()
31 .map(|[r, g, b]| [(1. - *r) / 2., (1. - *g) / 2., (1. - *b) / 2., 1.])
32 .collect();
33 colorful_cube.insert_attribute(Mesh::ATTRIBUTE_COLOR, colors);
34 }
35 commands.spawn((
36 Mesh3d(meshes.add(colorful_cube)),
37 // This is the default color, but note that vertex colors are
38 // multiplied by the base color, so you'll likely want this to be
39 // white if using vertex colors.
40 MeshMaterial3d(materials.add(Color::srgb(1., 1., 1.))),
41 Transform::from_xyz(0.0, 0.5, 0.0),
42 ));
43
44 // Light
45 commands.spawn((
46 PointLight {
47 shadow_maps_enabled: true,
48 ..default()
49 },
50 Transform::from_xyz(4.0, 5.0, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
51 ));
52
53 // Camera
54 commands.spawn((
55 Camera3d::default(),
56 Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
57 ));
58}53fn setup_scene(
54 asset_server: Res<AssetServer>,
55 mut images: ResMut<Assets<Image>>,
56 mut commands: Commands,
57 mut meshes: ResMut<Assets<Mesh>>,
58 mut materials: ResMut<Assets<StandardMaterial>>,
59) {
60 commands.insert_resource(GlobalAmbientLight {
61 color: Color::WHITE,
62 brightness: 300.0,
63 ..default()
64 });
65 commands.insert_resource(CameraMode::Chase);
66 commands.spawn((
67 DirectionalLight {
68 illuminance: 3_000.0,
69 shadow_maps_enabled: true,
70 ..default()
71 },
72 Transform::default().looking_to(Vec3::new(-1.0, -0.7, -1.0), Vec3::X),
73 ));
74 // Sky
75 commands.spawn((
76 Mesh3d(meshes.add(Sphere::default())),
77 MeshMaterial3d(materials.add(StandardMaterial {
78 unlit: true,
79 base_color: Color::linear_rgb(0.1, 0.6, 1.0),
80 ..default()
81 })),
82 Transform::default().with_scale(Vec3::splat(-4000.0)),
83 ));
84 // Ground
85 let mut plane: Mesh = Plane3d::default().into();
86 let uv_size = 4000.0;
87 let uvs = vec![[uv_size, 0.0], [0.0, 0.0], [0.0, uv_size], [uv_size; 2]];
88 plane.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
89 commands.spawn((
90 Mesh3d(meshes.add(plane)),
91 MeshMaterial3d(materials.add(StandardMaterial {
92 base_color: Color::WHITE,
93 perceptual_roughness: 1.0,
94 base_color_texture: Some(images.add(uv_debug_texture())),
95 ..default()
96 })),
97 Transform::from_xyz(0.0, -0.65, 0.0).with_scale(Vec3::splat(80.)),
98 ));
99
100 spawn_cars(&asset_server, &mut meshes, &mut materials, &mut commands);
101 spawn_trees(&mut meshes, &mut materials, &mut commands);
102 spawn_barriers(&mut meshes, &mut materials, &mut commands);
103}49fn star(
50 mut commands: Commands,
51 // We will add a new Mesh for the star being created
52 mut meshes: ResMut<Assets<Mesh>>,
53) {
54 // Let's define the mesh for the object we want to draw: a nice star.
55 // We will specify here what kind of topology is used to define the mesh,
56 // that is, how triangles are built from the vertices. We will use a
57 // triangle list, meaning that each vertex of the triangle has to be
58 // specified. We set `RenderAssetUsages::RENDER_WORLD`, meaning this mesh
59 // will not be accessible in future frames from the `meshes` resource, in
60 // order to save on memory once it has been uploaded to the GPU.
61 let mut star = Mesh::new(
62 PrimitiveTopology::TriangleList,
63 RenderAssetUsages::RENDER_WORLD,
64 );
65
66 // Vertices need to have a position attribute. We will use the following
67 // vertices (I hope you can spot the star in the schema).
68 //
69 // 1
70 //
71 // 10 2
72 // 9 0 3
73 // 8 4
74 // 6
75 // 7 5
76 //
77 // These vertices are specified in 3D space.
78 let mut v_pos = vec![[0.0, 0.0, 0.0]];
79 for i in 0..10 {
80 // The angle between each vertex is 1/10 of a full rotation.
81 let a = i as f32 * PI / 5.0;
82 // The radius of inner vertices (even indices) is 100. For outer vertices (odd indices) it's 200.
83 let r = (1 - i % 2) as f32 * 100.0 + 100.0;
84 // Add the vertex position.
85 v_pos.push([r * ops::sin(a), r * ops::cos(a), 0.0]);
86 }
87 // Set the position attribute
88 star.insert_attribute(Mesh::ATTRIBUTE_POSITION, v_pos);
89 // And a RGB color attribute as well. A built-in `Mesh::ATTRIBUTE_COLOR` exists, but we
90 // use a custom vertex attribute here for demonstration purposes.
91 let mut v_color: Vec<u32> = vec![LinearRgba::BLACK.as_u32()];
92 v_color.extend_from_slice(&[LinearRgba::from(YELLOW).as_u32(); 10]);
93 star.insert_attribute(
94 MeshVertexAttribute::new("Vertex_Color", 1, VertexFormat::Uint32),
95 v_color,
96 );
97
98 // Now, we specify the indices of the vertex that are going to compose the
99 // triangles in our star. Vertices in triangles have to be specified in CCW
100 // winding (that will be the front face, colored). Since we are using
101 // triangle list, we will specify each triangle as 3 vertices
102 // First triangle: 0, 2, 1
103 // Second triangle: 0, 3, 2
104 // Third triangle: 0, 4, 3
105 // etc
106 // Last triangle: 0, 1, 10
107 let mut indices = vec![0, 1, 10];
108 for i in 2..=10 {
109 indices.extend_from_slice(&[0, i, i - 1]);
110 }
111 star.insert_indices(Indices::U32(indices));
112
113 // We can now spawn the entities for the star and the camera
114 commands.spawn((
115 // We use a marker component to identify the custom colored meshes
116 ColoredMesh2d,
117 // The `Handle<Mesh>` needs to be wrapped in a `Mesh2d` for 2D rendering
118 Mesh2d(meshes.add(star)),
119 ));
120
121 commands.spawn(Camera2d);
122}374fn add_raytracing_meshes_on_scene_load(
375 scene_ready: On<WorldInstanceReady>,
376 children: Query<&Children>,
377 mesh_query: Query<(
378 &Mesh3d,
379 &MeshMaterial3d<StandardMaterial>,
380 Option<&GltfMaterialName>,
381 )>,
382 mut meshes: ResMut<Assets<Mesh>>,
383 mut materials: ResMut<Assets<StandardMaterial>>,
384 mut commands: Commands,
385 args: Res<Args>,
386) {
387 for descendant in children.iter_descendants(scene_ready.entity) {
388 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
389 mesh_query.get(descendant)
390 {
391 // Add raytracing mesh component
392 commands
393 .entity(descendant)
394 .insert(RaytracingMesh3d(mesh_handle.clone()));
395
396 // Ensure meshes are Solari compatible
397 let mut mesh = meshes.get_mut(mesh_handle).unwrap();
398 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
399 let vertex_count = mesh.count_vertices();
400 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
401 mesh.insert_attribute(
402 Mesh::ATTRIBUTE_TANGENT,
403 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
404 );
405 }
406 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
407 mesh.generate_tangents().unwrap();
408 }
409 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
410 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
411 }
412 if let Some(indices) = mesh.indices_mut()
413 && let Indices::U16(_) = indices
414 {
415 *indices = Indices::U32(indices.iter().map(|i| i as u32).collect());
416 }
417
418 // Prevent rasterization if using pathtracer
419 if args.pathtracer == Some(true) {
420 commands.entity(descendant).remove::<Mesh3d>();
421 }
422
423 // Adjust scene materials to better demo Solari features
424 if material_name.map(|s| s.0.as_str()) == Some("material") {
425 let mut material = materials.get_mut(material_handle).unwrap();
426 material.emissive = LinearRgba::BLACK;
427 }
428 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
429 let mut material = materials.get_mut(material_handle).unwrap();
430 material.emissive =
431 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
432 material.alpha_mode = AlphaMode::Opaque;
433 material.specular_transmission = 0.0;
434
435 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
436 }
437 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
438 let mut material = materials.get_mut(material_handle).unwrap();
439 material.alpha_mode = AlphaMode::Opaque;
440 material.specular_transmission = 0.0;
441 }
442 }
443 }
444}Sourcepub fn try_insert_attribute(
&mut self,
attribute: MeshVertexAttribute,
values: impl Into<VertexAttributeValues>,
) -> Result<(), MeshAccessError>
pub fn try_insert_attribute( &mut self, attribute: MeshVertexAttribute, values: impl Into<VertexAttributeValues>, ) -> Result<(), MeshAccessError>
Sets the data for a vertex attribute (position, normal, etc.). The name will
often be one of the associated constants such as Mesh::ATTRIBUTE_POSITION.
Aabb of entities with modified mesh are not updated automatically.
Returns an error if the mesh data has been extracted to RenderWorld.
§Panics
Panics when the format of the values does not match the attribute’s format.
Sourcepub fn with_inserted_attribute(
self,
attribute: MeshVertexAttribute,
values: impl Into<VertexAttributeValues>,
) -> Mesh
pub fn with_inserted_attribute( self, attribute: MeshVertexAttribute, values: impl Into<VertexAttributeValues>, ) -> Mesh
Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.).
The name will often be one of the associated constants such as Mesh::ATTRIBUTE_POSITION.
(Alternatively, you can use Mesh::insert_attribute to mutate an existing mesh in-place)
Aabb of entities with modified mesh are not updated automatically.
§Panics
Panics when the format of the values does not match the attribute’s format.
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_with_inserted_attribute
Examples found in repository?
85 fn from(line: LineList) -> Self {
86 let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect();
87
88 Mesh::new(
89 // This tells wgpu that the positions are list of lines
90 // where every pair is a start and end point
91 PrimitiveTopology::LineList,
92 RenderAssetUsages::RENDER_WORLD,
93 )
94 // Add the vertices positions as an attribute
95 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
96 }
97}
98
99/// A list of points that will have a line drawn between each consecutive points
100#[derive(Debug, Clone)]
101struct LineStrip {
102 points: Vec<Vec3>,
103 indices: Indices,
104}
105
106impl From<LineStrip> for Mesh {
107 fn from(line: LineStrip) -> Self {
108 Mesh::new(
109 // This tells wgpu that the positions are a list of points
110 // where a line will be drawn between each consecutive point
111 PrimitiveTopology::LineStrip,
112 RenderAssetUsages::RENDER_WORLD,
113 )
114 // Add the point positions as an attribute
115 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, line.points)
116 .with_inserted_indices(line.indices)
117 }More examples
30fn setup(
31 mut commands: Commands,
32 mut meshes: ResMut<Assets<Mesh>>,
33 mut materials: ResMut<Assets<CustomMaterial>>,
34) {
35 let mesh = Mesh::from(Cuboid::default())
36 // Sets the custom attribute
37 .with_inserted_attribute(
38 ATTRIBUTE_BLEND_COLOR,
39 // The cube mesh has 24 vertices (6 faces, 4 vertices per face), so we insert one BlendColor for each
40 vec![[1.0, 0.0, 0.0, 1.0]; 24],
41 );
42
43 // cube
44 commands.spawn((
45 Mesh3d(meshes.add(mesh)),
46 MeshMaterial3d(materials.add(CustomMaterial {
47 color: LinearRgba::WHITE,
48 })),
49 Transform::from_xyz(0.0, 0.5, 0.0),
50 ));
51
52 // camera
53 commands.spawn((
54 Camera3d::default(),
55 Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
56 ));
57}54fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
55 // Build a custom triangle mesh with colors
56 // We define a custom mesh because the examples only uses a limited
57 // set of vertex attributes for simplicity
58 let mesh = Mesh::new(
59 PrimitiveTopology::TriangleList,
60 RenderAssetUsages::default(),
61 )
62 .with_inserted_indices(Indices::U32(vec![0, 1, 2]))
63 .with_inserted_attribute(
64 Mesh::ATTRIBUTE_POSITION,
65 vec![
66 vec3(-0.5, -0.5, 0.0),
67 vec3(0.5, -0.5, 0.0),
68 vec3(0.0, 0.25, 0.0),
69 ],
70 )
71 .with_inserted_attribute(
72 Mesh::ATTRIBUTE_COLOR,
73 vec![
74 vec4(1.0, 0.0, 0.0, 1.0),
75 vec4(0.0, 1.0, 0.0, 1.0),
76 vec4(0.0, 0.0, 1.0, 1.0),
77 ],
78 );
79
80 // spawn 3 triangles to show that batching works
81 for (x, y) in [-0.5, 0.0, 0.5].into_iter().zip([-0.25, 0.5, -0.25]) {
82 // Spawn an entity with all the required components for it to be rendered with our custom pipeline
83 commands.spawn((
84 // We use a marker component to identify the mesh that will be rendered
85 // with our specialized pipeline
86 CustomRenderedEntity,
87 // We need to add the mesh handle to the entity
88 Mesh3d(meshes.add(mesh.clone())),
89 Transform::from_xyz(x, y, 0.0),
90 ));
91 }
92
93 // Spawn the camera.
94 commands.spawn((
95 Camera3d::default(),
96 // Move the camera back a bit to see all the triangles
97 Transform::from_xyz(0.0, 0.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
98 ));
99}514 fn build(&self) -> Mesh {
515 let radius = self.heart.radius;
516 // The curved parts of each wing (half) of the heart have an angle of `PI * 1.25` or 225°
517 let wing_angle = PI * 1.25;
518
519 // We create buffers for the vertices, their normals and UVs, as well as the indices used to connect the vertices.
520 let mut vertices = Vec::with_capacity(2 * self.resolution);
521 let mut uvs = Vec::with_capacity(2 * self.resolution);
522 let mut indices = Vec::with_capacity(6 * self.resolution - 9);
523 // Since the heart is flat, we know all the normals are identical already.
524 let normals = vec![[0f32, 0f32, 1f32]; 2 * self.resolution];
525
526 // The point in the middle of the two curved parts of the heart
527 vertices.push([0.0; 3]);
528 uvs.push([0.5, 0.5]);
529
530 // The left wing of the heart, starting from the point in the middle.
531 for i in 1..self.resolution {
532 let angle = (i as f32 / self.resolution as f32) * wing_angle;
533 let (sin, cos) = ops::sin_cos(angle);
534 vertices.push([radius * (cos - 1.0), radius * sin, 0.0]);
535 uvs.push([0.5 - (cos - 1.0) / 4., 0.5 - sin / 2.]);
536 }
537
538 // The bottom tip of the heart
539 vertices.push([0.0, radius * (-1. - SQRT_2), 0.0]);
540 uvs.push([0.5, 1.]);
541
542 // The right wing of the heart, starting from the bottom most point and going towards the middle point.
543 for i in 0..self.resolution - 1 {
544 let angle = (i as f32 / self.resolution as f32) * wing_angle - PI / 4.;
545 let (sin, cos) = ops::sin_cos(angle);
546 vertices.push([radius * (cos + 1.0), radius * sin, 0.0]);
547 uvs.push([0.5 - (cos + 1.0) / 4., 0.5 - sin / 2.]);
548 }
549
550 // This is where we build all the triangles from the points created above.
551 // Each triangle has one corner on the middle point with the other two being adjacent points on the perimeter of the heart.
552 for i in 2..2 * self.resolution as u32 {
553 indices.extend_from_slice(&[i - 1, i, 0]);
554 }
555
556 // Here, the actual `Mesh` is created. We set the indices, vertices, normals and UVs created above and specify the topology of the mesh.
557 Mesh::new(
558 bevy::mesh::PrimitiveTopology::TriangleList,
559 RenderAssetUsages::default(),
560 )
561 .with_inserted_indices(bevy::mesh::Indices::U32(indices))
562 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
563 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
564 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
565 }82fn setup(
83 mut commands: Commands,
84 mut meshes: ResMut<Assets<Mesh>>,
85 mut materials: ResMut<Assets<StandardMaterial>>,
86) {
87 // a truly empty mesh will error if used in Mesh3d
88 // so we set up the data to be what we want the compute shader to output
89 // We're using 36 indices and 24 vertices which is directly taken from
90 // the Bevy Cuboid mesh implementation.
91 //
92 // We allocate 50 spots for each attribute here because
93 // it is *very important* that the amount of data allocated here is
94 // *bigger* than (or exactly equal to) the amount of data we intend to
95 // write from the compute shader. This amount of data defines how big
96 // the buffer we get from the mesh_allocator will be, which in turn
97 // defines how big the buffer is when we're in the compute shader.
98 //
99 // If it turns out you don't need all of the space when the compute shader
100 // is writing data, you can write NaN to the rest of the data.
101 let empty_mesh = {
102 let mut mesh = Mesh::new(
103 PrimitiveTopology::TriangleList,
104 RenderAssetUsages::RENDER_WORLD,
105 )
106 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vec![[0.; 3]; 50])
107 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.; 3]; 50])
108 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.; 2]; 50])
109 .with_inserted_indices(Indices::U32(vec![0; 50]));
110
111 mesh.asset_usage = RenderAssetUsages::RENDER_WORLD;
112 mesh
113 };
114
115 let handle = meshes.add(empty_mesh);
116
117 // we spawn two "users" of the mesh handle,
118 // but only insert `GenerateMesh` on one of them
119 // to show that the mesh handle works as usual
120 commands.spawn((
121 GenerateMesh(handle.clone()),
122 Mesh3d(handle.clone()),
123 MeshMaterial3d(materials.add(StandardMaterial {
124 base_color: RED_400.into(),
125 ..default()
126 })),
127 Transform::from_xyz(-2.5, 1.5, 0.),
128 ));
129
130 commands.spawn((
131 Mesh3d(handle),
132 MeshMaterial3d(materials.add(StandardMaterial {
133 base_color: SKY_400.into(),
134 ..default()
135 })),
136 Transform::from_xyz(2.5, 1.5, 0.),
137 ));
138
139 // some additional scene elements.
140 // This mesh specifically is here so that we don't assume
141 // mesh_allocator offsets that would only work if we had
142 // one mesh in the scene.
143 commands.spawn((
144 Mesh3d(meshes.add(Circle::new(4.0))),
145 MeshMaterial3d(materials.add(Color::WHITE)),
146 Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
147 ));
148 commands.spawn((
149 PointLight {
150 shadow_maps_enabled: true,
151 ..default()
152 },
153 Transform::from_xyz(4.0, 8.0, 4.0),
154 ));
155 // camera
156 commands.spawn((
157 Camera3d::default(),
158 Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
159 ));
160}92fn setup_meshes(
93 mut commands: Commands,
94 mut mesh_assets: ResMut<Assets<Mesh>>,
95 mut material_assets: ResMut<Assets<StandardMaterial>>,
96 mut inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
97) {
98 // Create a mesh with two rectangles.
99 let unskinned_mesh = Mesh::new(
100 PrimitiveTopology::TriangleList,
101 RenderAssetUsages::default(),
102 )
103 .with_inserted_attribute(
104 Mesh::ATTRIBUTE_POSITION,
105 vec![
106 [-0.3, -0.3, 0.0],
107 [0.3, -0.3, 0.0],
108 [-0.3, 0.3, 0.0],
109 [0.3, 0.3, 0.0],
110 [-0.4, 0.8, 0.0],
111 [0.4, 0.8, 0.0],
112 [-0.4, 1.8, 0.0],
113 [0.4, 1.8, 0.0],
114 ],
115 )
116 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 8])
117 .with_inserted_indices(Indices::U16(vec![0, 1, 3, 0, 3, 2, 4, 5, 7, 4, 7, 6]));
118
119 // Copy the mesh and add skinning attributes that bind each rectangle to a joint.
120 let skinned_mesh = unskinned_mesh
121 .clone()
122 .with_inserted_attribute(
123 Mesh::ATTRIBUTE_JOINT_INDEX,
124 VertexAttributeValues::Uint16x4(vec![
125 [0, 0, 0, 0],
126 [0, 0, 0, 0],
127 [0, 0, 0, 0],
128 [0, 0, 0, 0],
129 [1, 0, 0, 0],
130 [1, 0, 0, 0],
131 [1, 0, 0, 0],
132 [1, 0, 0, 0],
133 ]),
134 )
135 .with_inserted_attribute(
136 Mesh::ATTRIBUTE_JOINT_WEIGHT,
137 vec![[1.00, 0.00, 0.0, 0.0]; 8],
138 );
139
140 let unskinned_mesh_handle = mesh_assets.add(unskinned_mesh);
141 let skinned_mesh_handle = mesh_assets.add(skinned_mesh);
142
143 let inverse_bindposes_handle = inverse_bindposes_assets.add(vec![
144 Mat4::IDENTITY,
145 Mat4::from_translation(Vec3::new(0.0, -1.3, 0.0)),
146 ]);
147
148 let mesh_material_handle = material_assets.add(StandardMaterial::default());
149
150 let background_material_handle = material_assets.add(StandardMaterial {
151 base_color: Color::srgb(0.05, 0.15, 0.05),
152 reflectance: 0.2,
153 ..default()
154 });
155
156 #[derive(PartialEq)]
157 enum Variation {
158 Normal,
159 MissingMeshAttributes,
160 MissingJointEntity,
161 MissingSkinnedMeshComponent,
162 }
163
164 for (index, variation) in [
165 Variation::Normal,
166 Variation::MissingMeshAttributes,
167 Variation::MissingJointEntity,
168 Variation::MissingSkinnedMeshComponent,
169 ]
170 .into_iter()
171 .enumerate()
172 {
173 // Skip variations that are currently broken. See https://github.com/bevyengine/bevy/issues/16929,
174 // https://github.com/bevyengine/bevy/pull/18074.
175 if (variation == Variation::MissingSkinnedMeshComponent)
176 || (variation == Variation::MissingMeshAttributes)
177 {
178 continue;
179 }
180
181 let transform = Transform::from_xyz(((index as f32) - 1.5) * 4.5, 0.0, 0.0);
182
183 let joint_0 = commands.spawn(transform).id();
184
185 let joint_1 = commands
186 .spawn((ChildOf(joint_0), AnimatedJoint, Transform::IDENTITY))
187 .id();
188
189 if variation == Variation::MissingJointEntity {
190 commands.entity(joint_1).despawn();
191 }
192
193 let mesh_handle = match variation {
194 Variation::MissingMeshAttributes => &unskinned_mesh_handle,
195 _ => &skinned_mesh_handle,
196 };
197
198 let mut entity_commands = commands.spawn((
199 Mesh3d(mesh_handle.clone()),
200 MeshMaterial3d(mesh_material_handle.clone()),
201 transform,
202 ));
203
204 if variation != Variation::MissingSkinnedMeshComponent {
205 entity_commands.insert(SkinnedMesh {
206 inverse_bindposes: inverse_bindposes_handle.clone(),
207 joints: vec![joint_0, joint_1],
208 });
209 }
210
211 // Add a square behind the mesh to distinguish it from the other meshes.
212 commands.spawn((
213 Transform::from_xyz(transform.translation.x, transform.translation.y, -0.8),
214 Mesh3d(mesh_assets.add(Plane3d::default().mesh().size(4.3, 4.3).normal(Dir3::Z))),
215 MeshMaterial3d(background_material_handle.clone()),
216 ));
217 }
218}Sourcepub fn try_with_inserted_attribute(
self,
attribute: MeshVertexAttribute,
values: impl Into<VertexAttributeValues>,
) -> Result<Mesh, MeshAccessError>
pub fn try_with_inserted_attribute( self, attribute: MeshVertexAttribute, values: impl Into<VertexAttributeValues>, ) -> Result<Mesh, MeshAccessError>
Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.).
The name will often be one of the associated constants such as Mesh::ATTRIBUTE_POSITION.
(Alternatively, you can use Mesh::insert_attribute to mutate an existing mesh in-place)
Aabb of entities with modified mesh are not updated automatically.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn remove_attribute(
&mut self,
attribute: impl Into<MeshVertexAttributeId>,
) -> Option<VertexAttributeValues>
pub fn remove_attribute( &mut self, attribute: impl Into<MeshVertexAttributeId>, ) -> Option<VertexAttributeValues>
Removes the data for a vertex attribute
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_remove_attribute
Examples found in repository?
374fn add_raytracing_meshes_on_scene_load(
375 scene_ready: On<WorldInstanceReady>,
376 children: Query<&Children>,
377 mesh_query: Query<(
378 &Mesh3d,
379 &MeshMaterial3d<StandardMaterial>,
380 Option<&GltfMaterialName>,
381 )>,
382 mut meshes: ResMut<Assets<Mesh>>,
383 mut materials: ResMut<Assets<StandardMaterial>>,
384 mut commands: Commands,
385 args: Res<Args>,
386) {
387 for descendant in children.iter_descendants(scene_ready.entity) {
388 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
389 mesh_query.get(descendant)
390 {
391 // Add raytracing mesh component
392 commands
393 .entity(descendant)
394 .insert(RaytracingMesh3d(mesh_handle.clone()));
395
396 // Ensure meshes are Solari compatible
397 let mut mesh = meshes.get_mut(mesh_handle).unwrap();
398 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
399 let vertex_count = mesh.count_vertices();
400 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
401 mesh.insert_attribute(
402 Mesh::ATTRIBUTE_TANGENT,
403 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
404 );
405 }
406 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
407 mesh.generate_tangents().unwrap();
408 }
409 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
410 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
411 }
412 if let Some(indices) = mesh.indices_mut()
413 && let Indices::U16(_) = indices
414 {
415 *indices = Indices::U32(indices.iter().map(|i| i as u32).collect());
416 }
417
418 // Prevent rasterization if using pathtracer
419 if args.pathtracer == Some(true) {
420 commands.entity(descendant).remove::<Mesh3d>();
421 }
422
423 // Adjust scene materials to better demo Solari features
424 if material_name.map(|s| s.0.as_str()) == Some("material") {
425 let mut material = materials.get_mut(material_handle).unwrap();
426 material.emissive = LinearRgba::BLACK;
427 }
428 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
429 let mut material = materials.get_mut(material_handle).unwrap();
430 material.emissive =
431 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
432 material.alpha_mode = AlphaMode::Opaque;
433 material.specular_transmission = 0.0;
434
435 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
436 }
437 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
438 let mut material = materials.get_mut(material_handle).unwrap();
439 material.alpha_mode = AlphaMode::Opaque;
440 material.specular_transmission = 0.0;
441 }
442 }
443 }
444}Sourcepub fn try_remove_attribute(
&mut self,
attribute: impl Into<MeshVertexAttributeId>,
) -> Result<VertexAttributeValues, MeshAccessError>
pub fn try_remove_attribute( &mut self, attribute: impl Into<MeshVertexAttributeId>, ) -> Result<VertexAttributeValues, MeshAccessError>
Removes the data for a vertex attribute
Returns an error if the mesh data has been extracted to RenderWorldor
if the attribute does not exist.
Sourcepub fn with_removed_attribute(
self,
attribute: impl Into<MeshVertexAttributeId>,
) -> Mesh
pub fn with_removed_attribute( self, attribute: impl Into<MeshVertexAttributeId>, ) -> Mesh
Consumes the mesh and returns a mesh without the data for a vertex attribute
(Alternatively, you can use Mesh::remove_attribute to mutate an existing mesh in-place)
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_with_removed_attribute
Sourcepub fn try_with_removed_attribute(
self,
attribute: impl Into<MeshVertexAttributeId>,
) -> Result<Mesh, MeshAccessError>
pub fn try_with_removed_attribute( self, attribute: impl Into<MeshVertexAttributeId>, ) -> Result<Mesh, MeshAccessError>
Consumes the mesh and returns a mesh without the data for a vertex attribute
(Alternatively, you can use Mesh::remove_attribute to mutate an existing mesh in-place)
Returns an error if the mesh data has been extracted to RenderWorldor
if the attribute does not exist.
Sourcepub fn contains_attribute(&self, id: impl Into<MeshVertexAttributeId>) -> bool
pub fn contains_attribute(&self, id: impl Into<MeshVertexAttributeId>) -> bool
Returns a bool indicating if the attribute is present in this mesh’s vertex data.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_contains_attribute
Examples found in repository?
374fn add_raytracing_meshes_on_scene_load(
375 scene_ready: On<WorldInstanceReady>,
376 children: Query<&Children>,
377 mesh_query: Query<(
378 &Mesh3d,
379 &MeshMaterial3d<StandardMaterial>,
380 Option<&GltfMaterialName>,
381 )>,
382 mut meshes: ResMut<Assets<Mesh>>,
383 mut materials: ResMut<Assets<StandardMaterial>>,
384 mut commands: Commands,
385 args: Res<Args>,
386) {
387 for descendant in children.iter_descendants(scene_ready.entity) {
388 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
389 mesh_query.get(descendant)
390 {
391 // Add raytracing mesh component
392 commands
393 .entity(descendant)
394 .insert(RaytracingMesh3d(mesh_handle.clone()));
395
396 // Ensure meshes are Solari compatible
397 let mut mesh = meshes.get_mut(mesh_handle).unwrap();
398 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
399 let vertex_count = mesh.count_vertices();
400 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
401 mesh.insert_attribute(
402 Mesh::ATTRIBUTE_TANGENT,
403 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
404 );
405 }
406 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
407 mesh.generate_tangents().unwrap();
408 }
409 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
410 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
411 }
412 if let Some(indices) = mesh.indices_mut()
413 && let Indices::U16(_) = indices
414 {
415 *indices = Indices::U32(indices.iter().map(|i| i as u32).collect());
416 }
417
418 // Prevent rasterization if using pathtracer
419 if args.pathtracer == Some(true) {
420 commands.entity(descendant).remove::<Mesh3d>();
421 }
422
423 // Adjust scene materials to better demo Solari features
424 if material_name.map(|s| s.0.as_str()) == Some("material") {
425 let mut material = materials.get_mut(material_handle).unwrap();
426 material.emissive = LinearRgba::BLACK;
427 }
428 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
429 let mut material = materials.get_mut(material_handle).unwrap();
430 material.emissive =
431 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
432 material.alpha_mode = AlphaMode::Opaque;
433 material.specular_transmission = 0.0;
434
435 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
436 }
437 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
438 let mut material = materials.get_mut(material_handle).unwrap();
439 material.alpha_mode = AlphaMode::Opaque;
440 material.specular_transmission = 0.0;
441 }
442 }
443 }
444}Sourcepub fn try_contains_attribute(
&self,
id: impl Into<MeshVertexAttributeId>,
) -> Result<bool, MeshAccessError>
pub fn try_contains_attribute( &self, id: impl Into<MeshVertexAttributeId>, ) -> Result<bool, MeshAccessError>
Returns a bool indicating if the attribute is present in this mesh’s vertex data.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn attribute(
&self,
id: impl Into<MeshVertexAttributeId>,
) -> Option<&VertexAttributeValues>
pub fn attribute( &self, id: impl Into<MeshVertexAttributeId>, ) -> Option<&VertexAttributeValues>
Retrieves the data currently set to the vertex attribute with the specified MeshVertexAttributeId.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_attribute or Mesh::try_attribute_option
Examples found in repository?
13fn setup(
14 mut commands: Commands,
15 mut meshes: ResMut<Assets<Mesh>>,
16 mut materials: ResMut<Assets<StandardMaterial>>,
17) {
18 // plane
19 commands.spawn((
20 Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))),
21 MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
22 ));
23 // cube
24 // Assign vertex colors based on vertex positions
25 let mut colorful_cube = Mesh::from(Cuboid::default());
26 if let Some(VertexAttributeValues::Float32x3(positions)) =
27 colorful_cube.attribute(Mesh::ATTRIBUTE_POSITION)
28 {
29 let colors: Vec<[f32; 4]> = positions
30 .iter()
31 .map(|[r, g, b]| [(1. - *r) / 2., (1. - *g) / 2., (1. - *b) / 2., 1.])
32 .collect();
33 colorful_cube.insert_attribute(Mesh::ATTRIBUTE_COLOR, colors);
34 }
35 commands.spawn((
36 Mesh3d(meshes.add(colorful_cube)),
37 // This is the default color, but note that vertex colors are
38 // multiplied by the base color, so you'll likely want this to be
39 // white if using vertex colors.
40 MeshMaterial3d(materials.add(Color::srgb(1., 1., 1.))),
41 Transform::from_xyz(0.0, 0.5, 0.0),
42 ));
43
44 // Light
45 commands.spawn((
46 PointLight {
47 shadow_maps_enabled: true,
48 ..default()
49 },
50 Transform::from_xyz(4.0, 5.0, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
51 ));
52
53 // Camera
54 commands.spawn((
55 Camera3d::default(),
56 Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
57 ));
58}More examples
254fn spawn_small_cubes(
255 commands: &mut Commands,
256 meshes: &mut Assets<Mesh>,
257 materials: &mut Assets<StandardMaterial>,
258) {
259 // Add the cube mesh.
260 let small_cube = meshes.add(Cuboid::new(
261 SMALL_CUBE_SIZE,
262 SMALL_CUBE_SIZE,
263 SMALL_CUBE_SIZE,
264 ));
265
266 // Add the cube material.
267 let small_cube_material = materials.add(StandardMaterial {
268 base_color: SILVER.into(),
269 ..default()
270 });
271
272 // Create the entity that the small cubes will be parented to. This is the
273 // entity that we rotate.
274 let sphere_parent = commands
275 .spawn(Transform::from_translation(Vec3::ZERO))
276 .insert(Visibility::default())
277 .insert(SphereParent)
278 .id();
279
280 // Now we have to figure out where to place the cubes. To do that, we create
281 // a sphere mesh, but we don't add it to the scene. Instead, we inspect the
282 // sphere mesh to find the positions of its vertices, and spawn a small cube
283 // at each one. That way, we end up with a bunch of cubes arranged in a
284 // spherical shape.
285
286 // Create the sphere mesh, and extract the positions of its vertices.
287 let sphere = Sphere::new(OUTER_RADIUS)
288 .mesh()
289 .ico(OUTER_SUBDIVISION_COUNT)
290 .unwrap();
291 let sphere_positions = sphere.attribute(Mesh::ATTRIBUTE_POSITION).unwrap();
292
293 // At each vertex, create a small cube.
294 for sphere_position in sphere_positions.as_float3().unwrap() {
295 let sphere_position = Vec3::from_slice(sphere_position);
296 let small_cube = commands
297 .spawn(Mesh3d(small_cube.clone()))
298 .insert(MeshMaterial3d(small_cube_material.clone()))
299 .insert(Transform::from_translation(sphere_position))
300 .id();
301 commands.entity(sphere_parent).add_child(small_cube);
302 }
303}Sourcepub fn try_attribute(
&self,
id: impl Into<MeshVertexAttributeId>,
) -> Result<&VertexAttributeValues, MeshAccessError>
pub fn try_attribute( &self, id: impl Into<MeshVertexAttributeId>, ) -> Result<&VertexAttributeValues, MeshAccessError>
Retrieves the data currently set to the vertex attribute with the specified MeshVertexAttributeId.
Returns an error if the mesh data has been extracted to RenderWorldor
if the attribute does not exist.
Sourcepub fn try_attribute_option(
&self,
id: impl Into<MeshVertexAttributeId>,
) -> Result<Option<&VertexAttributeValues>, MeshAccessError>
pub fn try_attribute_option( &self, id: impl Into<MeshVertexAttributeId>, ) -> Result<Option<&VertexAttributeValues>, MeshAccessError>
Retrieves the data currently set to the vertex attribute with the specified MeshVertexAttributeId.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn attribute_mut(
&mut self,
id: impl Into<MeshVertexAttributeId>,
) -> Option<&mut VertexAttributeValues>
pub fn attribute_mut( &mut self, id: impl Into<MeshVertexAttributeId>, ) -> Option<&mut VertexAttributeValues>
Retrieves the data currently set to the vertex attribute with the specified name mutably.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_attribute_mut
Examples found in repository?
254fn toggle_texture(mesh_to_change: &mut Mesh) {
255 // Get a mutable reference to the values of the UV attribute, so we can iterate over it.
256 let uv_attribute = mesh_to_change.attribute_mut(Mesh::ATTRIBUTE_UV_0).unwrap();
257 // The format of the UV coordinates should be Float32x2.
258 let VertexAttributeValues::Float32x2(uv_attribute) = uv_attribute else {
259 panic!("Unexpected vertex format, expected Float32x2.");
260 };
261
262 // Iterate over the UV coordinates, and change them as we want.
263 for uv_coord in uv_attribute.iter_mut() {
264 // If the UV coordinate points to the upper, "dirt+grass" part of the texture...
265 if (uv_coord[1] + 0.5) < 1.0 {
266 // ... point to the equivalent lower, "sand+water" part instead,
267 uv_coord[1] += 0.5;
268 } else {
269 // else, point back to the upper, "dirt+grass" part.
270 uv_coord[1] -= 0.5;
271 }
272 }
273}More examples
16fn find_top_material_and_mesh(
17 mut materials: ResMut<Assets<StandardMaterial>>,
18 mut meshes: ResMut<Assets<Mesh>>,
19 time: Res<Time>,
20 mat_query: Query<(
21 &MeshMaterial3d<StandardMaterial>,
22 &Mesh3d,
23 &GltfMaterialName,
24 )>,
25) {
26 for (mat_handle, mesh_handle, name) in mat_query.iter() {
27 // locate a material by material name
28 if name.0 == "Top" {
29 if let Some(mut material) = materials.get_mut(mat_handle) {
30 if let Color::Hsla(ref mut hsla) = material.base_color {
31 *hsla = hsla.rotate_hue(time.delta_secs() * 100.0);
32 } else {
33 material.base_color = Color::from(Hsla::hsl(0.0, 0.9, 0.7));
34 }
35 }
36
37 if let Some(mut mesh) = meshes.get_mut(mesh_handle)
38 && let Some(VertexAttributeValues::Float32x3(positions)) =
39 mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION)
40 {
41 for position in positions {
42 *position = (
43 position[0],
44 1.5 + 0.5 * ops::sin(time.elapsed_secs() / 2.0),
45 position[2],
46 )
47 .into();
48 }
49 }
50 }
51 }
52}175fn alter_mesh(
176 mut is_mesh_scaled: Local<bool>,
177 left_shape: Single<&Mesh3d, With<Left>>,
178 mut meshes: ResMut<Assets<Mesh>>,
179) {
180 // Obtain a mutable reference to the Mesh asset.
181 let Some(mut mesh) = meshes.get_mut(*left_shape) else {
182 return;
183 };
184
185 // Now we can directly manipulate vertices on the mesh. Here, we're just scaling in and out
186 // for demonstration purposes. This will affect all entities currently using the asset.
187 //
188 // To do this, we need to grab the stored attributes of each vertex. `Float32x3` just describes
189 // the format in which the attributes will be read: each position consists of an array of three
190 // f32 corresponding to x, y, and z.
191 //
192 // `ATTRIBUTE_POSITION` is a constant indicating that we want to know where the vertex is
193 // located in space (as opposed to which way its normal is facing, vertex color, or other
194 // details).
195 if let Some(VertexAttributeValues::Float32x3(positions)) =
196 mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION)
197 {
198 // Check a Local value (which only this system can make use of) to determine if we're
199 // currently scaled up or not.
200 let scale_factor = if *is_mesh_scaled { 0.5 } else { 2.0 };
201
202 for position in positions.iter_mut() {
203 // Apply the scale factor to each of x, y, and z.
204 position[0] *= scale_factor;
205 position[1] *= scale_factor;
206 position[2] *= scale_factor;
207 }
208
209 // Flip the local value to reverse the behavior next time the key is pressed.
210 *is_mesh_scaled = !*is_mesh_scaled;
211 }
212}200fn setup_many_lights(
201 mut commands: Commands,
202 asset_server: Res<AssetServer>,
203 mut meshes: ResMut<Assets<Mesh>>,
204 mut materials: ResMut<Assets<StandardMaterial>>,
205 args: Res<Args>,
206 #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))] dlss_rr_supported: Option<
207 Res<DlssRayReconstructionSupported>,
208 >,
209) {
210 let mut rng = ChaCha8Rng::seed_from_u64(42);
211
212 let mut plane_mesh = Plane3d::default()
213 .mesh()
214 .size(400.0, 400.0)
215 .build()
216 .with_generated_tangents()
217 .unwrap();
218 match plane_mesh.attribute_mut(Mesh::ATTRIBUTE_UV_0).unwrap() {
219 VertexAttributeValues::Float32x2(items) => {
220 items.iter_mut().flatten().for_each(|x| *x *= 3.0);
221 }
222 _ => unreachable!(),
223 }
224 let plane_mesh = meshes.add(plane_mesh);
225 let cube_mesh = meshes.add(
226 Cuboid::default()
227 .mesh()
228 .build()
229 .with_generated_tangents()
230 .unwrap(),
231 );
232 let sphere_mesh = meshes.add(
233 Sphere::new(1.0)
234 .mesh()
235 .build()
236 .with_generated_tangents()
237 .unwrap(),
238 );
239
240 commands
241 .spawn((
242 RaytracingMesh3d(plane_mesh.clone()),
243 MeshMaterial3d(
244 materials.add(StandardMaterial {
245 base_color_texture: Some(
246 asset_server
247 .load_builder()
248 .with_settings::<ImageLoaderSettings>(|settings| {
249 settings
250 .sampler
251 .get_or_init_descriptor()
252 .set_address_mode(ImageAddressMode::Repeat);
253 })
254 .load("textures/uv_checker_bw.png"),
255 ),
256 perceptual_roughness: 0.0,
257 ..default()
258 }),
259 ),
260 ))
261 .insert_if(Mesh3d(plane_mesh), || args.pathtracer != Some(true));
262
263 for _ in 0..8000 {
264 commands
265 .spawn((
266 RaytracingMesh3d(cube_mesh.clone()),
267 MeshMaterial3d(materials.add(StandardMaterial {
268 base_color: Color::srgb(rng.random(), rng.random(), rng.random()),
269 perceptual_roughness: rng.random(),
270 ..default()
271 })),
272 Transform::default()
273 .with_scale(Vec3 {
274 x: rng.random_range(0.2..=2.0),
275 y: rng.random_range(0.2..=2.0),
276 z: rng.random_range(0.2..=2.0),
277 })
278 .with_translation(Vec3::new(
279 rng.random_range(-180.0..=180.0),
280 0.2,
281 rng.random_range(-180.0..=180.0),
282 )),
283 ))
284 .insert_if(Mesh3d(cube_mesh.clone()), || args.pathtracer != Some(true));
285 }
286
287 for x in -10..=10 {
288 for y in -10..=10 {
289 commands
290 .spawn((
291 RaytracingMesh3d(sphere_mesh.clone()),
292 MeshMaterial3d(
293 materials.add(StandardMaterial {
294 emissive: Color::linear_rgb(
295 rng.random::<f32>() * 60000.0,
296 rng.random::<f32>() * 60000.0,
297 rng.random::<f32>() * 60000.0,
298 )
299 .into(),
300 ..default()
301 }),
302 ),
303 Transform::default().with_translation(Vec3::new(
304 (x * 20) as f32,
305 7.0,
306 (y * 20) as f32,
307 )),
308 ))
309 .insert_if(Mesh3d(sphere_mesh.clone()), || {
310 args.pathtracer != Some(true)
311 });
312 }
313 }
314
315 let mut camera = commands.spawn((
316 Camera3d::default(),
317 Camera {
318 clear_color: ClearColorConfig::Custom(Color::BLACK),
319 ..default()
320 },
321 FreeCamera {
322 walk_speed: 3.0,
323 run_speed: 10.0,
324 ..Default::default()
325 },
326 Transform::from_translation(Vec3::new(6.11329, 166.74896, 451.8226)).with_rotation(
327 Quat::from_xyzw(-0.183938, 0.009093744, 0.0017017953, 0.9828943),
328 ),
329 // Msaa::Off and CameraMainTextureUsages with STORAGE_BINDING are required for Solari
330 CameraMainTextureUsages::default().with(TextureUsages::STORAGE_BINDING),
331 Msaa::Off,
332 Bloom {
333 intensity: 0.1,
334 ..Bloom::NATURAL
335 },
336 ));
337
338 if args.pathtracer == Some(true) {
339 camera.insert(Pathtracer::default());
340 } else {
341 camera.insert(SolariLighting::default());
342 }
343
344 // Using DLSS Ray Reconstruction for denoising (and cheaper rendering via upscaling) is _highly_ recommended when using Solari
345 #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
346 if dlss_rr_supported.is_some() {
347 camera.insert(Dlss::<DlssRayReconstructionFeature> {
348 perf_quality_mode: Default::default(),
349 reset: Default::default(),
350 _phantom_data: Default::default(),
351 });
352 }
353
354 commands.spawn((
355 Node {
356 position_type: PositionType::Absolute,
357 right: px(0.0),
358 padding: px(4.0).all(),
359 border_radius: BorderRadius::bottom_left(px(4.0)),
360 ..default()
361 },
362 BackgroundColor(Color::srgba(0.10, 0.10, 0.10, 0.8)),
363 children![(
364 PerformanceText,
365 Text::default(),
366 TextFont {
367 font_size: FontSize::Px(8.0),
368 ..default()
369 },
370 )],
371 ));
372}Sourcepub fn try_attribute_mut(
&mut self,
id: impl Into<MeshVertexAttributeId>,
) -> Result<&mut VertexAttributeValues, MeshAccessError>
pub fn try_attribute_mut( &mut self, id: impl Into<MeshVertexAttributeId>, ) -> Result<&mut VertexAttributeValues, MeshAccessError>
Retrieves the data currently set to the vertex attribute with the specified name mutably.
Returns an error if the mesh data has been extracted to RenderWorldor
if the attribute does not exist.
Sourcepub fn try_attribute_mut_option(
&mut self,
id: impl Into<MeshVertexAttributeId>,
) -> Result<Option<&mut VertexAttributeValues>, MeshAccessError>
pub fn try_attribute_mut_option( &mut self, id: impl Into<MeshVertexAttributeId>, ) -> Result<Option<&mut VertexAttributeValues>, MeshAccessError>
Retrieves the data currently set to the vertex attribute with the specified name mutably.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn attributes(
&self,
) -> impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)>
pub fn attributes( &self, ) -> impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)>
Returns an iterator that yields references to the data of each vertex attribute.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_attributes
Sourcepub fn try_attributes(
&self,
) -> Result<impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)>, MeshAccessError>
pub fn try_attributes( &self, ) -> Result<impl Iterator<Item = (&MeshVertexAttribute, &VertexAttributeValues)>, MeshAccessError>
Returns an iterator that yields references to the data of each vertex attribute.
Returns an error if data has been extracted to RenderWorld
Sourcepub fn attributes_mut(
&mut self,
) -> impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)>
pub fn attributes_mut( &mut self, ) -> impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)>
Returns an iterator that yields mutable references to the data of each vertex attribute.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_attributes_mut
Sourcepub fn try_attributes_mut(
&mut self,
) -> Result<impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)>, MeshAccessError>
pub fn try_attributes_mut( &mut self, ) -> Result<impl Iterator<Item = (&MeshVertexAttribute, &mut VertexAttributeValues)>, MeshAccessError>
Returns an iterator that yields mutable references to the data of each vertex attribute.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn insert_indices(&mut self, indices: Indices)
pub fn insert_indices(&mut self, indices: Indices)
Sets the vertex indices of the mesh. They describe how triangles are constructed out of the
vertex attributes and are therefore only useful for the PrimitiveTopology variants
that use triangles.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_insert_indices
Examples found in repository?
49fn star(
50 mut commands: Commands,
51 // We will add a new Mesh for the star being created
52 mut meshes: ResMut<Assets<Mesh>>,
53) {
54 // Let's define the mesh for the object we want to draw: a nice star.
55 // We will specify here what kind of topology is used to define the mesh,
56 // that is, how triangles are built from the vertices. We will use a
57 // triangle list, meaning that each vertex of the triangle has to be
58 // specified. We set `RenderAssetUsages::RENDER_WORLD`, meaning this mesh
59 // will not be accessible in future frames from the `meshes` resource, in
60 // order to save on memory once it has been uploaded to the GPU.
61 let mut star = Mesh::new(
62 PrimitiveTopology::TriangleList,
63 RenderAssetUsages::RENDER_WORLD,
64 );
65
66 // Vertices need to have a position attribute. We will use the following
67 // vertices (I hope you can spot the star in the schema).
68 //
69 // 1
70 //
71 // 10 2
72 // 9 0 3
73 // 8 4
74 // 6
75 // 7 5
76 //
77 // These vertices are specified in 3D space.
78 let mut v_pos = vec![[0.0, 0.0, 0.0]];
79 for i in 0..10 {
80 // The angle between each vertex is 1/10 of a full rotation.
81 let a = i as f32 * PI / 5.0;
82 // The radius of inner vertices (even indices) is 100. For outer vertices (odd indices) it's 200.
83 let r = (1 - i % 2) as f32 * 100.0 + 100.0;
84 // Add the vertex position.
85 v_pos.push([r * ops::sin(a), r * ops::cos(a), 0.0]);
86 }
87 // Set the position attribute
88 star.insert_attribute(Mesh::ATTRIBUTE_POSITION, v_pos);
89 // And a RGB color attribute as well. A built-in `Mesh::ATTRIBUTE_COLOR` exists, but we
90 // use a custom vertex attribute here for demonstration purposes.
91 let mut v_color: Vec<u32> = vec![LinearRgba::BLACK.as_u32()];
92 v_color.extend_from_slice(&[LinearRgba::from(YELLOW).as_u32(); 10]);
93 star.insert_attribute(
94 MeshVertexAttribute::new("Vertex_Color", 1, VertexFormat::Uint32),
95 v_color,
96 );
97
98 // Now, we specify the indices of the vertex that are going to compose the
99 // triangles in our star. Vertices in triangles have to be specified in CCW
100 // winding (that will be the front face, colored). Since we are using
101 // triangle list, we will specify each triangle as 3 vertices
102 // First triangle: 0, 2, 1
103 // Second triangle: 0, 3, 2
104 // Third triangle: 0, 4, 3
105 // etc
106 // Last triangle: 0, 1, 10
107 let mut indices = vec![0, 1, 10];
108 for i in 2..=10 {
109 indices.extend_from_slice(&[0, i, i - 1]);
110 }
111 star.insert_indices(Indices::U32(indices));
112
113 // We can now spawn the entities for the star and the camera
114 commands.spawn((
115 // We use a marker component to identify the custom colored meshes
116 ColoredMesh2d,
117 // The `Handle<Mesh>` needs to be wrapped in a `Mesh2d` for 2D rendering
118 Mesh2d(meshes.add(star)),
119 ));
120
121 commands.spawn(Camera2d);
122}Sourcepub fn try_insert_indices(
&mut self,
indices: Indices,
) -> Result<(), MeshAccessError>
pub fn try_insert_indices( &mut self, indices: Indices, ) -> Result<(), MeshAccessError>
Sets the vertex indices of the mesh. They describe how triangles are constructed out of the
vertex attributes and are therefore only useful for the PrimitiveTopology variants
that use triangles.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn with_inserted_indices(self, indices: Indices) -> Mesh
pub fn with_inserted_indices(self, indices: Indices) -> Mesh
Consumes the mesh and returns a mesh with the given vertex indices. They describe how triangles
are constructed out of the vertex attributes and are therefore only useful for the
PrimitiveTopology variants that use triangles.
(Alternatively, you can use Mesh::insert_indices to mutate an existing mesh in-place)
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_with_inserted_indices
Examples found in repository?
More examples
54fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
55 // Build a custom triangle mesh with colors
56 // We define a custom mesh because the examples only uses a limited
57 // set of vertex attributes for simplicity
58 let mesh = Mesh::new(
59 PrimitiveTopology::TriangleList,
60 RenderAssetUsages::default(),
61 )
62 .with_inserted_indices(Indices::U32(vec![0, 1, 2]))
63 .with_inserted_attribute(
64 Mesh::ATTRIBUTE_POSITION,
65 vec![
66 vec3(-0.5, -0.5, 0.0),
67 vec3(0.5, -0.5, 0.0),
68 vec3(0.0, 0.25, 0.0),
69 ],
70 )
71 .with_inserted_attribute(
72 Mesh::ATTRIBUTE_COLOR,
73 vec![
74 vec4(1.0, 0.0, 0.0, 1.0),
75 vec4(0.0, 1.0, 0.0, 1.0),
76 vec4(0.0, 0.0, 1.0, 1.0),
77 ],
78 );
79
80 // spawn 3 triangles to show that batching works
81 for (x, y) in [-0.5, 0.0, 0.5].into_iter().zip([-0.25, 0.5, -0.25]) {
82 // Spawn an entity with all the required components for it to be rendered with our custom pipeline
83 commands.spawn((
84 // We use a marker component to identify the mesh that will be rendered
85 // with our specialized pipeline
86 CustomRenderedEntity,
87 // We need to add the mesh handle to the entity
88 Mesh3d(meshes.add(mesh.clone())),
89 Transform::from_xyz(x, y, 0.0),
90 ));
91 }
92
93 // Spawn the camera.
94 commands.spawn((
95 Camera3d::default(),
96 // Move the camera back a bit to see all the triangles
97 Transform::from_xyz(0.0, 0.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
98 ));
99}514 fn build(&self) -> Mesh {
515 let radius = self.heart.radius;
516 // The curved parts of each wing (half) of the heart have an angle of `PI * 1.25` or 225°
517 let wing_angle = PI * 1.25;
518
519 // We create buffers for the vertices, their normals and UVs, as well as the indices used to connect the vertices.
520 let mut vertices = Vec::with_capacity(2 * self.resolution);
521 let mut uvs = Vec::with_capacity(2 * self.resolution);
522 let mut indices = Vec::with_capacity(6 * self.resolution - 9);
523 // Since the heart is flat, we know all the normals are identical already.
524 let normals = vec![[0f32, 0f32, 1f32]; 2 * self.resolution];
525
526 // The point in the middle of the two curved parts of the heart
527 vertices.push([0.0; 3]);
528 uvs.push([0.5, 0.5]);
529
530 // The left wing of the heart, starting from the point in the middle.
531 for i in 1..self.resolution {
532 let angle = (i as f32 / self.resolution as f32) * wing_angle;
533 let (sin, cos) = ops::sin_cos(angle);
534 vertices.push([radius * (cos - 1.0), radius * sin, 0.0]);
535 uvs.push([0.5 - (cos - 1.0) / 4., 0.5 - sin / 2.]);
536 }
537
538 // The bottom tip of the heart
539 vertices.push([0.0, radius * (-1. - SQRT_2), 0.0]);
540 uvs.push([0.5, 1.]);
541
542 // The right wing of the heart, starting from the bottom most point and going towards the middle point.
543 for i in 0..self.resolution - 1 {
544 let angle = (i as f32 / self.resolution as f32) * wing_angle - PI / 4.;
545 let (sin, cos) = ops::sin_cos(angle);
546 vertices.push([radius * (cos + 1.0), radius * sin, 0.0]);
547 uvs.push([0.5 - (cos + 1.0) / 4., 0.5 - sin / 2.]);
548 }
549
550 // This is where we build all the triangles from the points created above.
551 // Each triangle has one corner on the middle point with the other two being adjacent points on the perimeter of the heart.
552 for i in 2..2 * self.resolution as u32 {
553 indices.extend_from_slice(&[i - 1, i, 0]);
554 }
555
556 // Here, the actual `Mesh` is created. We set the indices, vertices, normals and UVs created above and specify the topology of the mesh.
557 Mesh::new(
558 bevy::mesh::PrimitiveTopology::TriangleList,
559 RenderAssetUsages::default(),
560 )
561 .with_inserted_indices(bevy::mesh::Indices::U32(indices))
562 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
563 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
564 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
565 }82fn setup(
83 mut commands: Commands,
84 mut meshes: ResMut<Assets<Mesh>>,
85 mut materials: ResMut<Assets<StandardMaterial>>,
86) {
87 // a truly empty mesh will error if used in Mesh3d
88 // so we set up the data to be what we want the compute shader to output
89 // We're using 36 indices and 24 vertices which is directly taken from
90 // the Bevy Cuboid mesh implementation.
91 //
92 // We allocate 50 spots for each attribute here because
93 // it is *very important* that the amount of data allocated here is
94 // *bigger* than (or exactly equal to) the amount of data we intend to
95 // write from the compute shader. This amount of data defines how big
96 // the buffer we get from the mesh_allocator will be, which in turn
97 // defines how big the buffer is when we're in the compute shader.
98 //
99 // If it turns out you don't need all of the space when the compute shader
100 // is writing data, you can write NaN to the rest of the data.
101 let empty_mesh = {
102 let mut mesh = Mesh::new(
103 PrimitiveTopology::TriangleList,
104 RenderAssetUsages::RENDER_WORLD,
105 )
106 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vec![[0.; 3]; 50])
107 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.; 3]; 50])
108 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.; 2]; 50])
109 .with_inserted_indices(Indices::U32(vec![0; 50]));
110
111 mesh.asset_usage = RenderAssetUsages::RENDER_WORLD;
112 mesh
113 };
114
115 let handle = meshes.add(empty_mesh);
116
117 // we spawn two "users" of the mesh handle,
118 // but only insert `GenerateMesh` on one of them
119 // to show that the mesh handle works as usual
120 commands.spawn((
121 GenerateMesh(handle.clone()),
122 Mesh3d(handle.clone()),
123 MeshMaterial3d(materials.add(StandardMaterial {
124 base_color: RED_400.into(),
125 ..default()
126 })),
127 Transform::from_xyz(-2.5, 1.5, 0.),
128 ));
129
130 commands.spawn((
131 Mesh3d(handle),
132 MeshMaterial3d(materials.add(StandardMaterial {
133 base_color: SKY_400.into(),
134 ..default()
135 })),
136 Transform::from_xyz(2.5, 1.5, 0.),
137 ));
138
139 // some additional scene elements.
140 // This mesh specifically is here so that we don't assume
141 // mesh_allocator offsets that would only work if we had
142 // one mesh in the scene.
143 commands.spawn((
144 Mesh3d(meshes.add(Circle::new(4.0))),
145 MeshMaterial3d(materials.add(Color::WHITE)),
146 Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
147 ));
148 commands.spawn((
149 PointLight {
150 shadow_maps_enabled: true,
151 ..default()
152 },
153 Transform::from_xyz(4.0, 8.0, 4.0),
154 ));
155 // camera
156 commands.spawn((
157 Camera3d::default(),
158 Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
159 ));
160}92fn setup_meshes(
93 mut commands: Commands,
94 mut mesh_assets: ResMut<Assets<Mesh>>,
95 mut material_assets: ResMut<Assets<StandardMaterial>>,
96 mut inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
97) {
98 // Create a mesh with two rectangles.
99 let unskinned_mesh = Mesh::new(
100 PrimitiveTopology::TriangleList,
101 RenderAssetUsages::default(),
102 )
103 .with_inserted_attribute(
104 Mesh::ATTRIBUTE_POSITION,
105 vec![
106 [-0.3, -0.3, 0.0],
107 [0.3, -0.3, 0.0],
108 [-0.3, 0.3, 0.0],
109 [0.3, 0.3, 0.0],
110 [-0.4, 0.8, 0.0],
111 [0.4, 0.8, 0.0],
112 [-0.4, 1.8, 0.0],
113 [0.4, 1.8, 0.0],
114 ],
115 )
116 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 8])
117 .with_inserted_indices(Indices::U16(vec![0, 1, 3, 0, 3, 2, 4, 5, 7, 4, 7, 6]));
118
119 // Copy the mesh and add skinning attributes that bind each rectangle to a joint.
120 let skinned_mesh = unskinned_mesh
121 .clone()
122 .with_inserted_attribute(
123 Mesh::ATTRIBUTE_JOINT_INDEX,
124 VertexAttributeValues::Uint16x4(vec![
125 [0, 0, 0, 0],
126 [0, 0, 0, 0],
127 [0, 0, 0, 0],
128 [0, 0, 0, 0],
129 [1, 0, 0, 0],
130 [1, 0, 0, 0],
131 [1, 0, 0, 0],
132 [1, 0, 0, 0],
133 ]),
134 )
135 .with_inserted_attribute(
136 Mesh::ATTRIBUTE_JOINT_WEIGHT,
137 vec![[1.00, 0.00, 0.0, 0.0]; 8],
138 );
139
140 let unskinned_mesh_handle = mesh_assets.add(unskinned_mesh);
141 let skinned_mesh_handle = mesh_assets.add(skinned_mesh);
142
143 let inverse_bindposes_handle = inverse_bindposes_assets.add(vec![
144 Mat4::IDENTITY,
145 Mat4::from_translation(Vec3::new(0.0, -1.3, 0.0)),
146 ]);
147
148 let mesh_material_handle = material_assets.add(StandardMaterial::default());
149
150 let background_material_handle = material_assets.add(StandardMaterial {
151 base_color: Color::srgb(0.05, 0.15, 0.05),
152 reflectance: 0.2,
153 ..default()
154 });
155
156 #[derive(PartialEq)]
157 enum Variation {
158 Normal,
159 MissingMeshAttributes,
160 MissingJointEntity,
161 MissingSkinnedMeshComponent,
162 }
163
164 for (index, variation) in [
165 Variation::Normal,
166 Variation::MissingMeshAttributes,
167 Variation::MissingJointEntity,
168 Variation::MissingSkinnedMeshComponent,
169 ]
170 .into_iter()
171 .enumerate()
172 {
173 // Skip variations that are currently broken. See https://github.com/bevyengine/bevy/issues/16929,
174 // https://github.com/bevyengine/bevy/pull/18074.
175 if (variation == Variation::MissingSkinnedMeshComponent)
176 || (variation == Variation::MissingMeshAttributes)
177 {
178 continue;
179 }
180
181 let transform = Transform::from_xyz(((index as f32) - 1.5) * 4.5, 0.0, 0.0);
182
183 let joint_0 = commands.spawn(transform).id();
184
185 let joint_1 = commands
186 .spawn((ChildOf(joint_0), AnimatedJoint, Transform::IDENTITY))
187 .id();
188
189 if variation == Variation::MissingJointEntity {
190 commands.entity(joint_1).despawn();
191 }
192
193 let mesh_handle = match variation {
194 Variation::MissingMeshAttributes => &unskinned_mesh_handle,
195 _ => &skinned_mesh_handle,
196 };
197
198 let mut entity_commands = commands.spawn((
199 Mesh3d(mesh_handle.clone()),
200 MeshMaterial3d(mesh_material_handle.clone()),
201 transform,
202 ));
203
204 if variation != Variation::MissingSkinnedMeshComponent {
205 entity_commands.insert(SkinnedMesh {
206 inverse_bindposes: inverse_bindposes_handle.clone(),
207 joints: vec![joint_0, joint_1],
208 });
209 }
210
211 // Add a square behind the mesh to distinguish it from the other meshes.
212 commands.spawn((
213 Transform::from_xyz(transform.translation.x, transform.translation.y, -0.8),
214 Mesh3d(mesh_assets.add(Plane3d::default().mesh().size(4.3, 4.3).normal(Dir3::Z))),
215 MeshMaterial3d(background_material_handle.clone()),
216 ));
217 }
218}38fn setup(
39 mut commands: Commands,
40 asset_server: Res<AssetServer>,
41 mut meshes: ResMut<Assets<Mesh>>,
42 mut materials: ResMut<Assets<StandardMaterial>>,
43 mut skinned_mesh_inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
44) {
45 // Create a camera
46 commands.spawn((
47 Camera3d::default(),
48 Transform::from_xyz(2.5, 2.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
49 ));
50
51 // Create inverse bindpose matrices for a skeleton consists of 2 joints
52 let inverse_bindposes = skinned_mesh_inverse_bindposes_assets.add(vec![
53 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
54 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
55 ]);
56
57 // Create a mesh
58 let mesh = Mesh::new(
59 PrimitiveTopology::TriangleList,
60 RenderAssetUsages::RENDER_WORLD,
61 )
62 // Set mesh vertex positions
63 .with_inserted_attribute(
64 Mesh::ATTRIBUTE_POSITION,
65 vec![
66 [0.0, 0.0, 0.0],
67 [1.0, 0.0, 0.0],
68 [0.0, 0.5, 0.0],
69 [1.0, 0.5, 0.0],
70 [0.0, 1.0, 0.0],
71 [1.0, 1.0, 0.0],
72 [0.0, 1.5, 0.0],
73 [1.0, 1.5, 0.0],
74 [0.0, 2.0, 0.0],
75 [1.0, 2.0, 0.0],
76 ],
77 )
78 // Add UV coordinates that map the left half of the texture since its a 1 x
79 // 2 rectangle.
80 .with_inserted_attribute(
81 Mesh::ATTRIBUTE_UV_0,
82 vec![
83 [0.0, 0.00],
84 [0.5, 0.00],
85 [0.0, 0.25],
86 [0.5, 0.25],
87 [0.0, 0.50],
88 [0.5, 0.50],
89 [0.0, 0.75],
90 [0.5, 0.75],
91 [0.0, 1.00],
92 [0.5, 1.00],
93 ],
94 )
95 // Set mesh vertex normals
96 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10])
97 // Set mesh vertex joint indices for mesh skinning.
98 // Each vertex gets 4 indices used to address the `JointTransforms` array in the vertex shader
99 // as well as `SkinnedMeshJoint` array in the `SkinnedMesh` component.
100 // This means that a maximum of 4 joints can affect a single vertex.
101 .with_inserted_attribute(
102 Mesh::ATTRIBUTE_JOINT_INDEX,
103 // Need to be explicit here as [u16; 4] could be either Uint16x4 or Unorm16x4.
104 VertexAttributeValues::Uint16x4(vec![
105 [0, 0, 0, 0],
106 [0, 0, 0, 0],
107 [0, 1, 0, 0],
108 [0, 1, 0, 0],
109 [0, 1, 0, 0],
110 [0, 1, 0, 0],
111 [0, 1, 0, 0],
112 [0, 1, 0, 0],
113 [0, 1, 0, 0],
114 [0, 1, 0, 0],
115 ]),
116 )
117 // Set mesh vertex joint weights for mesh skinning.
118 // Each vertex gets 4 joint weights corresponding to the 4 joint indices assigned to it.
119 // The sum of these weights should equal to 1.
120 .with_inserted_attribute(
121 Mesh::ATTRIBUTE_JOINT_WEIGHT,
122 vec![
123 [1.00, 0.00, 0.0, 0.0],
124 [1.00, 0.00, 0.0, 0.0],
125 [0.75, 0.25, 0.0, 0.0],
126 [0.75, 0.25, 0.0, 0.0],
127 [0.50, 0.50, 0.0, 0.0],
128 [0.50, 0.50, 0.0, 0.0],
129 [0.25, 0.75, 0.0, 0.0],
130 [0.25, 0.75, 0.0, 0.0],
131 [0.00, 1.00, 0.0, 0.0],
132 [0.00, 1.00, 0.0, 0.0],
133 ],
134 )
135 // Tell bevy to construct triangles from a list of vertex indices,
136 // where each 3 vertex indices form a triangle.
137 .with_inserted_indices(Indices::U16(vec![
138 0, 1, 3, 0, 3, 2, 2, 3, 5, 2, 5, 4, 4, 5, 7, 4, 7, 6, 6, 7, 9, 6, 9, 8,
139 ]))
140 // Create skinned mesh bounds. Together with the `DynamicSkinnedMeshBounds`
141 // component, this will ensure the mesh is correctly frustum culled.
142 .with_generated_skinned_mesh_bounds()
143 .unwrap();
144
145 let mesh = meshes.add(mesh);
146
147 // We're seeding the PRNG here to make this example deterministic for testing purposes.
148 // This isn't strictly required in practical use unless you need your app to be deterministic.
149 let mut rng = ChaCha8Rng::seed_from_u64(42);
150
151 for i in -5..5 {
152 // Create joint entities
153 let joint_0 = commands
154 .spawn(Transform::from_xyz(
155 i as f32 * 1.5,
156 0.0,
157 // Move quads back a small amount to avoid Z-fighting and not
158 // obscure the transform gizmos.
159 -(i as f32 * 0.01).abs(),
160 ))
161 .id();
162 let joint_1 = commands.spawn((AnimatedJoint(i), Transform::IDENTITY)).id();
163
164 // Set joint_1 as a child of joint_0.
165 commands.entity(joint_0).add_children(&[joint_1]);
166
167 // Each joint in this vector corresponds to each inverse bindpose matrix in `SkinnedMeshInverseBindposes`.
168 let joint_entities = vec![joint_0, joint_1];
169
170 // Create skinned mesh renderer. Note that its transform doesn't affect the position of the mesh.
171 commands.spawn((
172 Mesh3d(mesh.clone()),
173 MeshMaterial3d(materials.add(StandardMaterial {
174 base_color: Color::srgb(
175 rng.random_range(0.0..1.0),
176 rng.random_range(0.0..1.0),
177 rng.random_range(0.0..1.0),
178 ),
179 base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
180 ..default()
181 })),
182 SkinnedMesh {
183 inverse_bindposes: inverse_bindposes.clone(),
184 joints: joint_entities,
185 },
186 DynamicSkinnedMeshBounds,
187 ));
188 }
189}Sourcepub fn try_with_inserted_indices(
self,
indices: Indices,
) -> Result<Mesh, MeshAccessError>
pub fn try_with_inserted_indices( self, indices: Indices, ) -> Result<Mesh, MeshAccessError>
Consumes the mesh and returns a mesh with the given vertex indices. They describe how triangles
are constructed out of the vertex attributes and are therefore only useful for the
PrimitiveTopology variants that use triangles.
(Alternatively, you can use Mesh::try_insert_indices to mutate an existing mesh in-place)
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn indices(&self) -> Option<&Indices>
pub fn indices(&self) -> Option<&Indices>
Retrieves the vertex indices of the mesh, returns None if not found.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_indices
Sourcepub fn try_indices(&self) -> Result<&Indices, MeshAccessError>
pub fn try_indices(&self) -> Result<&Indices, MeshAccessError>
Retrieves the vertex indices of the mesh.
Returns an error if the mesh data has been extracted to RenderWorldor
if the attribute does not exist.
Sourcepub fn try_indices_option(&self) -> Result<Option<&Indices>, MeshAccessError>
pub fn try_indices_option(&self) -> Result<Option<&Indices>, MeshAccessError>
Retrieves the vertex indices of the mesh, returns None if not found.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn indices_mut(&mut self) -> Option<&mut Indices>
pub fn indices_mut(&mut self) -> Option<&mut Indices>
Retrieves the vertex indices of the mesh mutably.
Examples found in repository?
374fn add_raytracing_meshes_on_scene_load(
375 scene_ready: On<WorldInstanceReady>,
376 children: Query<&Children>,
377 mesh_query: Query<(
378 &Mesh3d,
379 &MeshMaterial3d<StandardMaterial>,
380 Option<&GltfMaterialName>,
381 )>,
382 mut meshes: ResMut<Assets<Mesh>>,
383 mut materials: ResMut<Assets<StandardMaterial>>,
384 mut commands: Commands,
385 args: Res<Args>,
386) {
387 for descendant in children.iter_descendants(scene_ready.entity) {
388 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
389 mesh_query.get(descendant)
390 {
391 // Add raytracing mesh component
392 commands
393 .entity(descendant)
394 .insert(RaytracingMesh3d(mesh_handle.clone()));
395
396 // Ensure meshes are Solari compatible
397 let mut mesh = meshes.get_mut(mesh_handle).unwrap();
398 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
399 let vertex_count = mesh.count_vertices();
400 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
401 mesh.insert_attribute(
402 Mesh::ATTRIBUTE_TANGENT,
403 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
404 );
405 }
406 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
407 mesh.generate_tangents().unwrap();
408 }
409 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
410 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
411 }
412 if let Some(indices) = mesh.indices_mut()
413 && let Indices::U16(_) = indices
414 {
415 *indices = Indices::U32(indices.iter().map(|i| i as u32).collect());
416 }
417
418 // Prevent rasterization if using pathtracer
419 if args.pathtracer == Some(true) {
420 commands.entity(descendant).remove::<Mesh3d>();
421 }
422
423 // Adjust scene materials to better demo Solari features
424 if material_name.map(|s| s.0.as_str()) == Some("material") {
425 let mut material = materials.get_mut(material_handle).unwrap();
426 material.emissive = LinearRgba::BLACK;
427 }
428 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
429 let mut material = materials.get_mut(material_handle).unwrap();
430 material.emissive =
431 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
432 material.alpha_mode = AlphaMode::Opaque;
433 material.specular_transmission = 0.0;
434
435 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
436 }
437 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
438 let mut material = materials.get_mut(material_handle).unwrap();
439 material.alpha_mode = AlphaMode::Opaque;
440 material.specular_transmission = 0.0;
441 }
442 }
443 }
444}Sourcepub fn try_indices_mut(&mut self) -> Result<&mut Indices, MeshAccessError>
pub fn try_indices_mut(&mut self) -> Result<&mut Indices, MeshAccessError>
Retrieves the vertex indices of the mesh mutably.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn try_indices_mut_option(
&mut self,
) -> Result<Option<&mut Indices>, MeshAccessError>
pub fn try_indices_mut_option( &mut self, ) -> Result<Option<&mut Indices>, MeshAccessError>
Retrieves the vertex indices of the mesh mutably.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn remove_indices(&mut self) -> Option<Indices>
pub fn remove_indices(&mut self) -> Option<Indices>
Removes the vertex indices from the mesh and returns them.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_remove_indices
Sourcepub fn try_remove_indices(&mut self) -> Result<Option<Indices>, MeshAccessError>
pub fn try_remove_indices(&mut self) -> Result<Option<Indices>, MeshAccessError>
Removes the vertex indices from the mesh and returns them.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn with_removed_indices(self) -> Mesh
pub fn with_removed_indices(self) -> Mesh
Consumes the mesh and returns a mesh without the vertex indices of the mesh.
(Alternatively, you can use Mesh::remove_indices to mutate an existing mesh in-place)
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_with_removed_indices
Sourcepub fn try_with_removed_indices(self) -> Result<Mesh, MeshAccessError>
pub fn try_with_removed_indices(self) -> Result<Mesh, MeshAccessError>
Consumes the mesh and returns a mesh without the vertex indices of the mesh.
(Alternatively, you can use Mesh::try_remove_indices to mutate an existing mesh in-place)
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn get_vertex_size(&self) -> u64
pub fn get_vertex_size(&self) -> u64
Returns the size of a vertex in bytes.
§Panics
Panics when the mesh data has already been extracted to RenderWorld.
Sourcepub fn get_vertex_buffer_size(&self) -> usize
pub fn get_vertex_buffer_size(&self) -> usize
Returns the size required for the vertex buffer in bytes.
§Panics
Panics when the mesh data has already been extracted to RenderWorld.
Sourcepub fn get_index_buffer_bytes(&self) -> Option<&[u8]>
pub fn get_index_buffer_bytes(&self) -> Option<&[u8]>
Computes and returns the index data of the mesh as bytes. This is used to transform the index data into a GPU friendly format.
§Panics
Panics when the mesh data has already been extracted to RenderWorld.
Sourcepub fn get_morph_targets(&self) -> Option<&[MorphAttributes]>
Available on crate feature morph only.
pub fn get_morph_targets(&self) -> Option<&[MorphAttributes]>
morph only.If any morph displacements are present, returns them as a
MorphAttributes array.
§Panics
Panics when the mesh data has already been extracted to the render world.
Sourcepub fn get_mesh_vertex_buffer_layout(
&self,
mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,
) -> MeshVertexBufferLayoutRef
pub fn get_mesh_vertex_buffer_layout( &self, mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts, ) -> MeshVertexBufferLayoutRef
Get this Mesh’s MeshVertexBufferLayout, used in SpecializedMeshPipeline.
§Panics
Panics when the mesh data has already been extracted to RenderWorld.
Sourcepub fn count_vertices(&self) -> usize
pub fn count_vertices(&self) -> usize
Counts all vertices of the mesh.
If the attributes have different vertex counts, the smallest is returned.
§Panics
Panics when the mesh data has already been extracted to RenderWorld.
Examples found in repository?
374fn add_raytracing_meshes_on_scene_load(
375 scene_ready: On<WorldInstanceReady>,
376 children: Query<&Children>,
377 mesh_query: Query<(
378 &Mesh3d,
379 &MeshMaterial3d<StandardMaterial>,
380 Option<&GltfMaterialName>,
381 )>,
382 mut meshes: ResMut<Assets<Mesh>>,
383 mut materials: ResMut<Assets<StandardMaterial>>,
384 mut commands: Commands,
385 args: Res<Args>,
386) {
387 for descendant in children.iter_descendants(scene_ready.entity) {
388 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
389 mesh_query.get(descendant)
390 {
391 // Add raytracing mesh component
392 commands
393 .entity(descendant)
394 .insert(RaytracingMesh3d(mesh_handle.clone()));
395
396 // Ensure meshes are Solari compatible
397 let mut mesh = meshes.get_mut(mesh_handle).unwrap();
398 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
399 let vertex_count = mesh.count_vertices();
400 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
401 mesh.insert_attribute(
402 Mesh::ATTRIBUTE_TANGENT,
403 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
404 );
405 }
406 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
407 mesh.generate_tangents().unwrap();
408 }
409 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
410 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
411 }
412 if let Some(indices) = mesh.indices_mut()
413 && let Indices::U16(_) = indices
414 {
415 *indices = Indices::U32(indices.iter().map(|i| i as u32).collect());
416 }
417
418 // Prevent rasterization if using pathtracer
419 if args.pathtracer == Some(true) {
420 commands.entity(descendant).remove::<Mesh3d>();
421 }
422
423 // Adjust scene materials to better demo Solari features
424 if material_name.map(|s| s.0.as_str()) == Some("material") {
425 let mut material = materials.get_mut(material_handle).unwrap();
426 material.emissive = LinearRgba::BLACK;
427 }
428 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
429 let mut material = materials.get_mut(material_handle).unwrap();
430 material.emissive =
431 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
432 material.alpha_mode = AlphaMode::Opaque;
433 material.specular_transmission = 0.0;
434
435 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
436 }
437 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
438 let mut material = materials.get_mut(material_handle).unwrap();
439 material.alpha_mode = AlphaMode::Opaque;
440 material.specular_transmission = 0.0;
441 }
442 }
443 }
444}Sourcepub fn create_packed_vertex_buffer_data(&self) -> Vec<u8> ⓘ
pub fn create_packed_vertex_buffer_data(&self) -> Vec<u8> ⓘ
Computes and returns the vertex data of the mesh as bytes.
Therefore the attributes are located in the order of their MeshVertexAttribute::id.
This is used to transform the vertex data into a GPU friendly format.
If the vertex attributes have different lengths, they are all truncated to the length of the smallest.
This is a convenience method which allocates a Vec.
Prefer pre-allocating and using Mesh::write_packed_vertex_buffer_data when possible.
§Panics
Panics when the mesh data has already been extracted to RenderWorld.
Sourcepub fn write_packed_vertex_buffer_data(&self, slice: WriteOnly<'_, [u8]>)
pub fn write_packed_vertex_buffer_data(&self, slice: WriteOnly<'_, [u8]>)
Computes and write the vertex data of the mesh into a mutable byte slice.
The attributes are located in the order of their MeshVertexAttribute::id.
This is used to transform the vertex data into a GPU friendly format.
If the vertex attributes have different lengths, they are all truncated to the length of the smallest.
§Panics
Panics when the mesh data has already been extracted to RenderWorld.
Sourcepub fn duplicate_vertices(&mut self)
pub fn duplicate_vertices(&mut self)
Duplicates the vertex attributes so that no vertices are shared.
This can dramatically increase the vertex count, so make sure this is what you want. Does nothing if no Indices are set.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_duplicate_vertices
Sourcepub fn try_duplicate_vertices(&mut self) -> Result<(), MeshAccessError>
pub fn try_duplicate_vertices(&mut self) -> Result<(), MeshAccessError>
Duplicates the vertex attributes so that no vertices are shared.
This can dramatically increase the vertex count, so make sure this is what you want. Does nothing if no Indices are set.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn with_duplicated_vertices(self) -> Mesh
pub fn with_duplicated_vertices(self) -> Mesh
Consumes the mesh and returns a mesh with no shared vertices.
This can dramatically increase the vertex count, so make sure this is what you want.
Does nothing if no Indices are set.
(Alternatively, you can use Mesh::duplicate_vertices to mutate an existing mesh in-place)
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_with_duplicated_vertices
Examples found in repository?
289fn spawn_reflective_prism(
290 commands: &mut Commands,
291 meshes: &mut Assets<Mesh>,
292 material: Handle<StandardMaterial>,
293) {
294 // Create a mesh.
295 let cube = meshes.add(
296 Cuboid {
297 half_size: vec3(2.0, 1.0, 10.0),
298 }
299 .mesh()
300 .build()
301 // We use flat normals so that the surface appears flat, not curved.
302 .with_duplicated_vertices()
303 .with_computed_flat_normals(),
304 );
305
306 // Spawn the cube.
307 commands.spawn((
308 Mesh3d(cube),
309 MeshMaterial3d(material),
310 Transform::from_xyz(0.0, -4.0, -5.5),
311 ReflectivePrism,
312 Visibility::Hidden,
313 ));
314}More examples
104fn spawn_inner_cube(
105 commands: &mut Commands,
106 meshes: &mut Assets<Mesh>,
107 materials: &mut Assets<StandardMaterial>,
108) {
109 let cube_mesh = meshes.add(
110 Cuboid {
111 half_size: Vec3::new(5.0, 1.0, 2.0),
112 }
113 .mesh()
114 .build()
115 .with_duplicated_vertices()
116 .with_computed_flat_normals(),
117 );
118 let cube_material = materials.add(StandardMaterial {
119 base_color: Color::WHITE,
120 metallic: 1.0,
121 reflectance: 1.0,
122 perceptual_roughness: 0.0,
123 ..default()
124 });
125
126 commands.spawn((
127 Mesh3d(cube_mesh),
128 MeshMaterial3d(cube_material),
129 Transform::from_xyz(0.0, -4.0, -2.5),
130 InnerCube,
131 ));
132}184fn spawn_plane_mesh(
185 commands: &mut Commands,
186 asset_server: &AssetServer,
187 meshes: &mut Assets<Mesh>,
188 materials: &mut Assets<StandardMaterial>,
189) {
190 // Create a plane onto which we project decals.
191 //
192 // As the plane has a normal map, we must generate tangents for the
193 // vertices.
194 let plane_mesh = meshes.add(
195 Plane3d {
196 normal: Dir3::NEG_Z,
197 half_size: Vec2::splat(PLANE_HALF_SIZE),
198 }
199 .mesh()
200 .build()
201 .with_duplicated_vertices()
202 .with_computed_flat_normals()
203 .with_generated_tangents()
204 .unwrap(),
205 );
206
207 // Give the plane some texture.
208 //
209 // Note that, as this is a normal map, we must disable sRGB when loading.
210 let normal_map_texture = asset_server
211 .load_builder()
212 .with_settings(|settings: &mut ImageLoaderSettings| settings.is_srgb = false)
213 .load("textures/ScratchedGold-Normal.png");
214
215 // Actually spawn the plane.
216 commands.spawn((
217 Mesh3d(plane_mesh),
218 MeshMaterial3d(materials.add(StandardMaterial {
219 base_color: Color::from(CRIMSON),
220 normal_map_texture: Some(normal_map_texture),
221 ..StandardMaterial::default()
222 })),
223 Transform::IDENTITY,
224 ));
225}Sourcepub fn try_with_duplicated_vertices(self) -> Result<Mesh, MeshAccessError>
pub fn try_with_duplicated_vertices(self) -> Result<Mesh, MeshAccessError>
Consumes the mesh and returns a mesh with no shared vertices.
This can dramatically increase the vertex count, so make sure this is what you want.
Does nothing if no Indices are set.
(Alternatively, you can use Mesh::try_duplicate_vertices to mutate an existing mesh in-place)
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn merge_duplicate_vertices(
&mut self,
) -> Result<(), MeshMergeDuplicateVerticesError>
pub fn merge_duplicate_vertices( &mut self, ) -> Result<(), MeshMergeDuplicateVerticesError>
Remove duplicate vertices and create the index pointing to the unique vertices.
Returns an error if the mesh data has been extracted to RenderWorld.
Returns an error if the mesh already has Indices set, even if there
are duplicate vertices. If deduplication is needed with indices already set,
consider calling Mesh::duplicate_vertices and then this function.
Sourcepub fn with_merge_duplicate_vertices(
self,
) -> Result<Mesh, MeshMergeDuplicateVerticesError>
pub fn with_merge_duplicate_vertices( self, ) -> Result<Mesh, MeshMergeDuplicateVerticesError>
Consumes the mesh and returns a mesh with merged vertices.
(Alternatively, you can use Mesh::merge_duplicate_vertices to mutate an existing mesh in-place)
Returns an error if the mesh data has been extracted to RenderWorld.
Returns an error if the mesh already has Indices set, even if there
are duplicate vertices. If deduplication is needed with indices already set,
consider calling Mesh::duplicate_vertices and then this function.
Sourcepub fn invert_winding(&mut self) -> Result<(), MeshWindingInvertError>
pub fn invert_winding(&mut self) -> Result<(), MeshWindingInvertError>
Sourcepub fn with_inverted_winding(self) -> Result<Mesh, MeshWindingInvertError>
pub fn with_inverted_winding(self) -> Result<Mesh, MeshWindingInvertError>
Consumes the mesh and returns a mesh with inverted winding of the indices such that all counter-clockwise triangles are now clockwise and vice versa.
Does nothing if no Indices are set.
Sourcepub fn compute_normals(&mut self)
pub fn compute_normals(&mut self)
Calculates the Mesh::ATTRIBUTE_NORMAL of a mesh.
If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
normals.
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.=
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_compute_normals
Sourcepub fn try_compute_normals(&mut self) -> Result<(), MeshAccessError>
pub fn try_compute_normals(&mut self) -> Result<(), MeshAccessError>
Calculates the Mesh::ATTRIBUTE_NORMAL of a mesh.
If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
normals.
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.=
Sourcepub fn compute_flat_normals(&mut self)
pub fn compute_flat_normals(&mut self)
Calculates the Mesh::ATTRIBUTE_NORMAL of a mesh.
§Panics
Panics if Indices are set or Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Consider calling Mesh::duplicate_vertices or exporting your mesh with normal
attributes.
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_compute_flat_normals
FIXME: This should handle more cases since this is called as a part of gltf mesh loading where we can’t really blame users for loading meshes that might not conform to the limitations here!
Sourcepub fn try_compute_flat_normals(&mut self) -> Result<(), MeshAccessError>
pub fn try_compute_flat_normals(&mut self) -> Result<(), MeshAccessError>
Calculates the Mesh::ATTRIBUTE_NORMAL of a mesh.
§Panics
Panics if Indices are set or Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Consider calling Mesh::duplicate_vertices or exporting your mesh with normal
attributes.
FIXME: This should handle more cases since this is called as a part of gltf mesh loading where we can’t really blame users for loading meshes that might not conform to the limitations here!
Sourcepub fn compute_smooth_normals(&mut self)
pub fn compute_smooth_normals(&mut self)
Calculates the Mesh::ATTRIBUTE_NORMAL of an indexed mesh, smoothing normals for shared
vertices.
This method weights normals by the angles of the corners of connected triangles, thus
eliminating triangle area and count as factors in the final normal. This does make it
somewhat slower than Mesh::compute_area_weighted_normals which does not need to
greedily normalize each triangle’s normal or calculate corner angles.
If you would rather have the computed normals be weighted by triangle area, see
Mesh::compute_area_weighted_normals instead. If you need to weight them in some other
way, see Mesh::compute_custom_smooth_normals.
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh does not have indices defined.
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_compute_smooth_normals
Sourcepub fn try_compute_smooth_normals(&mut self) -> Result<(), MeshAccessError>
pub fn try_compute_smooth_normals(&mut self) -> Result<(), MeshAccessError>
Calculates the Mesh::ATTRIBUTE_NORMAL of an indexed mesh, smoothing normals for shared
vertices.
This method weights normals by the angles of the corners of connected triangles, thus
eliminating triangle area and count as factors in the final normal. This does make it
somewhat slower than Mesh::compute_area_weighted_normals which does not need to
greedily normalize each triangle’s normal or calculate corner angles.
If you would rather have the computed normals be weighted by triangle area, see
Mesh::compute_area_weighted_normals instead. If you need to weight them in some other
way, see Mesh::compute_custom_smooth_normals.
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh does not have indices defined.
Sourcepub fn compute_area_weighted_normals(&mut self)
pub fn compute_area_weighted_normals(&mut self)
Calculates the Mesh::ATTRIBUTE_NORMAL of an indexed mesh, smoothing normals for shared
vertices.
This method weights normals by the area of each triangle containing the vertex. Thus, larger triangles will skew the normals of their vertices towards their own normal more than smaller triangles will.
This method is actually somewhat faster than Mesh::compute_smooth_normals because an
intermediate result of triangle normal calculation is already scaled by the triangle’s area.
If you would rather have the computed normals be influenced only by the angles of connected
edges, see Mesh::compute_smooth_normals instead. If you need to weight them in some
other way, see Mesh::compute_custom_smooth_normals.
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh does not have indices defined.
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_compute_area_weighted_normals
Sourcepub fn try_compute_area_weighted_normals(
&mut self,
) -> Result<(), MeshAccessError>
pub fn try_compute_area_weighted_normals( &mut self, ) -> Result<(), MeshAccessError>
Calculates the Mesh::ATTRIBUTE_NORMAL of an indexed mesh, smoothing normals for shared
vertices.
This method weights normals by the area of each triangle containing the vertex. Thus, larger triangles will skew the normals of their vertices towards their own normal more than smaller triangles will.
This method is actually somewhat faster than Mesh::compute_smooth_normals because an
intermediate result of triangle normal calculation is already scaled by the triangle’s area.
If you would rather have the computed normals be influenced only by the angles of connected
edges, see Mesh::compute_smooth_normals instead. If you need to weight them in some
other way, see Mesh::compute_custom_smooth_normals.
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh does not have indices defined.
Sourcepub fn compute_custom_smooth_normals(
&mut self,
per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]),
)
pub fn compute_custom_smooth_normals( &mut self, per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]), )
Calculates the Mesh::ATTRIBUTE_NORMAL of an indexed mesh, smoothing normals for shared
vertices.
This method allows you to customize how normals are weighted via the per_triangle parameter,
which must be a function or closure that accepts 3 parameters:
- The indices of the three vertices of the triangle as a
[usize; 3]. - A reference to the values of the
Mesh::ATTRIBUTE_POSITIONof the mesh (&[[f32; 3]]). - A mutable reference to the sums of all normals so far.
See also the standard methods included in Bevy for calculating smooth normals:
An example that would weight each connected triangle’s normal equally, thus skewing normals towards the planes divided into the most triangles:
mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| {
let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c]));
for idx in [a, b, c] {
normals[idx] += normal;
}
});§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh does not have indices defined.
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_compute_custom_smooth_normals
Sourcepub fn try_compute_custom_smooth_normals(
&mut self,
per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]),
) -> Result<(), MeshAccessError>
pub fn try_compute_custom_smooth_normals( &mut self, per_triangle: impl FnMut([usize; 3], &[[f32; 3]], &mut [Vec3]), ) -> Result<(), MeshAccessError>
Calculates the Mesh::ATTRIBUTE_NORMAL of an indexed mesh, smoothing normals for shared
vertices.
This method allows you to customize how normals are weighted via the per_triangle parameter,
which must be a function or closure that accepts 3 parameters:
- The indices of the three vertices of the triangle as a
[usize; 3]. - A reference to the values of the
Mesh::ATTRIBUTE_POSITIONof the mesh (&[[f32; 3]]). - A mutable reference to the sums of all normals so far.
See also the standard methods included in Bevy for calculating smooth normals:
An example that would weight each connected triangle’s normal equally, thus skewing normals towards the planes divided into the most triangles:
mesh.compute_custom_smooth_normals(|[a, b, c], positions, normals| {
let normal = Vec3::from(bevy_mesh::triangle_normal(positions[a], positions[b], positions[c]));
for idx in [a, b, c] {
normals[idx] += normal;
}
});§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh does not have indices defined.
Sourcepub fn with_computed_normals(self) -> Mesh
pub fn with_computed_normals(self) -> Mesh
Consumes the mesh and returns a mesh with calculated Mesh::ATTRIBUTE_NORMAL.
If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
normals.
(Alternatively, you can use Mesh::compute_normals to mutate an existing mesh in-place)
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_with_computed_normals
Sourcepub fn try_with_computed_normals(self) -> Result<Mesh, MeshAccessError>
pub fn try_with_computed_normals(self) -> Result<Mesh, MeshAccessError>
Consumes the mesh and returns a mesh with calculated Mesh::ATTRIBUTE_NORMAL.
If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
normals.
(Alternatively, you can use Mesh::compute_normals to mutate an existing mesh in-place)
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Sourcepub fn with_computed_flat_normals(self) -> Mesh
pub fn with_computed_flat_normals(self) -> Mesh
Consumes the mesh and returns a mesh with calculated Mesh::ATTRIBUTE_NORMAL.
(Alternatively, you can use Mesh::compute_flat_normals to mutate an existing mesh in-place)
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh has indices defined
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_with_computed_flat_normals
Examples found in repository?
289fn spawn_reflective_prism(
290 commands: &mut Commands,
291 meshes: &mut Assets<Mesh>,
292 material: Handle<StandardMaterial>,
293) {
294 // Create a mesh.
295 let cube = meshes.add(
296 Cuboid {
297 half_size: vec3(2.0, 1.0, 10.0),
298 }
299 .mesh()
300 .build()
301 // We use flat normals so that the surface appears flat, not curved.
302 .with_duplicated_vertices()
303 .with_computed_flat_normals(),
304 );
305
306 // Spawn the cube.
307 commands.spawn((
308 Mesh3d(cube),
309 MeshMaterial3d(material),
310 Transform::from_xyz(0.0, -4.0, -5.5),
311 ReflectivePrism,
312 Visibility::Hidden,
313 ));
314}More examples
104fn spawn_inner_cube(
105 commands: &mut Commands,
106 meshes: &mut Assets<Mesh>,
107 materials: &mut Assets<StandardMaterial>,
108) {
109 let cube_mesh = meshes.add(
110 Cuboid {
111 half_size: Vec3::new(5.0, 1.0, 2.0),
112 }
113 .mesh()
114 .build()
115 .with_duplicated_vertices()
116 .with_computed_flat_normals(),
117 );
118 let cube_material = materials.add(StandardMaterial {
119 base_color: Color::WHITE,
120 metallic: 1.0,
121 reflectance: 1.0,
122 perceptual_roughness: 0.0,
123 ..default()
124 });
125
126 commands.spawn((
127 Mesh3d(cube_mesh),
128 MeshMaterial3d(cube_material),
129 Transform::from_xyz(0.0, -4.0, -2.5),
130 InnerCube,
131 ));
132}184fn spawn_plane_mesh(
185 commands: &mut Commands,
186 asset_server: &AssetServer,
187 meshes: &mut Assets<Mesh>,
188 materials: &mut Assets<StandardMaterial>,
189) {
190 // Create a plane onto which we project decals.
191 //
192 // As the plane has a normal map, we must generate tangents for the
193 // vertices.
194 let plane_mesh = meshes.add(
195 Plane3d {
196 normal: Dir3::NEG_Z,
197 half_size: Vec2::splat(PLANE_HALF_SIZE),
198 }
199 .mesh()
200 .build()
201 .with_duplicated_vertices()
202 .with_computed_flat_normals()
203 .with_generated_tangents()
204 .unwrap(),
205 );
206
207 // Give the plane some texture.
208 //
209 // Note that, as this is a normal map, we must disable sRGB when loading.
210 let normal_map_texture = asset_server
211 .load_builder()
212 .with_settings(|settings: &mut ImageLoaderSettings| settings.is_srgb = false)
213 .load("textures/ScratchedGold-Normal.png");
214
215 // Actually spawn the plane.
216 commands.spawn((
217 Mesh3d(plane_mesh),
218 MeshMaterial3d(materials.add(StandardMaterial {
219 base_color: Color::from(CRIMSON),
220 normal_map_texture: Some(normal_map_texture),
221 ..StandardMaterial::default()
222 })),
223 Transform::IDENTITY,
224 ));
225}Sourcepub fn try_with_computed_flat_normals(self) -> Result<Mesh, MeshAccessError>
pub fn try_with_computed_flat_normals(self) -> Result<Mesh, MeshAccessError>
Consumes the mesh and returns a mesh with calculated Mesh::ATTRIBUTE_NORMAL.
(Alternatively, you can use Mesh::compute_flat_normals to mutate an existing mesh in-place)
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh has indices defined
Sourcepub fn with_computed_smooth_normals(self) -> Mesh
pub fn with_computed_smooth_normals(self) -> Mesh
Consumes the mesh and returns a mesh with calculated Mesh::ATTRIBUTE_NORMAL.
(Alternatively, you can use Mesh::compute_smooth_normals to mutate an existing mesh in-place)
This method weights normals by the angles of triangle corners connected to each vertex. If
you would rather have the computed normals be weighted by triangle area, see
Mesh::with_computed_area_weighted_normals instead.
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh does not have indices defined.
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_with_computed_smooth_normals
Sourcepub fn try_with_computed_smooth_normals(self) -> Result<Mesh, MeshAccessError>
pub fn try_with_computed_smooth_normals(self) -> Result<Mesh, MeshAccessError>
Consumes the mesh and returns a mesh with calculated Mesh::ATTRIBUTE_NORMAL.
(Alternatively, you can use Mesh::compute_smooth_normals to mutate an existing mesh in-place)
This method weights normals by the angles of triangle corners connected to each vertex. If
you would rather have the computed normals be weighted by triangle area, see
Mesh::with_computed_area_weighted_normals instead.
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh does not have indices defined.
Sourcepub fn with_computed_area_weighted_normals(self) -> Mesh
pub fn with_computed_area_weighted_normals(self) -> Mesh
Consumes the mesh and returns a mesh with calculated Mesh::ATTRIBUTE_NORMAL.
(Alternatively, you can use Mesh::compute_area_weighted_normals to mutate an existing mesh in-place)
This method weights normals by the area of each triangle containing the vertex. Thus,
larger triangles will skew the normals of their vertices towards their own normal more
than smaller triangles will. If you would rather have the computed normals be influenced
only by the angles of connected edges, see Mesh::with_computed_smooth_normals instead.
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh does not have indices defined.
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_with_computed_area_weighted_normals
Sourcepub fn try_with_computed_area_weighted_normals(
self,
) -> Result<Mesh, MeshAccessError>
pub fn try_with_computed_area_weighted_normals( self, ) -> Result<Mesh, MeshAccessError>
Consumes the mesh and returns a mesh with calculated Mesh::ATTRIBUTE_NORMAL.
(Alternatively, you can use Mesh::compute_area_weighted_normals to mutate an existing mesh in-place)
This method weights normals by the area of each triangle containing the vertex. Thus,
larger triangles will skew the normals of their vertices towards their own normal more
than smaller triangles will. If you would rather have the computed normals be influenced
only by the angles of connected edges, see Mesh::with_computed_smooth_normals instead.
§Panics
Panics if Mesh::ATTRIBUTE_POSITION is not of type float3.
Panics if the mesh has any other topology than PrimitiveTopology::TriangleList.
Panics if the mesh does not have indices defined.
Sourcepub fn generate_tangents(&mut self) -> Result<(), GenerateTangentsError>
Available on crate feature bevy_mikktspace only.
pub fn generate_tangents(&mut self) -> Result<(), GenerateTangentsError>
bevy_mikktspace only.Generate tangents for the mesh using the mikktspace algorithm.
Sets the Mesh::ATTRIBUTE_TANGENT attribute if successful.
Requires a PrimitiveTopology::TriangleList topology and the Mesh::ATTRIBUTE_POSITION, Mesh::ATTRIBUTE_NORMAL and Mesh::ATTRIBUTE_UV_0 attributes set.
Examples found in repository?
More examples
212fn setup_parallax(
213 mut commands: Commands,
214 mut materials: ResMut<Assets<StandardMaterial>>,
215 mut meshes: ResMut<Assets<Mesh>>,
216 asset_server: Res<AssetServer>,
217) {
218 // The normal map. Note that to generate it in the GIMP image editor, you should
219 // open the depth map, and do Filters → Generic → Normal Map
220 // You should enable the "flip X" checkbox.
221 let normal_handle = asset_server
222 .load_builder()
223 .with_settings(
224 // The normal map texture is in linear color space. Lighting won't look correct
225 // if `is_srgb` is `true`, which is the default.
226 |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
227 )
228 .load("textures/parallax_example/cube_normal.png");
229
230 let mut cube = Mesh::from(Cuboid::new(0.15, 0.15, 0.15));
231
232 // NOTE: for normal maps and depth maps to work, the mesh
233 // needs tangents generated.
234 cube.generate_tangents().unwrap();
235
236 let parallax_material = materials.add(StandardMaterial {
237 perceptual_roughness: 0.4,
238 base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
239 normal_map_texture: Some(normal_handle),
240 // The depth map is a grayscale texture where black is the highest level and
241 // white the lowest.
242 depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
243 parallax_depth_scale: 0.09,
244 parallax_mapping_method: ParallaxMappingMethod::Relief { max_steps: 4 },
245 max_parallax_layer_count: ops::exp2(5.0f32),
246 ..default()
247 });
248 commands.spawn((
249 Mesh3d(meshes.add(cube)),
250 MeshMaterial3d(parallax_material),
251 Transform::from_xyz(0.4, 0.2, -0.8),
252 Spin { speed: 0.3 },
253 ));
254}60fn setup(
61 mut commands: Commands,
62 mut meshes: ResMut<Assets<Mesh>>,
63 mut materials: ResMut<Assets<StandardMaterial>>,
64) -> Result {
65 let mut seeded_rng = ChaCha8Rng::seed_from_u64(19878367467712);
66
67 // Make a plane for establishing space.
68 commands.spawn((
69 Mesh3d(meshes.add(Plane3d::default().mesh().size(12.0, 12.0))),
70 MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
71 Transform::from_xyz(0.0, -2.5, 0.0),
72 ));
73
74 // Spawn a light:
75 commands.spawn((
76 PointLight {
77 shadow_maps_enabled: true,
78 ..default()
79 },
80 Transform::from_xyz(4.0, 8.0, 4.0),
81 ));
82
83 // Spawn a camera:
84 commands.spawn((
85 Camera3d::default(),
86 Transform::from_xyz(-2.0, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
87 ));
88
89 // Create a new sphere mesh:
90 let mut sphere_mesh = Sphere::new(1.0).mesh().ico(7)?;
91 sphere_mesh.generate_tangents()?;
92
93 // Spawn the mesh into the scene:
94 let mut sphere = commands.spawn((
95 Mesh3d(meshes.add(sphere_mesh.clone())),
96 MeshMaterial3d(materials.add(StandardMaterial::default())),
97 Transform::from_xyz(-1.0, 1.0, 0.0),
98 ));
99
100 // Generate random sample points:
101 let triangles = sphere_mesh.triangles()?;
102 let distribution = UniformMeshSampler::try_new(triangles)?;
103
104 // Setup sample points:
105 let point_mesh = meshes.add(Sphere::new(0.01).mesh().ico(3)?);
106 let point_material = materials.add(StandardMaterial {
107 base_color: Srgba::RED.into(),
108 emissive: LinearRgba::rgb(1.0, 0.0, 0.0),
109 ..default()
110 });
111
112 // Add sample points as children of the sphere:
113 for point in distribution.sample_iter(&mut seeded_rng).take(10000) {
114 sphere.with_child((
115 Mesh3d(point_mesh.clone()),
116 MeshMaterial3d(point_material.clone()),
117 Transform::from_translation(point),
118 ));
119 }
120
121 // Indicate the system completed successfully:
122 Ok(())
123}374fn add_raytracing_meshes_on_scene_load(
375 scene_ready: On<WorldInstanceReady>,
376 children: Query<&Children>,
377 mesh_query: Query<(
378 &Mesh3d,
379 &MeshMaterial3d<StandardMaterial>,
380 Option<&GltfMaterialName>,
381 )>,
382 mut meshes: ResMut<Assets<Mesh>>,
383 mut materials: ResMut<Assets<StandardMaterial>>,
384 mut commands: Commands,
385 args: Res<Args>,
386) {
387 for descendant in children.iter_descendants(scene_ready.entity) {
388 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
389 mesh_query.get(descendant)
390 {
391 // Add raytracing mesh component
392 commands
393 .entity(descendant)
394 .insert(RaytracingMesh3d(mesh_handle.clone()));
395
396 // Ensure meshes are Solari compatible
397 let mut mesh = meshes.get_mut(mesh_handle).unwrap();
398 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
399 let vertex_count = mesh.count_vertices();
400 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
401 mesh.insert_attribute(
402 Mesh::ATTRIBUTE_TANGENT,
403 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
404 );
405 }
406 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
407 mesh.generate_tangents().unwrap();
408 }
409 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
410 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
411 }
412 if let Some(indices) = mesh.indices_mut()
413 && let Indices::U16(_) = indices
414 {
415 *indices = Indices::U32(indices.iter().map(|i| i as u32).collect());
416 }
417
418 // Prevent rasterization if using pathtracer
419 if args.pathtracer == Some(true) {
420 commands.entity(descendant).remove::<Mesh3d>();
421 }
422
423 // Adjust scene materials to better demo Solari features
424 if material_name.map(|s| s.0.as_str()) == Some("material") {
425 let mut material = materials.get_mut(material_handle).unwrap();
426 material.emissive = LinearRgba::BLACK;
427 }
428 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
429 let mut material = materials.get_mut(material_handle).unwrap();
430 material.emissive =
431 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
432 material.alpha_mode = AlphaMode::Opaque;
433 material.specular_transmission = 0.0;
434
435 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
436 }
437 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
438 let mut material = materials.get_mut(material_handle).unwrap();
439 material.alpha_mode = AlphaMode::Opaque;
440 material.specular_transmission = 0.0;
441 }
442 }
443 }
444}Sourcepub fn with_generated_tangents(self) -> Result<Mesh, GenerateTangentsError>
Available on crate feature bevy_mikktspace only.
pub fn with_generated_tangents(self) -> Result<Mesh, GenerateTangentsError>
bevy_mikktspace only.Consumes the mesh and returns a mesh with tangents generated using the mikktspace algorithm.
The resulting mesh will have the Mesh::ATTRIBUTE_TANGENT attribute if successful.
(Alternatively, you can use Mesh::generate_tangents to mutate an existing mesh in-place)
Requires a PrimitiveTopology::TriangleList topology and the Mesh::ATTRIBUTE_POSITION, Mesh::ATTRIBUTE_NORMAL and Mesh::ATTRIBUTE_UV_0 attributes set.
Examples found in repository?
100fn setup(mut commands: Commands, asset_server: Res<AssetServer>, app_status: Res<AppStatus>) {
101 commands.spawn((
102 Camera3d::default(),
103 Transform::from_translation(CAMERA_INITIAL_POSITION).looking_at(Vec3::ZERO, Vec3::Y),
104 ));
105
106 spawn_directional_light(&mut commands);
107
108 commands.spawn((
109 WorldAssetRoot(
110 asset_server.load("models/AnisotropyBarnLamp/AnisotropyBarnLamp.gltf#Scene0"),
111 ),
112 Transform::from_xyz(0.0, 0.07, -0.13),
113 Scene::BarnLamp,
114 ));
115
116 commands.spawn((
117 Mesh3d(
118 asset_server.add(
119 Mesh::from(Sphere::new(0.1))
120 .with_generated_tangents()
121 .unwrap(),
122 ),
123 ),
124 MeshMaterial3d(asset_server.add(StandardMaterial {
125 base_color: palettes::tailwind::GRAY_300.into(),
126 anisotropy_rotation: 0.5,
127 anisotropy_strength: 1.,
128 ..default()
129 })),
130 Scene::Sphere,
131 Visibility::Hidden,
132 ));
133
134 spawn_text(&mut commands, &app_status);
135}More examples
184fn spawn_plane_mesh(
185 commands: &mut Commands,
186 asset_server: &AssetServer,
187 meshes: &mut Assets<Mesh>,
188 materials: &mut Assets<StandardMaterial>,
189) {
190 // Create a plane onto which we project decals.
191 //
192 // As the plane has a normal map, we must generate tangents for the
193 // vertices.
194 let plane_mesh = meshes.add(
195 Plane3d {
196 normal: Dir3::NEG_Z,
197 half_size: Vec2::splat(PLANE_HALF_SIZE),
198 }
199 .mesh()
200 .build()
201 .with_duplicated_vertices()
202 .with_computed_flat_normals()
203 .with_generated_tangents()
204 .unwrap(),
205 );
206
207 // Give the plane some texture.
208 //
209 // Note that, as this is a normal map, we must disable sRGB when loading.
210 let normal_map_texture = asset_server
211 .load_builder()
212 .with_settings(|settings: &mut ImageLoaderSettings| settings.is_srgb = false)
213 .load("textures/ScratchedGold-Normal.png");
214
215 // Actually spawn the plane.
216 commands.spawn((
217 Mesh3d(plane_mesh),
218 MeshMaterial3d(materials.add(StandardMaterial {
219 base_color: Color::from(CRIMSON),
220 normal_map_texture: Some(normal_map_texture),
221 ..StandardMaterial::default()
222 })),
223 Transform::IDENTITY,
224 ));
225}200fn setup(
201 mut commands: Commands,
202 mut materials: ResMut<Assets<StandardMaterial>>,
203 mut meshes: ResMut<Assets<Mesh>>,
204 asset_server: Res<AssetServer>,
205) {
206 // The normal map. Note that to generate it in the GIMP image editor, you should
207 // open the depth map, and do Filters → Generic → Normal Map
208 // You should enable the "flip X" checkbox.
209 let normal_handle = asset_server
210 .load_builder()
211 .with_settings(
212 // The normal map texture is in linear color space. Lighting won't look correct
213 // if `is_srgb` is `true`, which is the default.
214 |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
215 )
216 .load("textures/parallax_example/cube_normal.png");
217
218 // Camera
219 commands.spawn((
220 Camera3d::default(),
221 Transform::from_xyz(1.5, 1.5, 1.5).looking_at(Vec3::ZERO, Vec3::Y),
222 FreeCameraController,
223 ));
224
225 // represent the light source as a sphere
226 let mesh = meshes.add(Sphere::new(0.05).mesh().ico(3).unwrap());
227
228 // light
229 commands.spawn((
230 PointLight {
231 shadow_maps_enabled: true,
232 ..default()
233 },
234 Transform::from_xyz(2.0, 1.0, -1.1),
235 children![(Mesh3d(mesh), MeshMaterial3d(materials.add(Color::WHITE)))],
236 ));
237
238 // Plane
239 commands.spawn((
240 Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))),
241 MeshMaterial3d(materials.add(StandardMaterial {
242 // standard material derived from dark green, but
243 // with roughness and reflectance set.
244 perceptual_roughness: 0.45,
245 reflectance: 0.18,
246 ..Color::srgb_u8(0, 80, 0).into()
247 })),
248 Transform::from_xyz(0.0, -1.0, 0.0),
249 ));
250
251 let parallax_depth_scale = TargetDepth::default().0;
252 let max_parallax_layer_count = ops::exp2(TargetLayers::default().0);
253 let parallax_mapping_method = CurrentMethod::default();
254 let parallax_material = materials.add(StandardMaterial {
255 perceptual_roughness: 0.4,
256 base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
257 normal_map_texture: Some(normal_handle),
258 // The depth map is a grayscale texture where black is the highest level and
259 // white the lowest.
260 depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
261 parallax_depth_scale,
262 parallax_mapping_method: parallax_mapping_method.0,
263 max_parallax_layer_count,
264 ..default()
265 });
266 commands.spawn((
267 Mesh3d(
268 meshes.add(
269 // NOTE: for normal maps and depth maps to work, the mesh
270 // needs tangents generated.
271 Mesh::from(Cuboid::default())
272 .with_generated_tangents()
273 .unwrap(),
274 ),
275 ),
276 MeshMaterial3d(parallax_material.clone()),
277 Spin { speed: 0.3 },
278 ));
279
280 let background_cube = meshes.add(
281 Mesh::from(Cuboid::new(40.0, 40.0, 40.0))
282 .with_generated_tangents()
283 .unwrap(),
284 );
285
286 let background_cube_bundle = |translation| {
287 (
288 Mesh3d(background_cube.clone()),
289 MeshMaterial3d(parallax_material.clone()),
290 Transform::from_translation(translation),
291 Spin { speed: -0.1 },
292 )
293 };
294 commands.spawn(background_cube_bundle(Vec3::new(45., 0., 0.)));
295 commands.spawn(background_cube_bundle(Vec3::new(-45., 0., 0.)));
296 commands.spawn(background_cube_bundle(Vec3::new(0., 0., 45.)));
297 commands.spawn(background_cube_bundle(Vec3::new(0., 0., -45.)));
298
299 // example instructions
300 commands.spawn((
301 Text::default(),
302 Node {
303 position_type: PositionType::Absolute,
304 top: px(12),
305 left: px(12),
306 ..default()
307 },
308 children![
309 (TextSpan(format!("Parallax depth scale: {parallax_depth_scale:.5}\n"))),
310 (TextSpan(format!("Layers: {max_parallax_layer_count:.0}\n"))),
311 (TextSpan(format!("{parallax_mapping_method}\n"))),
312 (TextSpan::new("\n\n")),
313 (TextSpan::new("Controls:\n")),
314 (TextSpan::new("Left click - Change view angle\n")),
315 (TextSpan::new("1/2 - Decrease/Increase parallax depth scale\n",)),
316 (TextSpan::new("3/4 - Decrease/Increase layer count\n")),
317 (TextSpan::new("Space - Switch parallaxing algorithm\n")),
318 ],
319 ));
320}200fn setup_many_lights(
201 mut commands: Commands,
202 asset_server: Res<AssetServer>,
203 mut meshes: ResMut<Assets<Mesh>>,
204 mut materials: ResMut<Assets<StandardMaterial>>,
205 args: Res<Args>,
206 #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))] dlss_rr_supported: Option<
207 Res<DlssRayReconstructionSupported>,
208 >,
209) {
210 let mut rng = ChaCha8Rng::seed_from_u64(42);
211
212 let mut plane_mesh = Plane3d::default()
213 .mesh()
214 .size(400.0, 400.0)
215 .build()
216 .with_generated_tangents()
217 .unwrap();
218 match plane_mesh.attribute_mut(Mesh::ATTRIBUTE_UV_0).unwrap() {
219 VertexAttributeValues::Float32x2(items) => {
220 items.iter_mut().flatten().for_each(|x| *x *= 3.0);
221 }
222 _ => unreachable!(),
223 }
224 let plane_mesh = meshes.add(plane_mesh);
225 let cube_mesh = meshes.add(
226 Cuboid::default()
227 .mesh()
228 .build()
229 .with_generated_tangents()
230 .unwrap(),
231 );
232 let sphere_mesh = meshes.add(
233 Sphere::new(1.0)
234 .mesh()
235 .build()
236 .with_generated_tangents()
237 .unwrap(),
238 );
239
240 commands
241 .spawn((
242 RaytracingMesh3d(plane_mesh.clone()),
243 MeshMaterial3d(
244 materials.add(StandardMaterial {
245 base_color_texture: Some(
246 asset_server
247 .load_builder()
248 .with_settings::<ImageLoaderSettings>(|settings| {
249 settings
250 .sampler
251 .get_or_init_descriptor()
252 .set_address_mode(ImageAddressMode::Repeat);
253 })
254 .load("textures/uv_checker_bw.png"),
255 ),
256 perceptual_roughness: 0.0,
257 ..default()
258 }),
259 ),
260 ))
261 .insert_if(Mesh3d(plane_mesh), || args.pathtracer != Some(true));
262
263 for _ in 0..8000 {
264 commands
265 .spawn((
266 RaytracingMesh3d(cube_mesh.clone()),
267 MeshMaterial3d(materials.add(StandardMaterial {
268 base_color: Color::srgb(rng.random(), rng.random(), rng.random()),
269 perceptual_roughness: rng.random(),
270 ..default()
271 })),
272 Transform::default()
273 .with_scale(Vec3 {
274 x: rng.random_range(0.2..=2.0),
275 y: rng.random_range(0.2..=2.0),
276 z: rng.random_range(0.2..=2.0),
277 })
278 .with_translation(Vec3::new(
279 rng.random_range(-180.0..=180.0),
280 0.2,
281 rng.random_range(-180.0..=180.0),
282 )),
283 ))
284 .insert_if(Mesh3d(cube_mesh.clone()), || args.pathtracer != Some(true));
285 }
286
287 for x in -10..=10 {
288 for y in -10..=10 {
289 commands
290 .spawn((
291 RaytracingMesh3d(sphere_mesh.clone()),
292 MeshMaterial3d(
293 materials.add(StandardMaterial {
294 emissive: Color::linear_rgb(
295 rng.random::<f32>() * 60000.0,
296 rng.random::<f32>() * 60000.0,
297 rng.random::<f32>() * 60000.0,
298 )
299 .into(),
300 ..default()
301 }),
302 ),
303 Transform::default().with_translation(Vec3::new(
304 (x * 20) as f32,
305 7.0,
306 (y * 20) as f32,
307 )),
308 ))
309 .insert_if(Mesh3d(sphere_mesh.clone()), || {
310 args.pathtracer != Some(true)
311 });
312 }
313 }
314
315 let mut camera = commands.spawn((
316 Camera3d::default(),
317 Camera {
318 clear_color: ClearColorConfig::Custom(Color::BLACK),
319 ..default()
320 },
321 FreeCamera {
322 walk_speed: 3.0,
323 run_speed: 10.0,
324 ..Default::default()
325 },
326 Transform::from_translation(Vec3::new(6.11329, 166.74896, 451.8226)).with_rotation(
327 Quat::from_xyzw(-0.183938, 0.009093744, 0.0017017953, 0.9828943),
328 ),
329 // Msaa::Off and CameraMainTextureUsages with STORAGE_BINDING are required for Solari
330 CameraMainTextureUsages::default().with(TextureUsages::STORAGE_BINDING),
331 Msaa::Off,
332 Bloom {
333 intensity: 0.1,
334 ..Bloom::NATURAL
335 },
336 ));
337
338 if args.pathtracer == Some(true) {
339 camera.insert(Pathtracer::default());
340 } else {
341 camera.insert(SolariLighting::default());
342 }
343
344 // Using DLSS Ray Reconstruction for denoising (and cheaper rendering via upscaling) is _highly_ recommended when using Solari
345 #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
346 if dlss_rr_supported.is_some() {
347 camera.insert(Dlss::<DlssRayReconstructionFeature> {
348 perf_quality_mode: Default::default(),
349 reset: Default::default(),
350 _phantom_data: Default::default(),
351 });
352 }
353
354 commands.spawn((
355 Node {
356 position_type: PositionType::Absolute,
357 right: px(0.0),
358 padding: px(4.0).all(),
359 border_radius: BorderRadius::bottom_left(px(4.0)),
360 ..default()
361 },
362 BackgroundColor(Color::srgba(0.10, 0.10, 0.10, 0.8)),
363 children![(
364 PerformanceText,
365 Text::default(),
366 TextFont {
367 font_size: FontSize::Px(8.0),
368 ..default()
369 },
370 )],
371 ));
372}Sourcepub fn merge(&mut self, other: &Mesh) -> Result<(), MeshMergeError>
pub fn merge(&mut self, other: &Mesh) -> Result<(), MeshMergeError>
Merges the Mesh data of other with self. The attributes and indices of other will be appended to self.
Note that attributes of other that don’t exist on self will be ignored.
Aabb of entities with modified mesh are not updated automatically.
§Errors
If any of the following conditions are not met, this function errors:
- All of the vertex attributes that have the same attribute id, must also
have the same attribute type.
For example two attributes with the same id, but where one is a
VertexAttributeValues::Float32and the other is aVertexAttributeValues::Float32x3, would be invalid. - Both meshes must have the same primitive topology.
Sourcepub fn transformed_by(self, transform: Transform) -> Mesh
pub fn transformed_by(self, transform: Transform) -> Mesh
Transforms the vertex positions, normals, and tangents of the mesh by the given Transform.
Aabb of entities with modified mesh are not updated automatically.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_transformed_by
Sourcepub fn try_transformed_by(
self,
transform: Transform,
) -> Result<Mesh, MeshAccessError>
pub fn try_transformed_by( self, transform: Transform, ) -> Result<Mesh, MeshAccessError>
Transforms the vertex positions, normals, and tangents of the mesh by the given Transform.
Aabb of entities with modified mesh are not updated automatically.
Sourcepub fn transform_by(&mut self, transform: Transform)
pub fn transform_by(&mut self, transform: Transform)
Transforms the vertex positions, normals, and tangents of the mesh in place by the given Transform.
Aabb of entities with modified mesh are not updated automatically.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_transform_by
Sourcepub fn try_transform_by(
&mut self,
transform: Transform,
) -> Result<(), MeshAccessError>
pub fn try_transform_by( &mut self, transform: Transform, ) -> Result<(), MeshAccessError>
Transforms the vertex positions, normals, and tangents of the mesh in place by the given Transform.
Aabb of entities with modified mesh are not updated automatically.
Sourcepub fn translated_by(self, translation: Vec3) -> Mesh
pub fn translated_by(self, translation: Vec3) -> Mesh
Translates the vertex positions of the mesh by the given Vec3.
Aabb of entities with modified mesh are not updated automatically.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_translated_by
Sourcepub fn try_translated_by(
self,
translation: Vec3,
) -> Result<Mesh, MeshAccessError>
pub fn try_translated_by( self, translation: Vec3, ) -> Result<Mesh, MeshAccessError>
Translates the vertex positions of the mesh by the given Vec3.
Aabb of entities with modified mesh are not updated automatically.
Sourcepub fn translate_by(&mut self, translation: Vec3)
pub fn translate_by(&mut self, translation: Vec3)
Translates the vertex positions of the mesh in place by the given Vec3.
Aabb of entities with modified mesh are not updated automatically.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_translate_by
Sourcepub fn try_translate_by(
&mut self,
translation: Vec3,
) -> Result<(), MeshAccessError>
pub fn try_translate_by( &mut self, translation: Vec3, ) -> Result<(), MeshAccessError>
Translates the vertex positions of the mesh in place by the given Vec3.
Aabb of entities with modified mesh are not updated automatically.
Sourcepub fn rotated_by(self, rotation: Quat) -> Mesh
pub fn rotated_by(self, rotation: Quat) -> Mesh
Rotates the vertex positions, normals, and tangents of the mesh by the given Quat.
Aabb of entities with modified mesh are not updated automatically.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_rotated_by
Sourcepub fn try_rotated_by(self, rotation: Quat) -> Result<Mesh, MeshAccessError>
pub fn try_rotated_by(self, rotation: Quat) -> Result<Mesh, MeshAccessError>
Rotates the vertex positions, normals, and tangents of the mesh by the given Quat.
Aabb of entities with modified mesh are not updated automatically.
Sourcepub fn rotate_by(&mut self, rotation: Quat)
pub fn rotate_by(&mut self, rotation: Quat)
Rotates the vertex positions, normals, and tangents of the mesh in place by the given Quat.
Aabb of entities with modified mesh are not updated automatically.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_rotate_by
Sourcepub fn try_rotate_by(&mut self, rotation: Quat) -> Result<(), MeshAccessError>
pub fn try_rotate_by(&mut self, rotation: Quat) -> Result<(), MeshAccessError>
Rotates the vertex positions, normals, and tangents of the mesh in place by the given Quat.
Aabb of entities with modified mesh are not updated automatically.
Sourcepub fn scaled_by(self, scale: Vec3) -> Mesh
pub fn scaled_by(self, scale: Vec3) -> Mesh
Scales the vertex positions, normals, and tangents of the mesh by the given Vec3.
Aabb of entities with modified mesh are not updated automatically.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_scaled_by
Sourcepub fn try_scaled_by(self, scale: Vec3) -> Result<Mesh, MeshAccessError>
pub fn try_scaled_by(self, scale: Vec3) -> Result<Mesh, MeshAccessError>
Scales the vertex positions, normals, and tangents of the mesh by the given Vec3.
Aabb of entities with modified mesh are not updated automatically.
Sourcepub fn scale_by(&mut self, scale: Vec3)
pub fn scale_by(&mut self, scale: Vec3)
Scales the vertex positions, normals, and tangents of the mesh in place by the given Vec3.
Aabb of entities with modified mesh are not updated automatically.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_scale_by
Sourcepub fn try_scale_by(&mut self, scale: Vec3) -> Result<(), MeshAccessError>
pub fn try_scale_by(&mut self, scale: Vec3) -> Result<(), MeshAccessError>
Scales the vertex positions, normals, and tangents of the mesh in place by the given Vec3.
Aabb of entities with modified mesh are not updated automatically.
Sourcepub fn normalize_joint_weights(&mut self)
pub fn normalize_joint_weights(&mut self)
Normalize joint weights so they sum to 1.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_normalize_joint_weights
Sourcepub fn try_normalize_joint_weights(&mut self) -> Result<(), MeshAccessError>
pub fn try_normalize_joint_weights(&mut self) -> Result<(), MeshAccessError>
Normalize joint weights so they sum to 1.
Sourcepub fn triangles(
&self,
) -> Result<impl Iterator<Item = Triangle3d>, MeshTrianglesError>
pub fn triangles( &self, ) -> Result<impl Iterator<Item = Triangle3d>, MeshTrianglesError>
Get a list of this Mesh’s triangles as an iterator if possible.
Returns an error if any of the following conditions are met (see MeshTrianglesError):
- The Mesh’s primitive topology is not
TriangleListorTriangleStrip. - The Mesh is missing position or index data.
- The Mesh’s position data has the wrong format (not
Float32x3).
Examples found in repository?
60fn setup(
61 mut commands: Commands,
62 mut meshes: ResMut<Assets<Mesh>>,
63 mut materials: ResMut<Assets<StandardMaterial>>,
64) -> Result {
65 let mut seeded_rng = ChaCha8Rng::seed_from_u64(19878367467712);
66
67 // Make a plane for establishing space.
68 commands.spawn((
69 Mesh3d(meshes.add(Plane3d::default().mesh().size(12.0, 12.0))),
70 MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
71 Transform::from_xyz(0.0, -2.5, 0.0),
72 ));
73
74 // Spawn a light:
75 commands.spawn((
76 PointLight {
77 shadow_maps_enabled: true,
78 ..default()
79 },
80 Transform::from_xyz(4.0, 8.0, 4.0),
81 ));
82
83 // Spawn a camera:
84 commands.spawn((
85 Camera3d::default(),
86 Transform::from_xyz(-2.0, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
87 ));
88
89 // Create a new sphere mesh:
90 let mut sphere_mesh = Sphere::new(1.0).mesh().ico(7)?;
91 sphere_mesh.generate_tangents()?;
92
93 // Spawn the mesh into the scene:
94 let mut sphere = commands.spawn((
95 Mesh3d(meshes.add(sphere_mesh.clone())),
96 MeshMaterial3d(materials.add(StandardMaterial::default())),
97 Transform::from_xyz(-1.0, 1.0, 0.0),
98 ));
99
100 // Generate random sample points:
101 let triangles = sphere_mesh.triangles()?;
102 let distribution = UniformMeshSampler::try_new(triangles)?;
103
104 // Setup sample points:
105 let point_mesh = meshes.add(Sphere::new(0.01).mesh().ico(3)?);
106 let point_material = materials.add(StandardMaterial {
107 base_color: Srgba::RED.into(),
108 emissive: LinearRgba::rgb(1.0, 0.0, 0.0),
109 ..default()
110 });
111
112 // Add sample points as children of the sphere:
113 for point in distribution.sample_iter(&mut seeded_rng).take(10000) {
114 sphere.with_child((
115 Mesh3d(point_mesh.clone()),
116 MeshMaterial3d(point_material.clone()),
117 Transform::from_translation(point),
118 ));
119 }
120
121 // Indicate the system completed successfully:
122 Ok(())
123}Sourcepub fn take_gpu_data(&mut self) -> Result<Mesh, MeshAccessError>
pub fn take_gpu_data(&mut self) -> Result<Mesh, MeshAccessError>
Extracts the mesh vertex, index and morph target data for GPU upload. This function is called internally in render world extraction, it is unlikely to be useful outside of that context.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn skinned_mesh_bounds(&self) -> Option<&SkinnedMeshBounds>
pub fn skinned_mesh_bounds(&self) -> Option<&SkinnedMeshBounds>
Get this mesh’s SkinnedMeshBounds.
Sourcepub fn set_skinned_mesh_bounds(
&mut self,
skinned_mesh_bounds: Option<SkinnedMeshBounds>,
)
pub fn set_skinned_mesh_bounds( &mut self, skinned_mesh_bounds: Option<SkinnedMeshBounds>, )
Set this mesh’s SkinnedMeshBounds.
Sourcepub fn with_skinned_mesh_bounds(
self,
skinned_mesh_bounds: Option<SkinnedMeshBounds>,
) -> Mesh
pub fn with_skinned_mesh_bounds( self, skinned_mesh_bounds: Option<SkinnedMeshBounds>, ) -> Mesh
Consumes the mesh and returns a mesh with the given SkinnedMeshBounds.
Sourcepub fn generate_skinned_mesh_bounds(
&mut self,
) -> Result<(), SkinnedMeshBoundsError>
pub fn generate_skinned_mesh_bounds( &mut self, ) -> Result<(), SkinnedMeshBoundsError>
Generate SkinnedMeshBounds for this mesh.
Sourcepub fn with_generated_skinned_mesh_bounds(
self,
) -> Result<Mesh, SkinnedMeshBoundsError>
pub fn with_generated_skinned_mesh_bounds( self, ) -> Result<Mesh, SkinnedMeshBoundsError>
Consumes the mesh and returns a mesh with generated SkinnedMeshBounds.
Examples found in repository?
124fn spawn_custom_meshes(
125 mut commands: Commands,
126 mut mesh_assets: ResMut<Assets<Mesh>>,
127 mut material_assets: ResMut<Assets<StandardMaterial>>,
128 mut inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
129) {
130 let mesh_handle = mesh_assets.add(
131 Mesh::new(
132 PrimitiveTopology::TriangleStrip,
133 // Test that skinned mesh bounds work even if the mesh is render
134 // world only.
135 RenderAssetUsages::RENDER_WORLD,
136 )
137 .with_inserted_attribute(
138 Mesh::ATTRIBUTE_POSITION,
139 vec![
140 [-0.5, 0.0, 0.0],
141 [0.5, 0.0, 0.0],
142 [-0.5, 0.5, 0.0],
143 [0.5, 0.5, 0.0],
144 [-0.5, 1.0, 0.0],
145 [0.5, 1.0, 0.0],
146 [-0.5, 1.5, 0.0],
147 [0.5, 1.5, 0.0],
148 [-0.5, 2.0, 0.0],
149 [0.5, 2.0, 0.0],
150 ],
151 )
152 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10])
153 .with_inserted_attribute(
154 Mesh::ATTRIBUTE_JOINT_INDEX,
155 VertexAttributeValues::Uint16x4(vec![
156 [1, 0, 0, 0],
157 [1, 0, 0, 0],
158 [1, 2, 0, 0],
159 [1, 2, 0, 0],
160 [1, 2, 0, 0],
161 [1, 2, 0, 0],
162 [2, 1, 0, 0],
163 [2, 1, 0, 0],
164 [2, 0, 0, 0],
165 [2, 0, 0, 0],
166 ]),
167 )
168 .with_inserted_attribute(
169 Mesh::ATTRIBUTE_JOINT_WEIGHT,
170 vec![
171 [1.00, 0.00, 0.0, 0.0],
172 [1.00, 0.00, 0.0, 0.0],
173 [0.75, 0.25, 0.0, 0.0],
174 [0.75, 0.25, 0.0, 0.0],
175 [0.50, 0.50, 0.0, 0.0],
176 [0.50, 0.50, 0.0, 0.0],
177 [0.75, 0.25, 0.0, 0.0],
178 [0.75, 0.25, 0.0, 0.0],
179 [1.00, 0.00, 0.0, 0.0],
180 [1.00, 0.00, 0.0, 0.0],
181 ],
182 )
183 .with_generated_skinned_mesh_bounds()
184 .unwrap(),
185 );
186
187 let inverse_bindposes_handle = inverse_bindposes_assets.add(vec![
188 Mat4::from_translation(Vec3::new(0.0, 0.0, 0.0)),
189 Mat4::from_translation(Vec3::new(0.0, 0.0, 0.0)),
190 Mat4::from_translation(Vec3::new(0.0, -1.0, 0.0)),
191 ]);
192
193 struct MeshInstance {
194 animations: [CustomAnimationId; 2],
195 }
196
197 let mesh_instances = [
198 // Simple cases. First joint is still, second joint is all rotation/translation/scale variations.
199 MeshInstance { animations: [0, 1] },
200 MeshInstance { animations: [0, 2] },
201 MeshInstance { animations: [0, 3] },
202 MeshInstance { animations: [0, 4] },
203 MeshInstance { animations: [0, 5] },
204 MeshInstance { animations: [0, 6] },
205 MeshInstance { animations: [0, 7] },
206 MeshInstance { animations: [0, 8] },
207 // Skewed cases. First joint is non-uniform scaling, second joint is rotation/translation variations.
208 MeshInstance { animations: [9, 1] },
209 MeshInstance { animations: [9, 2] },
210 MeshInstance { animations: [9, 3] },
211 MeshInstance { animations: [9, 4] },
212 MeshInstance { animations: [9, 5] },
213 ];
214
215 for (i, mesh_instance) in mesh_instances.iter().enumerate() {
216 let x = ((i as f32) * 2.0) - ((mesh_instances.len() - 1) as f32);
217
218 let base_entity = commands
219 .spawn((Transform::from_xyz(x, 0.0, 0.0), Visibility::default()))
220 .id();
221
222 let joints = vec![
223 commands.spawn((Transform::IDENTITY,)).id(),
224 commands
225 .spawn((
226 CustomAnimation(mesh_instance.animations[0]),
227 Transform::IDENTITY,
228 ))
229 .id(),
230 commands
231 .spawn((
232 CustomAnimation(mesh_instance.animations[1]),
233 Transform::IDENTITY,
234 ))
235 .id(),
236 ];
237
238 commands.entity(joints[0]).insert(ChildOf(base_entity));
239
240 commands.entity(joints[1]).insert(ChildOf(joints[0]));
241 commands.entity(joints[2]).insert(ChildOf(joints[1]));
242
243 let mesh_entity = commands
244 .spawn((
245 Transform::IDENTITY,
246 Mesh3d(mesh_handle.clone()),
247 MeshMaterial3d(material_assets.add(StandardMaterial {
248 base_color: Color::WHITE,
249 cull_mode: None,
250 ..default()
251 })),
252 SkinnedMesh {
253 inverse_bindposes: inverse_bindposes_handle.clone(),
254 joints: joints.clone(),
255 },
256 DynamicSkinnedMeshBounds,
257 ))
258 .id();
259
260 commands.entity(mesh_entity).insert(ChildOf(base_entity));
261 }
262}More examples
38fn setup(
39 mut commands: Commands,
40 asset_server: Res<AssetServer>,
41 mut meshes: ResMut<Assets<Mesh>>,
42 mut materials: ResMut<Assets<StandardMaterial>>,
43 mut skinned_mesh_inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
44) {
45 // Create a camera
46 commands.spawn((
47 Camera3d::default(),
48 Transform::from_xyz(2.5, 2.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
49 ));
50
51 // Create inverse bindpose matrices for a skeleton consists of 2 joints
52 let inverse_bindposes = skinned_mesh_inverse_bindposes_assets.add(vec![
53 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
54 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
55 ]);
56
57 // Create a mesh
58 let mesh = Mesh::new(
59 PrimitiveTopology::TriangleList,
60 RenderAssetUsages::RENDER_WORLD,
61 )
62 // Set mesh vertex positions
63 .with_inserted_attribute(
64 Mesh::ATTRIBUTE_POSITION,
65 vec![
66 [0.0, 0.0, 0.0],
67 [1.0, 0.0, 0.0],
68 [0.0, 0.5, 0.0],
69 [1.0, 0.5, 0.0],
70 [0.0, 1.0, 0.0],
71 [1.0, 1.0, 0.0],
72 [0.0, 1.5, 0.0],
73 [1.0, 1.5, 0.0],
74 [0.0, 2.0, 0.0],
75 [1.0, 2.0, 0.0],
76 ],
77 )
78 // Add UV coordinates that map the left half of the texture since its a 1 x
79 // 2 rectangle.
80 .with_inserted_attribute(
81 Mesh::ATTRIBUTE_UV_0,
82 vec![
83 [0.0, 0.00],
84 [0.5, 0.00],
85 [0.0, 0.25],
86 [0.5, 0.25],
87 [0.0, 0.50],
88 [0.5, 0.50],
89 [0.0, 0.75],
90 [0.5, 0.75],
91 [0.0, 1.00],
92 [0.5, 1.00],
93 ],
94 )
95 // Set mesh vertex normals
96 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10])
97 // Set mesh vertex joint indices for mesh skinning.
98 // Each vertex gets 4 indices used to address the `JointTransforms` array in the vertex shader
99 // as well as `SkinnedMeshJoint` array in the `SkinnedMesh` component.
100 // This means that a maximum of 4 joints can affect a single vertex.
101 .with_inserted_attribute(
102 Mesh::ATTRIBUTE_JOINT_INDEX,
103 // Need to be explicit here as [u16; 4] could be either Uint16x4 or Unorm16x4.
104 VertexAttributeValues::Uint16x4(vec![
105 [0, 0, 0, 0],
106 [0, 0, 0, 0],
107 [0, 1, 0, 0],
108 [0, 1, 0, 0],
109 [0, 1, 0, 0],
110 [0, 1, 0, 0],
111 [0, 1, 0, 0],
112 [0, 1, 0, 0],
113 [0, 1, 0, 0],
114 [0, 1, 0, 0],
115 ]),
116 )
117 // Set mesh vertex joint weights for mesh skinning.
118 // Each vertex gets 4 joint weights corresponding to the 4 joint indices assigned to it.
119 // The sum of these weights should equal to 1.
120 .with_inserted_attribute(
121 Mesh::ATTRIBUTE_JOINT_WEIGHT,
122 vec![
123 [1.00, 0.00, 0.0, 0.0],
124 [1.00, 0.00, 0.0, 0.0],
125 [0.75, 0.25, 0.0, 0.0],
126 [0.75, 0.25, 0.0, 0.0],
127 [0.50, 0.50, 0.0, 0.0],
128 [0.50, 0.50, 0.0, 0.0],
129 [0.25, 0.75, 0.0, 0.0],
130 [0.25, 0.75, 0.0, 0.0],
131 [0.00, 1.00, 0.0, 0.0],
132 [0.00, 1.00, 0.0, 0.0],
133 ],
134 )
135 // Tell bevy to construct triangles from a list of vertex indices,
136 // where each 3 vertex indices form a triangle.
137 .with_inserted_indices(Indices::U16(vec![
138 0, 1, 3, 0, 3, 2, 2, 3, 5, 2, 5, 4, 4, 5, 7, 4, 7, 6, 6, 7, 9, 6, 9, 8,
139 ]))
140 // Create skinned mesh bounds. Together with the `DynamicSkinnedMeshBounds`
141 // component, this will ensure the mesh is correctly frustum culled.
142 .with_generated_skinned_mesh_bounds()
143 .unwrap();
144
145 let mesh = meshes.add(mesh);
146
147 // We're seeding the PRNG here to make this example deterministic for testing purposes.
148 // This isn't strictly required in practical use unless you need your app to be deterministic.
149 let mut rng = ChaCha8Rng::seed_from_u64(42);
150
151 for i in -5..5 {
152 // Create joint entities
153 let joint_0 = commands
154 .spawn(Transform::from_xyz(
155 i as f32 * 1.5,
156 0.0,
157 // Move quads back a small amount to avoid Z-fighting and not
158 // obscure the transform gizmos.
159 -(i as f32 * 0.01).abs(),
160 ))
161 .id();
162 let joint_1 = commands.spawn((AnimatedJoint(i), Transform::IDENTITY)).id();
163
164 // Set joint_1 as a child of joint_0.
165 commands.entity(joint_0).add_children(&[joint_1]);
166
167 // Each joint in this vector corresponds to each inverse bindpose matrix in `SkinnedMeshInverseBindposes`.
168 let joint_entities = vec![joint_0, joint_1];
169
170 // Create skinned mesh renderer. Note that its transform doesn't affect the position of the mesh.
171 commands.spawn((
172 Mesh3d(mesh.clone()),
173 MeshMaterial3d(materials.add(StandardMaterial {
174 base_color: Color::srgb(
175 rng.random_range(0.0..1.0),
176 rng.random_range(0.0..1.0),
177 rng.random_range(0.0..1.0),
178 ),
179 base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
180 ..default()
181 })),
182 SkinnedMesh {
183 inverse_bindposes: inverse_bindposes.clone(),
184 joints: joint_entities,
185 },
186 DynamicSkinnedMeshBounds,
187 ));
188 }
189}Source§impl Mesh
impl Mesh
Sourcepub fn has_morph_targets(&self) -> bool
Available on crate feature morph only.
pub fn has_morph_targets(&self) -> bool
morph only.Whether this mesh has morph targets.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_has_morph_targets
Sourcepub fn try_has_morph_targets(&self) -> Result<bool, MeshAccessError>
Available on crate feature morph only.
pub fn try_has_morph_targets(&self) -> Result<bool, MeshAccessError>
morph only.Whether this mesh has morph targets.
Sourcepub fn set_morph_targets(&mut self, morph_targets: Vec<MorphAttributes>)
Available on crate feature morph only.
pub fn set_morph_targets(&mut self, morph_targets: Vec<MorphAttributes>)
morph only.Set the morph target displacements for this mesh.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_set_morph_targets
Sourcepub fn try_set_morph_targets(
&mut self,
morph_targets: Vec<MorphAttributes>,
) -> Result<(), MeshAccessError>
Available on crate feature morph only.
pub fn try_set_morph_targets( &mut self, morph_targets: Vec<MorphAttributes>, ) -> Result<(), MeshAccessError>
morph only.Set the [morph target] displacements for this mesh.
Sourcepub fn morph_targets(&self) -> Option<&Vec<MorphAttributes>>
Available on crate feature morph only.
pub fn morph_targets(&self) -> Option<&Vec<MorphAttributes>>
morph only.Retrieve the morph target displacements for this mesh, or None if there are no morph targets.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_morph_targets
Sourcepub fn try_morph_targets(
&self,
) -> Result<&Vec<MorphAttributes>, MeshAccessError>
Available on crate feature morph only.
pub fn try_morph_targets( &self, ) -> Result<&Vec<MorphAttributes>, MeshAccessError>
morph only.Retrieve the morph displacements for this mesh, or None if there are no morph targets.
Returns an error if the mesh data has been extracted to RenderWorldor
if the morph targets do not exist.
Sourcepub fn with_morph_targets(self, morph_targets: Vec<MorphAttributes>) -> Mesh
Available on crate feature morph only.
pub fn with_morph_targets(self, morph_targets: Vec<MorphAttributes>) -> Mesh
morph only.Consumes the mesh and returns a mesh with the given morph target displacements.
(Alternatively, you can use Mesh::set_morph_targets to mutate an existing mesh in-place)
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_with_morph_targets
Sourcepub fn try_with_morph_targets(
self,
morph_targets: Vec<MorphAttributes>,
) -> Result<Mesh, MeshAccessError>
Available on crate feature morph only.
pub fn try_with_morph_targets( self, morph_targets: Vec<MorphAttributes>, ) -> Result<Mesh, MeshAccessError>
morph only.Consumes the mesh and returns a mesh with the given morph targets.
(Alternatively, you can use Mesh::set_morph_targets to mutate an existing mesh in-place)
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn set_morph_target_names(&mut self, names: Vec<String>)
Available on crate feature morph only.
pub fn set_morph_target_names(&mut self, names: Vec<String>)
morph only.Sets the names of each morph target. This should correspond to the order of the morph targets in set_morph_targets.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_set_morph_target_names
Sourcepub fn try_set_morph_target_names(
&mut self,
names: Vec<String>,
) -> Result<(), MeshAccessError>
Available on crate feature morph only.
pub fn try_set_morph_target_names( &mut self, names: Vec<String>, ) -> Result<(), MeshAccessError>
morph only.Sets the names of each morph target. This should correspond to the order of the morph targets in set_morph_targets.
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn with_morph_target_names(self, names: Vec<String>) -> Mesh
Available on crate feature morph only.
pub fn with_morph_target_names(self, names: Vec<String>) -> Mesh
morph only.Consumes the mesh and returns a mesh with morph target names.
Names should correspond to the order of the morph targets in set_morph_targets.
(Alternatively, you can use Mesh::set_morph_target_names to mutate an existing mesh in-place)
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_set_morph_target_names
Sourcepub fn try_with_morph_target_names(
self,
names: Vec<String>,
) -> Result<Mesh, MeshAccessError>
Available on crate feature morph only.
pub fn try_with_morph_target_names( self, names: Vec<String>, ) -> Result<Mesh, MeshAccessError>
morph only.Consumes the mesh and returns a mesh with morph target names.
Names should correspond to the order of the morph targets in set_morph_targets.
(Alternatively, you can use Mesh::set_morph_target_names to mutate an existing mesh in-place)
Returns an error if the mesh data has been extracted to RenderWorld.
Sourcepub fn morph_target_names(&self) -> Option<&[String]>
Available on crate feature morph only.
pub fn morph_target_names(&self) -> Option<&[String]>
morph only.Gets a list of all morph target names, if they exist.
§Panics
Panics when the mesh data has already been extracted to RenderWorld. To handle
this as an error use Mesh::try_morph_target_names
Examples found in repository?
80fn name_morphs(
81 asset_server: Res<AssetServer>,
82 mut events: MessageReader<AssetEvent<Mesh>>,
83 meshes: Res<Assets<Mesh>>,
84) {
85 for event in events.read() {
86 if let AssetEvent::<Mesh>::Added { id } = event
87 && let Some(path) = asset_server.get_path(*id)
88 && let Some(mesh) = meshes.get(*id)
89 && let Some(names) = mesh.morph_target_names()
90 {
91 info!("Morph target names for {path:?}:");
92
93 for name in names {
94 info!(" {name}");
95 }
96 }
97 }
98}Sourcepub fn try_morph_target_names(
&self,
) -> Result<Option<&[String]>, MeshAccessError>
Available on crate feature morph only.
pub fn try_morph_target_names( &self, ) -> Result<Option<&[String]>, MeshAccessError>
morph only.Gets a list of all morph target names, if they exist.
Returns an error if the mesh data has been extracted to RenderWorldor
if the morph targets do not exist.
Trait Implementations§
impl Asset for Mesh
Source§impl From<CircularSector> for Mesh
impl From<CircularSector> for Mesh
Source§fn from(sector: CircularSector) -> Mesh
fn from(sector: CircularSector) -> Mesh
Converts this sector into a Mesh using a default CircularSectorMeshBuilder.
See the documentation of CircularSectorMeshBuilder for more details.
Source§impl From<CircularSegment> for Mesh
impl From<CircularSegment> for Mesh
Source§fn from(segment: CircularSegment) -> Mesh
fn from(segment: CircularSegment) -> Mesh
Converts this sector into a Mesh using a default CircularSegmentMeshBuilder.
See the documentation of CircularSegmentMeshBuilder for more details.
Source§impl From<ConicalFrustum> for Mesh
impl From<ConicalFrustum> for Mesh
Source§fn from(frustum: ConicalFrustum) -> Mesh
fn from(frustum: ConicalFrustum) -> Mesh
Source§impl From<ConvexPolygon> for Mesh
impl From<ConvexPolygon> for Mesh
Source§fn from(polygon: ConvexPolygon) -> Mesh
fn from(polygon: ConvexPolygon) -> Mesh
Source§impl From<Polyline2d> for Mesh
impl From<Polyline2d> for Mesh
Source§fn from(polyline: Polyline2d) -> Mesh
fn from(polyline: Polyline2d) -> Mesh
Source§impl From<Polyline3d> for Mesh
impl From<Polyline3d> for Mesh
Source§fn from(polyline: Polyline3d) -> Mesh
fn from(polyline: Polyline3d) -> Mesh
Source§impl From<RegularPolygon> for Mesh
impl From<RegularPolygon> for Mesh
Source§fn from(polygon: RegularPolygon) -> Mesh
fn from(polygon: RegularPolygon) -> Mesh
Source§impl<T> From<T> for Meshwhere
T: MeshBuilder,
impl<T> From<T> for Meshwhere
T: MeshBuilder,
Source§impl From<Tetrahedron> for Mesh
impl From<Tetrahedron> for Mesh
Source§fn from(tetrahedron: Tetrahedron) -> Mesh
fn from(tetrahedron: Tetrahedron) -> Mesh
Source§impl From<Triangle2d> for Mesh
impl From<Triangle2d> for Mesh
Source§fn from(triangle: Triangle2d) -> Mesh
fn from(triangle: Triangle2d) -> Mesh
Source§impl From<Triangle3d> for Mesh
impl From<Triangle3d> for Mesh
Source§fn from(triangle: Triangle3d) -> Mesh
fn from(triangle: Triangle3d) -> Mesh
Source§impl FromReflect for Mesh
impl FromReflect for Mesh
Source§fn from_reflect(reflect: &(dyn PartialReflect + 'static)) -> Option<Mesh>
fn from_reflect(reflect: &(dyn PartialReflect + 'static)) -> Option<Mesh>
Self from a reflected value.Source§fn take_from_reflect(
reflect: Box<dyn PartialReflect>,
) -> Result<Self, Box<dyn PartialReflect>>
fn take_from_reflect( reflect: Box<dyn PartialReflect>, ) -> Result<Self, Box<dyn PartialReflect>>
Self using,
constructing the value using from_reflect if that fails. Read moreSource§impl GetTypeRegistration for Mesh
impl GetTypeRegistration for Mesh
Source§fn get_type_registration() -> TypeRegistration
fn get_type_registration() -> TypeRegistration
TypeRegistration for this type.Source§fn register_type_dependencies(registry: &mut TypeRegistry)
fn register_type_dependencies(registry: &mut TypeRegistry)
Source§impl IntoReturn for Mesh
impl IntoReturn for Mesh
Source§impl PartialReflect for Mesh
impl PartialReflect for Mesh
Source§fn get_represented_type_info(&self) -> Option<&'static TypeInfo>
fn get_represented_type_info(&self) -> Option<&'static TypeInfo>
Source§fn try_apply(
&mut self,
value: &(dyn PartialReflect + 'static),
) -> Result<(), ApplyError>
fn try_apply( &mut self, value: &(dyn PartialReflect + 'static), ) -> Result<(), ApplyError>
Source§fn reflect_kind(&self) -> ReflectKind
fn reflect_kind(&self) -> ReflectKind
Source§fn reflect_ref(&self) -> ReflectRef<'_>
fn reflect_ref(&self) -> ReflectRef<'_>
Source§fn reflect_mut(&mut self) -> ReflectMut<'_>
fn reflect_mut(&mut self) -> ReflectMut<'_>
Source§fn reflect_owned(self: Box<Mesh>) -> ReflectOwned
fn reflect_owned(self: Box<Mesh>) -> ReflectOwned
Source§fn try_into_reflect(
self: Box<Mesh>,
) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>>
fn try_into_reflect( self: Box<Mesh>, ) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>>
Source§fn try_as_reflect(&self) -> Option<&(dyn Reflect + 'static)>
fn try_as_reflect(&self) -> Option<&(dyn Reflect + 'static)>
Source§fn try_as_reflect_mut(&mut self) -> Option<&mut (dyn Reflect + 'static)>
fn try_as_reflect_mut(&mut self) -> Option<&mut (dyn Reflect + 'static)>
Source§fn into_partial_reflect(self: Box<Mesh>) -> Box<dyn PartialReflect>
fn into_partial_reflect(self: Box<Mesh>) -> Box<dyn PartialReflect>
Source§fn as_partial_reflect(&self) -> &(dyn PartialReflect + 'static)
fn as_partial_reflect(&self) -> &(dyn PartialReflect + 'static)
Source§fn as_partial_reflect_mut(&mut self) -> &mut (dyn PartialReflect + 'static)
fn as_partial_reflect_mut(&mut self) -> &mut (dyn PartialReflect + 'static)
Source§fn reflect_partial_eq(
&self,
value: &(dyn PartialReflect + 'static),
) -> Option<bool>
fn reflect_partial_eq( &self, value: &(dyn PartialReflect + 'static), ) -> Option<bool>
Source§fn reflect_partial_cmp(
&self,
value: &(dyn PartialReflect + 'static),
) -> Option<Ordering>
fn reflect_partial_cmp( &self, value: &(dyn PartialReflect + 'static), ) -> Option<Ordering>
Source§fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError>
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError>
Self using reflection. Read moreSource§fn apply(&mut self, value: &(dyn PartialReflect + 'static))
fn apply(&mut self, value: &(dyn PartialReflect + 'static))
Source§fn to_dynamic(&self) -> Box<dyn PartialReflect>
fn to_dynamic(&self) -> Box<dyn PartialReflect>
Source§fn reflect_clone_and_take<T>(&self) -> Result<T, ReflectCloneError>
fn reflect_clone_and_take<T>(&self) -> Result<T, ReflectCloneError>
PartialReflect, combines reflect_clone and
take in a useful fashion, automatically constructing an appropriate
ReflectCloneError if the downcast fails.Source§fn reflect_hash(&self) -> Option<u64>
fn reflect_hash(&self) -> Option<u64>
Source§fn debug(&self, f: &mut Formatter<'_>) -> Result<(), Error>
fn debug(&self, f: &mut Formatter<'_>) -> Result<(), Error>
Source§fn is_dynamic(&self) -> bool
fn is_dynamic(&self) -> bool
Source§impl Reflect for Mesh
impl Reflect for Mesh
Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut dyn Any. Read moreSource§fn into_reflect(self: Box<Mesh>) -> Box<dyn Reflect>
fn into_reflect(self: Box<Mesh>) -> Box<dyn Reflect>
Source§fn as_reflect(&self) -> &(dyn Reflect + 'static)
fn as_reflect(&self) -> &(dyn Reflect + 'static)
Source§fn as_reflect_mut(&mut self) -> &mut (dyn Reflect + 'static)
fn as_reflect_mut(&mut self) -> &mut (dyn Reflect + 'static)
Source§impl Struct for Mesh
impl Struct for Mesh
Source§fn field(&self, name: &str) -> Option<&(dyn PartialReflect + 'static)>
fn field(&self, name: &str) -> Option<&(dyn PartialReflect + 'static)>
name as a &dyn PartialReflect.Source§fn field_mut(
&mut self,
name: &str,
) -> Option<&mut (dyn PartialReflect + 'static)>
fn field_mut( &mut self, name: &str, ) -> Option<&mut (dyn PartialReflect + 'static)>
name as a
&mut dyn PartialReflect.Source§fn field_at(&self, index: usize) -> Option<&(dyn PartialReflect + 'static)>
fn field_at(&self, index: usize) -> Option<&(dyn PartialReflect + 'static)>
index as a
&dyn PartialReflect.Source§fn field_at_mut(
&mut self,
index: usize,
) -> Option<&mut (dyn PartialReflect + 'static)>
fn field_at_mut( &mut self, index: usize, ) -> Option<&mut (dyn PartialReflect + 'static)>
index
as a &mut dyn PartialReflect.Source§fn index_of_name(&self, name: &str) -> Option<usize>
fn index_of_name(&self, name: &str) -> Option<usize>
Source§fn iter_fields(&self) -> FieldIter<'_> ⓘ
fn iter_fields(&self) -> FieldIter<'_> ⓘ
Source§fn to_dynamic_struct(&self) -> DynamicStruct
fn to_dynamic_struct(&self) -> DynamicStruct
DynamicStruct from this struct.Source§fn get_represented_struct_info(&self) -> Option<&'static StructInfo>
fn get_represented_struct_info(&self) -> Option<&'static StructInfo>
None if TypeInfo is not available.impl StructuralPartialEq for Mesh
Source§impl TypePath for Mesh
impl TypePath for Mesh
Source§fn type_path() -> &'static str
fn type_path() -> &'static str
Source§fn short_type_path() -> &'static str
fn short_type_path() -> &'static str
Source§fn type_ident() -> Option<&'static str>
fn type_ident() -> Option<&'static str>
Source§fn crate_name() -> Option<&'static str>
fn crate_name() -> Option<&'static str>
Source§impl VisitAssetDependencies for Mesh
impl VisitAssetDependencies for Mesh
fn visit_dependencies(&self, visit: &mut impl FnMut(UntypedAssetId))
Auto Trait Implementations§
impl Freeze for Mesh
impl RefUnwindSafe for Mesh
impl Send for Mesh
impl Sync for Mesh
impl Unpin for Mesh
impl UnsafeUnpin for Mesh
impl UnwindSafe for Mesh
Blanket Implementations§
Source§impl<T, U> AsBindGroupShaderType<U> for T
impl<T, U> AsBindGroupShaderType<U> for T
Source§fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
T ShaderType for self. When used in AsBindGroup
derives, it is safe to assume that all images in self exist.Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> ConditionalSend for Twhere
T: Send,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
Source§impl<T> DynamicTypePath for Twhere
T: TypePath,
impl<T> DynamicTypePath for Twhere
T: TypePath,
Source§fn reflect_type_path(&self) -> &str
fn reflect_type_path(&self) -> &str
TypePath::type_path.Source§fn reflect_short_type_path(&self) -> &str
fn reflect_short_type_path(&self) -> &str
Source§fn reflect_type_ident(&self) -> Option<&str>
fn reflect_type_ident(&self) -> Option<&str>
TypePath::type_ident.Source§fn reflect_crate_name(&self) -> Option<&str>
fn reflect_crate_name(&self) -> Option<&str>
TypePath::crate_name.Source§fn reflect_module_path(&self) -> Option<&str>
fn reflect_module_path(&self) -> Option<&str>
Source§impl<T> DynamicTyped for Twhere
T: Typed,
impl<T> DynamicTyped for Twhere
T: Typed,
Source§fn reflect_type_info(&self) -> &'static TypeInfo
fn reflect_type_info(&self) -> &'static TypeInfo
Typed::type_info.impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<S> GetField for Swhere
S: Struct,
impl<S> GetField for Swhere
S: Struct,
Source§impl<T> GetPath for T
impl<T> GetPath for T
Source§fn reflect_path<'p>(
&self,
path: impl ReflectPath<'p>,
) -> Result<&(dyn PartialReflect + 'static), ReflectPathError<'p>>
fn reflect_path<'p>( &self, path: impl ReflectPath<'p>, ) -> Result<&(dyn PartialReflect + 'static), ReflectPathError<'p>>
path. Read moreSource§fn reflect_path_mut<'p>(
&mut self,
path: impl ReflectPath<'p>,
) -> Result<&mut (dyn PartialReflect + 'static), ReflectPathError<'p>>
fn reflect_path_mut<'p>( &mut self, path: impl ReflectPath<'p>, ) -> Result<&mut (dyn PartialReflect + 'static), ReflectPathError<'p>>
path. Read moreSource§fn path<'p, T>(
&self,
path: impl ReflectPath<'p>,
) -> Result<&T, ReflectPathError<'p>>where
T: Reflect,
fn path<'p, T>(
&self,
path: impl ReflectPath<'p>,
) -> Result<&T, ReflectPathError<'p>>where
T: Reflect,
path. Read moreSource§fn path_mut<'p, T>(
&mut self,
path: impl ReflectPath<'p>,
) -> Result<&mut T, ReflectPathError<'p>>where
T: Reflect,
fn path_mut<'p, T>(
&mut self,
path: impl ReflectPath<'p>,
) -> Result<&mut T, ReflectPathError<'p>>where
T: Reflect,
path. Read moreSource§impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T> HitDataExtra for T
Source§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
Source§impl<T> InitializeFromFunction<T> for T
impl<T> InitializeFromFunction<T> for T
Source§fn initialize_from_function(f: fn() -> T) -> T
fn initialize_from_function(f: fn() -> T) -> T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoResult<T> for T
impl<T> IntoResult<T> for T
Source§fn into_result(self) -> Result<T, RunSystemError>
fn into_result(self) -> Result<T, RunSystemError>
Source§impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
fn into_sample(self) -> T
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
impl<T> Reflectable for T
impl<T> Settings for T
Source§impl<Ret> SpawnIfAsync<(), Ret> for Ret
impl<Ret> SpawnIfAsync<(), Ret> for Ret
Source§impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
Source§fn super_from(input: T) -> O
fn super_from(input: T) -> O
Source§impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
Source§fn super_into(self) -> O
fn super_into(self) -> O
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.