pub struct Mesh {
pub asset_usage: RenderAssetUsages,
pub enable_raytracing: bool,
/* 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).
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 = 8u64
pub const FIRST_AVAILABLE_CUSTOM_ATTRIBUTE: u64 = 8u64
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?
96 fn from(line: LineList) -> Self {
97 let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect();
98
99 Mesh::new(
100 // This tells wgpu that the positions are list of lines
101 // where every pair is a start and end point
102 PrimitiveTopology::LineList,
103 RenderAssetUsages::RENDER_WORLD,
104 )
105 // Add the vertices positions as an attribute
106 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
107 }
108}
109
110/// A list of points that will have a line drawn between each consecutive points
111#[derive(Debug, Clone)]
112struct LineStrip {
113 points: Vec<Vec3>,
114}
115
116impl From<LineStrip> for Mesh {
117 fn from(line: LineStrip) -> Self {
118 Mesh::new(
119 // This tells wgpu that the positions are a list of points
120 // where a line will be drawn between each consecutive point
121 PrimitiveTopology::LineStrip,
122 RenderAssetUsages::RENDER_WORLD,
123 )
124 // Add the point positions as an attribute
125 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, line.points)
126 }More examples
52fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
53 // Build a custom triangle mesh with colors
54 // We define a custom mesh because the examples only uses a limited
55 // set of vertex attributes for simplicity
56 let mesh = Mesh::new(
57 PrimitiveTopology::TriangleList,
58 RenderAssetUsages::default(),
59 )
60 .with_inserted_indices(Indices::U32(vec![0, 1, 2]))
61 .with_inserted_attribute(
62 Mesh::ATTRIBUTE_POSITION,
63 vec![
64 vec3(-0.5, -0.5, 0.0),
65 vec3(0.5, -0.5, 0.0),
66 vec3(0.0, 0.25, 0.0),
67 ],
68 )
69 .with_inserted_attribute(
70 Mesh::ATTRIBUTE_COLOR,
71 vec![
72 vec4(1.0, 0.0, 0.0, 1.0),
73 vec4(0.0, 1.0, 0.0, 1.0),
74 vec4(0.0, 0.0, 1.0, 1.0),
75 ],
76 );
77
78 // spawn 3 triangles to show that batching works
79 for (x, y) in [-0.5, 0.0, 0.5].into_iter().zip([-0.25, 0.5, -0.25]) {
80 // Spawn an entity with all the required components for it to be rendered with our custom pipeline
81 commands.spawn((
82 // We use a marker component to identify the mesh that will be rendered
83 // with our specialized pipeline
84 CustomRenderedEntity,
85 // We need to add the mesh handle to the entity
86 Mesh3d(meshes.add(mesh.clone())),
87 Transform::from_xyz(x, y, 0.0),
88 ));
89 }
90
91 // Spawn the camera.
92 commands.spawn((
93 Camera3d::default(),
94 // Move the camera back a bit to see all the triangles
95 Transform::from_xyz(0.0, 0.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
96 ));
97}400 fn build(&self) -> Mesh {
401 let radius = self.heart.radius;
402 // The curved parts of each wing (half) of the heart have an angle of `PI * 1.25` or 225°
403 let wing_angle = PI * 1.25;
404
405 // We create buffers for the vertices, their normals and UVs, as well as the indices used to connect the vertices.
406 let mut vertices = Vec::with_capacity(2 * self.resolution);
407 let mut uvs = Vec::with_capacity(2 * self.resolution);
408 let mut indices = Vec::with_capacity(6 * self.resolution - 9);
409 // Since the heart is flat, we know all the normals are identical already.
410 let normals = vec![[0f32, 0f32, 1f32]; 2 * self.resolution];
411
412 // The point in the middle of the two curved parts of the heart
413 vertices.push([0.0; 3]);
414 uvs.push([0.5, 0.5]);
415
416 // The left wing of the heart, starting from the point in the middle.
417 for i in 1..self.resolution {
418 let angle = (i as f32 / self.resolution as f32) * wing_angle;
419 let (sin, cos) = ops::sin_cos(angle);
420 vertices.push([radius * (cos - 1.0), radius * sin, 0.0]);
421 uvs.push([0.5 - (cos - 1.0) / 4., 0.5 - sin / 2.]);
422 }
423
424 // The bottom tip of the heart
425 vertices.push([0.0, radius * (-1. - SQRT_2), 0.0]);
426 uvs.push([0.5, 1.]);
427
428 // The right wing of the heart, starting from the bottom most point and going towards the middle point.
429 for i in 0..self.resolution - 1 {
430 let angle = (i as f32 / self.resolution as f32) * wing_angle - PI / 4.;
431 let (sin, cos) = ops::sin_cos(angle);
432 vertices.push([radius * (cos + 1.0), radius * sin, 0.0]);
433 uvs.push([0.5 - (cos + 1.0) / 4., 0.5 - sin / 2.]);
434 }
435
436 // This is where we build all the triangles from the points created above.
437 // Each triangle has one corner on the middle point with the other two being adjacent points on the perimeter of the heart.
438 for i in 2..2 * self.resolution as u32 {
439 indices.extend_from_slice(&[i - 1, i, 0]);
440 }
441
442 // Here, the actual `Mesh` is created. We set the indices, vertices, normals and UVs created above and specify the topology of the mesh.
443 Mesh::new(
444 bevy::mesh::PrimitiveTopology::TriangleList,
445 RenderAssetUsages::default(),
446 )
447 .with_inserted_indices(bevy::mesh::Indices::U32(indices))
448 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
449 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
450 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
451 }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}37fn setup(
38 mut commands: Commands,
39 asset_server: Res<AssetServer>,
40 mut meshes: ResMut<Assets<Mesh>>,
41 mut materials: ResMut<Assets<StandardMaterial>>,
42 mut skinned_mesh_inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
43) {
44 // Create a camera
45 commands.spawn((
46 Camera3d::default(),
47 Transform::from_xyz(2.5, 2.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
48 ));
49
50 // Create inverse bindpose matrices for a skeleton consists of 2 joints
51 let inverse_bindposes = skinned_mesh_inverse_bindposes_assets.add(vec![
52 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
53 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
54 ]);
55
56 // Create a mesh
57 let mesh = Mesh::new(
58 PrimitiveTopology::TriangleList,
59 RenderAssetUsages::RENDER_WORLD,
60 )
61 // Set mesh vertex positions
62 .with_inserted_attribute(
63 Mesh::ATTRIBUTE_POSITION,
64 vec![
65 [0.0, 0.0, 0.0],
66 [1.0, 0.0, 0.0],
67 [0.0, 0.5, 0.0],
68 [1.0, 0.5, 0.0],
69 [0.0, 1.0, 0.0],
70 [1.0, 1.0, 0.0],
71 [0.0, 1.5, 0.0],
72 [1.0, 1.5, 0.0],
73 [0.0, 2.0, 0.0],
74 [1.0, 2.0, 0.0],
75 ],
76 )
77 // Add UV coordinates that map the left half of the texture since its a 1 x
78 // 2 rectangle.
79 .with_inserted_attribute(
80 Mesh::ATTRIBUTE_UV_0,
81 vec![
82 [0.0, 0.00],
83 [0.5, 0.00],
84 [0.0, 0.25],
85 [0.5, 0.25],
86 [0.0, 0.50],
87 [0.5, 0.50],
88 [0.0, 0.75],
89 [0.5, 0.75],
90 [0.0, 1.00],
91 [0.5, 1.00],
92 ],
93 )
94 // Set mesh vertex normals
95 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10])
96 // Set mesh vertex joint indices for mesh skinning.
97 // Each vertex gets 4 indices used to address the `JointTransforms` array in the vertex shader
98 // as well as `SkinnedMeshJoint` array in the `SkinnedMesh` component.
99 // This means that a maximum of 4 joints can affect a single vertex.
100 .with_inserted_attribute(
101 Mesh::ATTRIBUTE_JOINT_INDEX,
102 // Need to be explicit here as [u16; 4] could be either Uint16x4 or Unorm16x4.
103 VertexAttributeValues::Uint16x4(vec![
104 [0, 0, 0, 0],
105 [0, 0, 0, 0],
106 [0, 1, 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 ]),
115 )
116 // Set mesh vertex joint weights for mesh skinning.
117 // Each vertex gets 4 joint weights corresponding to the 4 joint indices assigned to it.
118 // The sum of these weights should equal to 1.
119 .with_inserted_attribute(
120 Mesh::ATTRIBUTE_JOINT_WEIGHT,
121 vec![
122 [1.00, 0.00, 0.0, 0.0],
123 [1.00, 0.00, 0.0, 0.0],
124 [0.75, 0.25, 0.0, 0.0],
125 [0.75, 0.25, 0.0, 0.0],
126 [0.50, 0.50, 0.0, 0.0],
127 [0.50, 0.50, 0.0, 0.0],
128 [0.25, 0.75, 0.0, 0.0],
129 [0.25, 0.75, 0.0, 0.0],
130 [0.00, 1.00, 0.0, 0.0],
131 [0.00, 1.00, 0.0, 0.0],
132 ],
133 )
134 // Tell bevy to construct triangles from a list of vertex indices,
135 // where each 3 vertex indices form a triangle.
136 .with_inserted_indices(Indices::U16(vec![
137 0, 1, 3, 0, 3, 2, 2, 3, 5, 2, 5, 4, 4, 5, 7, 4, 7, 6, 6, 7, 9, 6, 9, 8,
138 ]));
139
140 let mesh = meshes.add(mesh);
141
142 // We're seeding the PRNG here to make this example deterministic for testing purposes.
143 // This isn't strictly required in practical use unless you need your app to be deterministic.
144 let mut rng = ChaCha8Rng::seed_from_u64(42);
145
146 for i in -5..5 {
147 // Create joint entities
148 let joint_0 = commands
149 .spawn(Transform::from_xyz(
150 i as f32 * 1.5,
151 0.0,
152 // Move quads back a small amount to avoid Z-fighting and not
153 // obscure the transform gizmos.
154 -(i as f32 * 0.01).abs(),
155 ))
156 .id();
157 let joint_1 = commands.spawn((AnimatedJoint(i), Transform::IDENTITY)).id();
158
159 // Set joint_1 as a child of joint_0.
160 commands.entity(joint_0).add_children(&[joint_1]);
161
162 // Each joint in this vector corresponds to each inverse bindpose matrix in `SkinnedMeshInverseBindposes`.
163 let joint_entities = vec![joint_0, joint_1];
164
165 // Create skinned mesh renderer. Note that its transform doesn't affect the position of the mesh.
166 commands.spawn((
167 Mesh3d(mesh.clone()),
168 MeshMaterial3d(materials.add(StandardMaterial {
169 base_color: Color::srgb(
170 rng.random_range(0.0..1.0),
171 rng.random_range(0.0..1.0),
172 rng.random_range(0.0..1.0),
173 ),
174 base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
175 ..default()
176 })),
177 SkinnedMesh {
178 inverse_bindposes: inverse_bindposes.clone(),
179 joints: joint_entities,
180 },
181 ));
182 }
183}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.
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 shadows_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(AmbientLight {
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 shadows_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}161fn add_raytracing_meshes_on_scene_load(
162 scene_ready: On<SceneInstanceReady>,
163 children: Query<&Children>,
164 mesh_query: Query<(
165 &Mesh3d,
166 &MeshMaterial3d<StandardMaterial>,
167 Option<&GltfMaterialName>,
168 )>,
169 mut meshes: ResMut<Assets<Mesh>>,
170 mut materials: ResMut<Assets<StandardMaterial>>,
171 mut commands: Commands,
172 args: Res<Args>,
173) {
174 for descendant in children.iter_descendants(scene_ready.entity) {
175 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
176 mesh_query.get(descendant)
177 {
178 // Add raytracing mesh component
179 commands
180 .entity(descendant)
181 .insert(RaytracingMesh3d(mesh_handle.clone()));
182
183 // Ensure meshes are Solari compatible
184 let mesh = meshes.get_mut(mesh_handle).unwrap();
185 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
186 let vertex_count = mesh.count_vertices();
187 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
188 mesh.insert_attribute(
189 Mesh::ATTRIBUTE_TANGENT,
190 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
191 );
192 }
193 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
194 mesh.generate_tangents().unwrap();
195 }
196 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
197 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
198 }
199
200 // Prevent rasterization if using pathtracer
201 if args.pathtracer == Some(true) {
202 commands.entity(descendant).remove::<Mesh3d>();
203 }
204
205 // Adjust scene materials to better demo Solari features
206 if material_name.map(|s| s.0.as_str()) == Some("material") {
207 let material = materials.get_mut(material_handle).unwrap();
208 material.emissive = LinearRgba::BLACK;
209 }
210 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
211 let material = materials.get_mut(material_handle).unwrap();
212 material.emissive =
213 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
214 material.alpha_mode = AlphaMode::Opaque;
215 material.specular_transmission = 0.0;
216
217 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
218 }
219 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
220 let material = materials.get_mut(material_handle).unwrap();
221 material.alpha_mode = AlphaMode::Opaque;
222 material.specular_transmission = 0.0;
223 }
224 }
225 }
226}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 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.
Examples found in repository?
96 fn from(line: LineList) -> Self {
97 let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect();
98
99 Mesh::new(
100 // This tells wgpu that the positions are list of lines
101 // where every pair is a start and end point
102 PrimitiveTopology::LineList,
103 RenderAssetUsages::RENDER_WORLD,
104 )
105 // Add the vertices positions as an attribute
106 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
107 }
108}
109
110/// A list of points that will have a line drawn between each consecutive points
111#[derive(Debug, Clone)]
112struct LineStrip {
113 points: Vec<Vec3>,
114}
115
116impl From<LineStrip> for Mesh {
117 fn from(line: LineStrip) -> Self {
118 Mesh::new(
119 // This tells wgpu that the positions are a list of points
120 // where a line will be drawn between each consecutive point
121 PrimitiveTopology::LineStrip,
122 RenderAssetUsages::RENDER_WORLD,
123 )
124 // Add the point positions as an attribute
125 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, line.points)
126 }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}52fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
53 // Build a custom triangle mesh with colors
54 // We define a custom mesh because the examples only uses a limited
55 // set of vertex attributes for simplicity
56 let mesh = Mesh::new(
57 PrimitiveTopology::TriangleList,
58 RenderAssetUsages::default(),
59 )
60 .with_inserted_indices(Indices::U32(vec![0, 1, 2]))
61 .with_inserted_attribute(
62 Mesh::ATTRIBUTE_POSITION,
63 vec![
64 vec3(-0.5, -0.5, 0.0),
65 vec3(0.5, -0.5, 0.0),
66 vec3(0.0, 0.25, 0.0),
67 ],
68 )
69 .with_inserted_attribute(
70 Mesh::ATTRIBUTE_COLOR,
71 vec![
72 vec4(1.0, 0.0, 0.0, 1.0),
73 vec4(0.0, 1.0, 0.0, 1.0),
74 vec4(0.0, 0.0, 1.0, 1.0),
75 ],
76 );
77
78 // spawn 3 triangles to show that batching works
79 for (x, y) in [-0.5, 0.0, 0.5].into_iter().zip([-0.25, 0.5, -0.25]) {
80 // Spawn an entity with all the required components for it to be rendered with our custom pipeline
81 commands.spawn((
82 // We use a marker component to identify the mesh that will be rendered
83 // with our specialized pipeline
84 CustomRenderedEntity,
85 // We need to add the mesh handle to the entity
86 Mesh3d(meshes.add(mesh.clone())),
87 Transform::from_xyz(x, y, 0.0),
88 ));
89 }
90
91 // Spawn the camera.
92 commands.spawn((
93 Camera3d::default(),
94 // Move the camera back a bit to see all the triangles
95 Transform::from_xyz(0.0, 0.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
96 ));
97}400 fn build(&self) -> Mesh {
401 let radius = self.heart.radius;
402 // The curved parts of each wing (half) of the heart have an angle of `PI * 1.25` or 225°
403 let wing_angle = PI * 1.25;
404
405 // We create buffers for the vertices, their normals and UVs, as well as the indices used to connect the vertices.
406 let mut vertices = Vec::with_capacity(2 * self.resolution);
407 let mut uvs = Vec::with_capacity(2 * self.resolution);
408 let mut indices = Vec::with_capacity(6 * self.resolution - 9);
409 // Since the heart is flat, we know all the normals are identical already.
410 let normals = vec![[0f32, 0f32, 1f32]; 2 * self.resolution];
411
412 // The point in the middle of the two curved parts of the heart
413 vertices.push([0.0; 3]);
414 uvs.push([0.5, 0.5]);
415
416 // The left wing of the heart, starting from the point in the middle.
417 for i in 1..self.resolution {
418 let angle = (i as f32 / self.resolution as f32) * wing_angle;
419 let (sin, cos) = ops::sin_cos(angle);
420 vertices.push([radius * (cos - 1.0), radius * sin, 0.0]);
421 uvs.push([0.5 - (cos - 1.0) / 4., 0.5 - sin / 2.]);
422 }
423
424 // The bottom tip of the heart
425 vertices.push([0.0, radius * (-1. - SQRT_2), 0.0]);
426 uvs.push([0.5, 1.]);
427
428 // The right wing of the heart, starting from the bottom most point and going towards the middle point.
429 for i in 0..self.resolution - 1 {
430 let angle = (i as f32 / self.resolution as f32) * wing_angle - PI / 4.;
431 let (sin, cos) = ops::sin_cos(angle);
432 vertices.push([radius * (cos + 1.0), radius * sin, 0.0]);
433 uvs.push([0.5 - (cos + 1.0) / 4., 0.5 - sin / 2.]);
434 }
435
436 // This is where we build all the triangles from the points created above.
437 // Each triangle has one corner on the middle point with the other two being adjacent points on the perimeter of the heart.
438 for i in 2..2 * self.resolution as u32 {
439 indices.extend_from_slice(&[i - 1, i, 0]);
440 }
441
442 // Here, the actual `Mesh` is created. We set the indices, vertices, normals and UVs created above and specify the topology of the mesh.
443 Mesh::new(
444 bevy::mesh::PrimitiveTopology::TriangleList,
445 RenderAssetUsages::default(),
446 )
447 .with_inserted_indices(bevy::mesh::Indices::U32(indices))
448 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
449 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
450 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
451 }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}37fn setup(
38 mut commands: Commands,
39 asset_server: Res<AssetServer>,
40 mut meshes: ResMut<Assets<Mesh>>,
41 mut materials: ResMut<Assets<StandardMaterial>>,
42 mut skinned_mesh_inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
43) {
44 // Create a camera
45 commands.spawn((
46 Camera3d::default(),
47 Transform::from_xyz(2.5, 2.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
48 ));
49
50 // Create inverse bindpose matrices for a skeleton consists of 2 joints
51 let inverse_bindposes = skinned_mesh_inverse_bindposes_assets.add(vec![
52 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
53 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
54 ]);
55
56 // Create a mesh
57 let mesh = Mesh::new(
58 PrimitiveTopology::TriangleList,
59 RenderAssetUsages::RENDER_WORLD,
60 )
61 // Set mesh vertex positions
62 .with_inserted_attribute(
63 Mesh::ATTRIBUTE_POSITION,
64 vec![
65 [0.0, 0.0, 0.0],
66 [1.0, 0.0, 0.0],
67 [0.0, 0.5, 0.0],
68 [1.0, 0.5, 0.0],
69 [0.0, 1.0, 0.0],
70 [1.0, 1.0, 0.0],
71 [0.0, 1.5, 0.0],
72 [1.0, 1.5, 0.0],
73 [0.0, 2.0, 0.0],
74 [1.0, 2.0, 0.0],
75 ],
76 )
77 // Add UV coordinates that map the left half of the texture since its a 1 x
78 // 2 rectangle.
79 .with_inserted_attribute(
80 Mesh::ATTRIBUTE_UV_0,
81 vec![
82 [0.0, 0.00],
83 [0.5, 0.00],
84 [0.0, 0.25],
85 [0.5, 0.25],
86 [0.0, 0.50],
87 [0.5, 0.50],
88 [0.0, 0.75],
89 [0.5, 0.75],
90 [0.0, 1.00],
91 [0.5, 1.00],
92 ],
93 )
94 // Set mesh vertex normals
95 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10])
96 // Set mesh vertex joint indices for mesh skinning.
97 // Each vertex gets 4 indices used to address the `JointTransforms` array in the vertex shader
98 // as well as `SkinnedMeshJoint` array in the `SkinnedMesh` component.
99 // This means that a maximum of 4 joints can affect a single vertex.
100 .with_inserted_attribute(
101 Mesh::ATTRIBUTE_JOINT_INDEX,
102 // Need to be explicit here as [u16; 4] could be either Uint16x4 or Unorm16x4.
103 VertexAttributeValues::Uint16x4(vec![
104 [0, 0, 0, 0],
105 [0, 0, 0, 0],
106 [0, 1, 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 ]),
115 )
116 // Set mesh vertex joint weights for mesh skinning.
117 // Each vertex gets 4 joint weights corresponding to the 4 joint indices assigned to it.
118 // The sum of these weights should equal to 1.
119 .with_inserted_attribute(
120 Mesh::ATTRIBUTE_JOINT_WEIGHT,
121 vec![
122 [1.00, 0.00, 0.0, 0.0],
123 [1.00, 0.00, 0.0, 0.0],
124 [0.75, 0.25, 0.0, 0.0],
125 [0.75, 0.25, 0.0, 0.0],
126 [0.50, 0.50, 0.0, 0.0],
127 [0.50, 0.50, 0.0, 0.0],
128 [0.25, 0.75, 0.0, 0.0],
129 [0.25, 0.75, 0.0, 0.0],
130 [0.00, 1.00, 0.0, 0.0],
131 [0.00, 1.00, 0.0, 0.0],
132 ],
133 )
134 // Tell bevy to construct triangles from a list of vertex indices,
135 // where each 3 vertex indices form a triangle.
136 .with_inserted_indices(Indices::U16(vec![
137 0, 1, 3, 0, 3, 2, 2, 3, 5, 2, 5, 4, 4, 5, 7, 4, 7, 6, 6, 7, 9, 6, 9, 8,
138 ]));
139
140 let mesh = meshes.add(mesh);
141
142 // We're seeding the PRNG here to make this example deterministic for testing purposes.
143 // This isn't strictly required in practical use unless you need your app to be deterministic.
144 let mut rng = ChaCha8Rng::seed_from_u64(42);
145
146 for i in -5..5 {
147 // Create joint entities
148 let joint_0 = commands
149 .spawn(Transform::from_xyz(
150 i as f32 * 1.5,
151 0.0,
152 // Move quads back a small amount to avoid Z-fighting and not
153 // obscure the transform gizmos.
154 -(i as f32 * 0.01).abs(),
155 ))
156 .id();
157 let joint_1 = commands.spawn((AnimatedJoint(i), Transform::IDENTITY)).id();
158
159 // Set joint_1 as a child of joint_0.
160 commands.entity(joint_0).add_children(&[joint_1]);
161
162 // Each joint in this vector corresponds to each inverse bindpose matrix in `SkinnedMeshInverseBindposes`.
163 let joint_entities = vec![joint_0, joint_1];
164
165 // Create skinned mesh renderer. Note that its transform doesn't affect the position of the mesh.
166 commands.spawn((
167 Mesh3d(mesh.clone()),
168 MeshMaterial3d(materials.add(StandardMaterial {
169 base_color: Color::srgb(
170 rng.random_range(0.0..1.0),
171 rng.random_range(0.0..1.0),
172 rng.random_range(0.0..1.0),
173 ),
174 base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
175 ..default()
176 })),
177 SkinnedMesh {
178 inverse_bindposes: inverse_bindposes.clone(),
179 joints: joint_entities,
180 },
181 ));
182 }
183}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
Examples found in repository?
161fn add_raytracing_meshes_on_scene_load(
162 scene_ready: On<SceneInstanceReady>,
163 children: Query<&Children>,
164 mesh_query: Query<(
165 &Mesh3d,
166 &MeshMaterial3d<StandardMaterial>,
167 Option<&GltfMaterialName>,
168 )>,
169 mut meshes: ResMut<Assets<Mesh>>,
170 mut materials: ResMut<Assets<StandardMaterial>>,
171 mut commands: Commands,
172 args: Res<Args>,
173) {
174 for descendant in children.iter_descendants(scene_ready.entity) {
175 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
176 mesh_query.get(descendant)
177 {
178 // Add raytracing mesh component
179 commands
180 .entity(descendant)
181 .insert(RaytracingMesh3d(mesh_handle.clone()));
182
183 // Ensure meshes are Solari compatible
184 let mesh = meshes.get_mut(mesh_handle).unwrap();
185 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
186 let vertex_count = mesh.count_vertices();
187 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
188 mesh.insert_attribute(
189 Mesh::ATTRIBUTE_TANGENT,
190 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
191 );
192 }
193 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
194 mesh.generate_tangents().unwrap();
195 }
196 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
197 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
198 }
199
200 // Prevent rasterization if using pathtracer
201 if args.pathtracer == Some(true) {
202 commands.entity(descendant).remove::<Mesh3d>();
203 }
204
205 // Adjust scene materials to better demo Solari features
206 if material_name.map(|s| s.0.as_str()) == Some("material") {
207 let material = materials.get_mut(material_handle).unwrap();
208 material.emissive = LinearRgba::BLACK;
209 }
210 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
211 let material = materials.get_mut(material_handle).unwrap();
212 material.emissive =
213 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
214 material.alpha_mode = AlphaMode::Opaque;
215 material.specular_transmission = 0.0;
216
217 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
218 }
219 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
220 let material = materials.get_mut(material_handle).unwrap();
221 material.alpha_mode = AlphaMode::Opaque;
222 material.specular_transmission = 0.0;
223 }
224 }
225 }
226}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)
Sourcepub fn contains_attribute(&self, id: impl Into<MeshVertexAttributeId>) -> bool
pub fn contains_attribute(&self, id: impl Into<MeshVertexAttributeId>) -> bool
Examples found in repository?
161fn add_raytracing_meshes_on_scene_load(
162 scene_ready: On<SceneInstanceReady>,
163 children: Query<&Children>,
164 mesh_query: Query<(
165 &Mesh3d,
166 &MeshMaterial3d<StandardMaterial>,
167 Option<&GltfMaterialName>,
168 )>,
169 mut meshes: ResMut<Assets<Mesh>>,
170 mut materials: ResMut<Assets<StandardMaterial>>,
171 mut commands: Commands,
172 args: Res<Args>,
173) {
174 for descendant in children.iter_descendants(scene_ready.entity) {
175 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
176 mesh_query.get(descendant)
177 {
178 // Add raytracing mesh component
179 commands
180 .entity(descendant)
181 .insert(RaytracingMesh3d(mesh_handle.clone()));
182
183 // Ensure meshes are Solari compatible
184 let mesh = meshes.get_mut(mesh_handle).unwrap();
185 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
186 let vertex_count = mesh.count_vertices();
187 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
188 mesh.insert_attribute(
189 Mesh::ATTRIBUTE_TANGENT,
190 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
191 );
192 }
193 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
194 mesh.generate_tangents().unwrap();
195 }
196 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
197 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
198 }
199
200 // Prevent rasterization if using pathtracer
201 if args.pathtracer == Some(true) {
202 commands.entity(descendant).remove::<Mesh3d>();
203 }
204
205 // Adjust scene materials to better demo Solari features
206 if material_name.map(|s| s.0.as_str()) == Some("material") {
207 let material = materials.get_mut(material_handle).unwrap();
208 material.emissive = LinearRgba::BLACK;
209 }
210 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
211 let material = materials.get_mut(material_handle).unwrap();
212 material.emissive =
213 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
214 material.alpha_mode = AlphaMode::Opaque;
215 material.specular_transmission = 0.0;
216
217 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
218 }
219 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
220 let material = materials.get_mut(material_handle).unwrap();
221 material.alpha_mode = AlphaMode::Opaque;
222 material.specular_transmission = 0.0;
223 }
224 }
225 }
226}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.
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 shadows_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
281fn spawn_small_cubes(
282 commands: &mut Commands,
283 meshes: &mut Assets<Mesh>,
284 materials: &mut Assets<StandardMaterial>,
285) {
286 // Add the cube mesh.
287 let small_cube = meshes.add(Cuboid::new(
288 SMALL_CUBE_SIZE,
289 SMALL_CUBE_SIZE,
290 SMALL_CUBE_SIZE,
291 ));
292
293 // Add the cube material.
294 let small_cube_material = materials.add(StandardMaterial {
295 base_color: SILVER.into(),
296 ..default()
297 });
298
299 // Create the entity that the small cubes will be parented to. This is the
300 // entity that we rotate.
301 let sphere_parent = commands
302 .spawn(Transform::from_translation(Vec3::ZERO))
303 .insert(Visibility::default())
304 .insert(SphereParent)
305 .id();
306
307 // Now we have to figure out where to place the cubes. To do that, we create
308 // a sphere mesh, but we don't add it to the scene. Instead, we inspect the
309 // sphere mesh to find the positions of its vertices, and spawn a small cube
310 // at each one. That way, we end up with a bunch of cubes arranged in a
311 // spherical shape.
312
313 // Create the sphere mesh, and extract the positions of its vertices.
314 let sphere = Sphere::new(OUTER_RADIUS)
315 .mesh()
316 .ico(OUTER_SUBDIVISION_COUNT)
317 .unwrap();
318 let sphere_positions = sphere.attribute(Mesh::ATTRIBUTE_POSITION).unwrap();
319
320 // At each vertex, create a small cube.
321 for sphere_position in sphere_positions.as_float3().unwrap() {
322 let sphere_position = Vec3::from_slice(sphere_position);
323 let small_cube = commands
324 .spawn(Mesh3d(small_cube.clone()))
325 .insert(MeshMaterial3d(small_cube_material.clone()))
326 .insert(Transform::from_translation(sphere_position))
327 .id();
328 commands.entity(sphere_parent).add_child(small_cube);
329 }
330}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.
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(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(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}171fn alter_mesh(
172 mut is_mesh_scaled: Local<bool>,
173 left_shape: Single<&Mesh3d, With<Left>>,
174 mut meshes: ResMut<Assets<Mesh>>,
175) {
176 // Obtain a mutable reference to the Mesh asset.
177 let Some(mesh) = meshes.get_mut(*left_shape) else {
178 return;
179 };
180
181 // Now we can directly manipulate vertices on the mesh. Here, we're just scaling in and out
182 // for demonstration purposes. This will affect all entities currently using the asset.
183 //
184 // To do this, we need to grab the stored attributes of each vertex. `Float32x3` just describes
185 // the format in which the attributes will be read: each position consists of an array of three
186 // f32 corresponding to x, y, and z.
187 //
188 // `ATTRIBUTE_POSITION` is a constant indicating that we want to know where the vertex is
189 // located in space (as opposed to which way its normal is facing, vertex color, or other
190 // details).
191 if let Some(VertexAttributeValues::Float32x3(positions)) =
192 mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION)
193 {
194 // Check a Local value (which only this system can make use of) to determine if we're
195 // currently scaled up or not.
196 let scale_factor = if *is_mesh_scaled { 0.5 } else { 2.0 };
197
198 for position in positions.iter_mut() {
199 // Apply the scale factor to each of x, y, and z.
200 position[0] *= scale_factor;
201 position[1] *= scale_factor;
202 position[2] *= scale_factor;
203 }
204
205 // Flip the local value to reverse the behavior next time the key is pressed.
206 *is_mesh_scaled = !*is_mesh_scaled;
207 }
208}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.
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.
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.
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 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)
Examples found in repository?
52fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
53 // Build a custom triangle mesh with colors
54 // We define a custom mesh because the examples only uses a limited
55 // set of vertex attributes for simplicity
56 let mesh = Mesh::new(
57 PrimitiveTopology::TriangleList,
58 RenderAssetUsages::default(),
59 )
60 .with_inserted_indices(Indices::U32(vec![0, 1, 2]))
61 .with_inserted_attribute(
62 Mesh::ATTRIBUTE_POSITION,
63 vec![
64 vec3(-0.5, -0.5, 0.0),
65 vec3(0.5, -0.5, 0.0),
66 vec3(0.0, 0.25, 0.0),
67 ],
68 )
69 .with_inserted_attribute(
70 Mesh::ATTRIBUTE_COLOR,
71 vec![
72 vec4(1.0, 0.0, 0.0, 1.0),
73 vec4(0.0, 1.0, 0.0, 1.0),
74 vec4(0.0, 0.0, 1.0, 1.0),
75 ],
76 );
77
78 // spawn 3 triangles to show that batching works
79 for (x, y) in [-0.5, 0.0, 0.5].into_iter().zip([-0.25, 0.5, -0.25]) {
80 // Spawn an entity with all the required components for it to be rendered with our custom pipeline
81 commands.spawn((
82 // We use a marker component to identify the mesh that will be rendered
83 // with our specialized pipeline
84 CustomRenderedEntity,
85 // We need to add the mesh handle to the entity
86 Mesh3d(meshes.add(mesh.clone())),
87 Transform::from_xyz(x, y, 0.0),
88 ));
89 }
90
91 // Spawn the camera.
92 commands.spawn((
93 Camera3d::default(),
94 // Move the camera back a bit to see all the triangles
95 Transform::from_xyz(0.0, 0.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
96 ));
97}More examples
400 fn build(&self) -> Mesh {
401 let radius = self.heart.radius;
402 // The curved parts of each wing (half) of the heart have an angle of `PI * 1.25` or 225°
403 let wing_angle = PI * 1.25;
404
405 // We create buffers for the vertices, their normals and UVs, as well as the indices used to connect the vertices.
406 let mut vertices = Vec::with_capacity(2 * self.resolution);
407 let mut uvs = Vec::with_capacity(2 * self.resolution);
408 let mut indices = Vec::with_capacity(6 * self.resolution - 9);
409 // Since the heart is flat, we know all the normals are identical already.
410 let normals = vec![[0f32, 0f32, 1f32]; 2 * self.resolution];
411
412 // The point in the middle of the two curved parts of the heart
413 vertices.push([0.0; 3]);
414 uvs.push([0.5, 0.5]);
415
416 // The left wing of the heart, starting from the point in the middle.
417 for i in 1..self.resolution {
418 let angle = (i as f32 / self.resolution as f32) * wing_angle;
419 let (sin, cos) = ops::sin_cos(angle);
420 vertices.push([radius * (cos - 1.0), radius * sin, 0.0]);
421 uvs.push([0.5 - (cos - 1.0) / 4., 0.5 - sin / 2.]);
422 }
423
424 // The bottom tip of the heart
425 vertices.push([0.0, radius * (-1. - SQRT_2), 0.0]);
426 uvs.push([0.5, 1.]);
427
428 // The right wing of the heart, starting from the bottom most point and going towards the middle point.
429 for i in 0..self.resolution - 1 {
430 let angle = (i as f32 / self.resolution as f32) * wing_angle - PI / 4.;
431 let (sin, cos) = ops::sin_cos(angle);
432 vertices.push([radius * (cos + 1.0), radius * sin, 0.0]);
433 uvs.push([0.5 - (cos + 1.0) / 4., 0.5 - sin / 2.]);
434 }
435
436 // This is where we build all the triangles from the points created above.
437 // Each triangle has one corner on the middle point with the other two being adjacent points on the perimeter of the heart.
438 for i in 2..2 * self.resolution as u32 {
439 indices.extend_from_slice(&[i - 1, i, 0]);
440 }
441
442 // Here, the actual `Mesh` is created. We set the indices, vertices, normals and UVs created above and specify the topology of the mesh.
443 Mesh::new(
444 bevy::mesh::PrimitiveTopology::TriangleList,
445 RenderAssetUsages::default(),
446 )
447 .with_inserted_indices(bevy::mesh::Indices::U32(indices))
448 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
449 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
450 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
451 }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}37fn setup(
38 mut commands: Commands,
39 asset_server: Res<AssetServer>,
40 mut meshes: ResMut<Assets<Mesh>>,
41 mut materials: ResMut<Assets<StandardMaterial>>,
42 mut skinned_mesh_inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
43) {
44 // Create a camera
45 commands.spawn((
46 Camera3d::default(),
47 Transform::from_xyz(2.5, 2.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
48 ));
49
50 // Create inverse bindpose matrices for a skeleton consists of 2 joints
51 let inverse_bindposes = skinned_mesh_inverse_bindposes_assets.add(vec![
52 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
53 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
54 ]);
55
56 // Create a mesh
57 let mesh = Mesh::new(
58 PrimitiveTopology::TriangleList,
59 RenderAssetUsages::RENDER_WORLD,
60 )
61 // Set mesh vertex positions
62 .with_inserted_attribute(
63 Mesh::ATTRIBUTE_POSITION,
64 vec![
65 [0.0, 0.0, 0.0],
66 [1.0, 0.0, 0.0],
67 [0.0, 0.5, 0.0],
68 [1.0, 0.5, 0.0],
69 [0.0, 1.0, 0.0],
70 [1.0, 1.0, 0.0],
71 [0.0, 1.5, 0.0],
72 [1.0, 1.5, 0.0],
73 [0.0, 2.0, 0.0],
74 [1.0, 2.0, 0.0],
75 ],
76 )
77 // Add UV coordinates that map the left half of the texture since its a 1 x
78 // 2 rectangle.
79 .with_inserted_attribute(
80 Mesh::ATTRIBUTE_UV_0,
81 vec![
82 [0.0, 0.00],
83 [0.5, 0.00],
84 [0.0, 0.25],
85 [0.5, 0.25],
86 [0.0, 0.50],
87 [0.5, 0.50],
88 [0.0, 0.75],
89 [0.5, 0.75],
90 [0.0, 1.00],
91 [0.5, 1.00],
92 ],
93 )
94 // Set mesh vertex normals
95 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10])
96 // Set mesh vertex joint indices for mesh skinning.
97 // Each vertex gets 4 indices used to address the `JointTransforms` array in the vertex shader
98 // as well as `SkinnedMeshJoint` array in the `SkinnedMesh` component.
99 // This means that a maximum of 4 joints can affect a single vertex.
100 .with_inserted_attribute(
101 Mesh::ATTRIBUTE_JOINT_INDEX,
102 // Need to be explicit here as [u16; 4] could be either Uint16x4 or Unorm16x4.
103 VertexAttributeValues::Uint16x4(vec![
104 [0, 0, 0, 0],
105 [0, 0, 0, 0],
106 [0, 1, 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 ]),
115 )
116 // Set mesh vertex joint weights for mesh skinning.
117 // Each vertex gets 4 joint weights corresponding to the 4 joint indices assigned to it.
118 // The sum of these weights should equal to 1.
119 .with_inserted_attribute(
120 Mesh::ATTRIBUTE_JOINT_WEIGHT,
121 vec![
122 [1.00, 0.00, 0.0, 0.0],
123 [1.00, 0.00, 0.0, 0.0],
124 [0.75, 0.25, 0.0, 0.0],
125 [0.75, 0.25, 0.0, 0.0],
126 [0.50, 0.50, 0.0, 0.0],
127 [0.50, 0.50, 0.0, 0.0],
128 [0.25, 0.75, 0.0, 0.0],
129 [0.25, 0.75, 0.0, 0.0],
130 [0.00, 1.00, 0.0, 0.0],
131 [0.00, 1.00, 0.0, 0.0],
132 ],
133 )
134 // Tell bevy to construct triangles from a list of vertex indices,
135 // where each 3 vertex indices form a triangle.
136 .with_inserted_indices(Indices::U16(vec![
137 0, 1, 3, 0, 3, 2, 2, 3, 5, 2, 5, 4, 4, 5, 7, 4, 7, 6, 6, 7, 9, 6, 9, 8,
138 ]));
139
140 let mesh = meshes.add(mesh);
141
142 // We're seeding the PRNG here to make this example deterministic for testing purposes.
143 // This isn't strictly required in practical use unless you need your app to be deterministic.
144 let mut rng = ChaCha8Rng::seed_from_u64(42);
145
146 for i in -5..5 {
147 // Create joint entities
148 let joint_0 = commands
149 .spawn(Transform::from_xyz(
150 i as f32 * 1.5,
151 0.0,
152 // Move quads back a small amount to avoid Z-fighting and not
153 // obscure the transform gizmos.
154 -(i as f32 * 0.01).abs(),
155 ))
156 .id();
157 let joint_1 = commands.spawn((AnimatedJoint(i), Transform::IDENTITY)).id();
158
159 // Set joint_1 as a child of joint_0.
160 commands.entity(joint_0).add_children(&[joint_1]);
161
162 // Each joint in this vector corresponds to each inverse bindpose matrix in `SkinnedMeshInverseBindposes`.
163 let joint_entities = vec![joint_0, joint_1];
164
165 // Create skinned mesh renderer. Note that its transform doesn't affect the position of the mesh.
166 commands.spawn((
167 Mesh3d(mesh.clone()),
168 MeshMaterial3d(materials.add(StandardMaterial {
169 base_color: Color::srgb(
170 rng.random_range(0.0..1.0),
171 rng.random_range(0.0..1.0),
172 rng.random_range(0.0..1.0),
173 ),
174 base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
175 ..default()
176 })),
177 SkinnedMesh {
178 inverse_bindposes: inverse_bindposes.clone(),
179 joints: joint_entities,
180 },
181 ));
182 }
183}106fn create_cube_mesh() -> Mesh {
107 // Keep the mesh data accessible in future frames to be able to mutate it in toggle_texture.
108 Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD)
109 .with_inserted_attribute(
110 Mesh::ATTRIBUTE_POSITION,
111 // Each array is an [x, y, z] coordinate in local space.
112 // The camera coordinate space is right-handed x-right, y-up, z-back. This means "forward" is -Z.
113 // Meshes always rotate around their local [0, 0, 0] when a rotation is applied to their Transform.
114 // By centering our mesh around the origin, rotating the mesh preserves its center of mass.
115 vec![
116 // top (facing towards +y)
117 [-0.5, 0.5, -0.5], // vertex with index 0
118 [0.5, 0.5, -0.5], // vertex with index 1
119 [0.5, 0.5, 0.5], // etc. until 23
120 [-0.5, 0.5, 0.5],
121 // bottom (-y)
122 [-0.5, -0.5, -0.5],
123 [0.5, -0.5, -0.5],
124 [0.5, -0.5, 0.5],
125 [-0.5, -0.5, 0.5],
126 // right (+x)
127 [0.5, -0.5, -0.5],
128 [0.5, -0.5, 0.5],
129 [0.5, 0.5, 0.5], // This vertex is at the same position as vertex with index 2, but they'll have different UV and normal
130 [0.5, 0.5, -0.5],
131 // left (-x)
132 [-0.5, -0.5, -0.5],
133 [-0.5, -0.5, 0.5],
134 [-0.5, 0.5, 0.5],
135 [-0.5, 0.5, -0.5],
136 // back (+z)
137 [-0.5, -0.5, 0.5],
138 [-0.5, 0.5, 0.5],
139 [0.5, 0.5, 0.5],
140 [0.5, -0.5, 0.5],
141 // forward (-z)
142 [-0.5, -0.5, -0.5],
143 [-0.5, 0.5, -0.5],
144 [0.5, 0.5, -0.5],
145 [0.5, -0.5, -0.5],
146 ],
147 )
148 // Set-up UV coordinates to point to the upper (V < 0.5), "dirt+grass" part of the texture.
149 // Take a look at the custom image (assets/textures/array_texture.png)
150 // so the UV coords will make more sense
151 // Note: (0.0, 0.0) = Top-Left in UV mapping, (1.0, 1.0) = Bottom-Right in UV mapping
152 .with_inserted_attribute(
153 Mesh::ATTRIBUTE_UV_0,
154 vec![
155 // Assigning the UV coords for the top side.
156 [0.0, 0.2], [0.0, 0.0], [1.0, 0.0], [1.0, 0.2],
157 // Assigning the UV coords for the bottom side.
158 [0.0, 0.45], [0.0, 0.25], [1.0, 0.25], [1.0, 0.45],
159 // Assigning the UV coords for the right side.
160 [1.0, 0.45], [0.0, 0.45], [0.0, 0.2], [1.0, 0.2],
161 // Assigning the UV coords for the left side.
162 [1.0, 0.45], [0.0, 0.45], [0.0, 0.2], [1.0, 0.2],
163 // Assigning the UV coords for the back side.
164 [0.0, 0.45], [0.0, 0.2], [1.0, 0.2], [1.0, 0.45],
165 // Assigning the UV coords for the forward side.
166 [0.0, 0.45], [0.0, 0.2], [1.0, 0.2], [1.0, 0.45],
167 ],
168 )
169 // For meshes with flat shading, normals are orthogonal (pointing out) from the direction of
170 // the surface.
171 // Normals are required for correct lighting calculations.
172 // Each array represents a normalized vector, which length should be equal to 1.0.
173 .with_inserted_attribute(
174 Mesh::ATTRIBUTE_NORMAL,
175 vec![
176 // Normals for the top side (towards +y)
177 [0.0, 1.0, 0.0],
178 [0.0, 1.0, 0.0],
179 [0.0, 1.0, 0.0],
180 [0.0, 1.0, 0.0],
181 // Normals for the bottom side (towards -y)
182 [0.0, -1.0, 0.0],
183 [0.0, -1.0, 0.0],
184 [0.0, -1.0, 0.0],
185 [0.0, -1.0, 0.0],
186 // Normals for the right side (towards +x)
187 [1.0, 0.0, 0.0],
188 [1.0, 0.0, 0.0],
189 [1.0, 0.0, 0.0],
190 [1.0, 0.0, 0.0],
191 // Normals for the left side (towards -x)
192 [-1.0, 0.0, 0.0],
193 [-1.0, 0.0, 0.0],
194 [-1.0, 0.0, 0.0],
195 [-1.0, 0.0, 0.0],
196 // Normals for the back side (towards +z)
197 [0.0, 0.0, 1.0],
198 [0.0, 0.0, 1.0],
199 [0.0, 0.0, 1.0],
200 [0.0, 0.0, 1.0],
201 // Normals for the forward side (towards -z)
202 [0.0, 0.0, -1.0],
203 [0.0, 0.0, -1.0],
204 [0.0, 0.0, -1.0],
205 [0.0, 0.0, -1.0],
206 ],
207 )
208 // Create the triangles out of the 24 vertices we created.
209 // To construct a square, we need 2 triangles, therefore 12 triangles in total.
210 // To construct a triangle, we need the indices of its 3 defined vertices, adding them one
211 // by one, in a counter-clockwise order (relative to the position of the viewer, the order
212 // should appear counter-clockwise from the front of the triangle, in this case from outside the cube).
213 // Read more about how to correctly build a mesh manually in the Bevy documentation of a Mesh,
214 // further examples and the implementation of the built-in shapes.
215 //
216 // The first two defined triangles look like this (marked with the vertex indices,
217 // and the axis), when looking down at the top (+y) of the cube:
218 // -Z
219 // ^
220 // 0---1
221 // | /|
222 // | / | -> +X
223 // |/ |
224 // 3---2
225 //
226 // The right face's (+x) triangles look like this, seen from the outside of the cube.
227 // +Y
228 // ^
229 // 10--11
230 // | /|
231 // | / | -> -Z
232 // |/ |
233 // 9---8
234 //
235 // The back face's (+z) triangles look like this, seen from the outside of the cube.
236 // +Y
237 // ^
238 // 17--18
239 // |\ |
240 // | \ | -> +X
241 // | \|
242 // 16--19
243 .with_inserted_indices(Indices::U32(vec![
244 0,3,1 , 1,3,2, // triangles making up the top (+y) facing side.
245 4,5,7 , 5,6,7, // bottom (-y)
246 8,11,9 , 9,11,10, // right (+x)
247 12,13,15 , 13,14,15, // left (-x)
248 16,19,17 , 17,19,18, // back (+z)
249 20,21,23 , 21,22,23, // forward (-z)
250 ]))
251}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.
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.
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)
Sourcepub fn get_vertex_size(&self) -> u64
pub fn get_vertex_size(&self) -> u64
Returns the size of a vertex in bytes.
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.
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.
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.
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.
Examples found in repository?
161fn add_raytracing_meshes_on_scene_load(
162 scene_ready: On<SceneInstanceReady>,
163 children: Query<&Children>,
164 mesh_query: Query<(
165 &Mesh3d,
166 &MeshMaterial3d<StandardMaterial>,
167 Option<&GltfMaterialName>,
168 )>,
169 mut meshes: ResMut<Assets<Mesh>>,
170 mut materials: ResMut<Assets<StandardMaterial>>,
171 mut commands: Commands,
172 args: Res<Args>,
173) {
174 for descendant in children.iter_descendants(scene_ready.entity) {
175 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
176 mesh_query.get(descendant)
177 {
178 // Add raytracing mesh component
179 commands
180 .entity(descendant)
181 .insert(RaytracingMesh3d(mesh_handle.clone()));
182
183 // Ensure meshes are Solari compatible
184 let mesh = meshes.get_mut(mesh_handle).unwrap();
185 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
186 let vertex_count = mesh.count_vertices();
187 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
188 mesh.insert_attribute(
189 Mesh::ATTRIBUTE_TANGENT,
190 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
191 );
192 }
193 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
194 mesh.generate_tangents().unwrap();
195 }
196 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
197 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
198 }
199
200 // Prevent rasterization if using pathtracer
201 if args.pathtracer == Some(true) {
202 commands.entity(descendant).remove::<Mesh3d>();
203 }
204
205 // Adjust scene materials to better demo Solari features
206 if material_name.map(|s| s.0.as_str()) == Some("material") {
207 let material = materials.get_mut(material_handle).unwrap();
208 material.emissive = LinearRgba::BLACK;
209 }
210 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
211 let material = materials.get_mut(material_handle).unwrap();
212 material.emissive =
213 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
214 material.alpha_mode = AlphaMode::Opaque;
215 material.specular_transmission = 0.0;
216
217 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
218 }
219 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
220 let material = materials.get_mut(material_handle).unwrap();
221 material.alpha_mode = AlphaMode::Opaque;
222 material.specular_transmission = 0.0;
223 }
224 }
225 }
226}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.
Sourcepub fn write_packed_vertex_buffer_data(&self, slice: &mut [u8])
pub fn write_packed_vertex_buffer_data(&self, slice: &mut [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.
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.
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)
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.=
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.
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.
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.
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.
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.
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
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.
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.
Sourcepub fn generate_tangents(&mut self) -> Result<(), GenerateTangentsError>
pub fn generate_tangents(&mut self) -> Result<(), GenerateTangentsError>
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
211fn setup_parallax(
212 mut commands: Commands,
213 mut materials: ResMut<Assets<StandardMaterial>>,
214 mut meshes: ResMut<Assets<Mesh>>,
215 asset_server: Res<AssetServer>,
216) {
217 // The normal map. Note that to generate it in the GIMP image editor, you should
218 // open the depth map, and do Filters → Generic → Normal Map
219 // You should enable the "flip X" checkbox.
220 let normal_handle = asset_server.load_with_settings(
221 "textures/parallax_example/cube_normal.png",
222 // The normal map texture is in linear color space. Lighting won't look correct
223 // if `is_srgb` is `true`, which is the default.
224 |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
225 );
226
227 let mut cube = Mesh::from(Cuboid::new(0.15, 0.15, 0.15));
228
229 // NOTE: for normal maps and depth maps to work, the mesh
230 // needs tangents generated.
231 cube.generate_tangents().unwrap();
232
233 let parallax_material = materials.add(StandardMaterial {
234 perceptual_roughness: 0.4,
235 base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
236 normal_map_texture: Some(normal_handle),
237 // The depth map is a grayscale texture where black is the highest level and
238 // white the lowest.
239 depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
240 parallax_depth_scale: 0.09,
241 parallax_mapping_method: ParallaxMappingMethod::Relief { max_steps: 4 },
242 max_parallax_layer_count: ops::exp2(5.0f32),
243 ..default()
244 });
245 commands.spawn((
246 Mesh3d(meshes.add(cube)),
247 MeshMaterial3d(parallax_material),
248 Transform::from_xyz(0.4, 0.2, -0.8),
249 Spin { speed: 0.3 },
250 ));
251}59fn setup(
60 mut commands: Commands,
61 mut meshes: ResMut<Assets<Mesh>>,
62 mut materials: ResMut<Assets<StandardMaterial>>,
63) -> Result {
64 let mut seeded_rng = ChaCha8Rng::seed_from_u64(19878367467712);
65
66 // Make a plane for establishing space.
67 commands.spawn((
68 Mesh3d(meshes.add(Plane3d::default().mesh().size(12.0, 12.0))),
69 MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
70 Transform::from_xyz(0.0, -2.5, 0.0),
71 ));
72
73 // Spawn a light:
74 commands.spawn((
75 PointLight {
76 shadows_enabled: true,
77 ..default()
78 },
79 Transform::from_xyz(4.0, 8.0, 4.0),
80 ));
81
82 // Spawn a camera:
83 commands.spawn((
84 Camera3d::default(),
85 Transform::from_xyz(-2.0, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
86 ));
87
88 // Create a new sphere mesh:
89 let mut sphere_mesh = Sphere::new(1.0).mesh().ico(7)?;
90 sphere_mesh.generate_tangents()?;
91
92 // Spawn the mesh into the scene:
93 let mut sphere = commands.spawn((
94 Mesh3d(meshes.add(sphere_mesh.clone())),
95 MeshMaterial3d(materials.add(StandardMaterial::default())),
96 Transform::from_xyz(-1.0, 1.0, 0.0),
97 ));
98
99 // Generate random sample points:
100 let triangles = sphere_mesh.triangles()?;
101 let distribution = UniformMeshSampler::try_new(triangles)?;
102
103 // Setup sample points:
104 let point_mesh = meshes.add(Sphere::new(0.01).mesh().ico(3)?);
105 let point_material = materials.add(StandardMaterial {
106 base_color: Srgba::RED.into(),
107 emissive: LinearRgba::rgb(1.0, 0.0, 0.0),
108 ..default()
109 });
110
111 // Add sample points as children of the sphere:
112 for point in distribution.sample_iter(&mut seeded_rng).take(10000) {
113 sphere.with_child((
114 Mesh3d(point_mesh.clone()),
115 MeshMaterial3d(point_material.clone()),
116 Transform::from_translation(point),
117 ));
118 }
119
120 // Indicate the system completed successfully:
121 Ok(())
122}161fn add_raytracing_meshes_on_scene_load(
162 scene_ready: On<SceneInstanceReady>,
163 children: Query<&Children>,
164 mesh_query: Query<(
165 &Mesh3d,
166 &MeshMaterial3d<StandardMaterial>,
167 Option<&GltfMaterialName>,
168 )>,
169 mut meshes: ResMut<Assets<Mesh>>,
170 mut materials: ResMut<Assets<StandardMaterial>>,
171 mut commands: Commands,
172 args: Res<Args>,
173) {
174 for descendant in children.iter_descendants(scene_ready.entity) {
175 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
176 mesh_query.get(descendant)
177 {
178 // Add raytracing mesh component
179 commands
180 .entity(descendant)
181 .insert(RaytracingMesh3d(mesh_handle.clone()));
182
183 // Ensure meshes are Solari compatible
184 let mesh = meshes.get_mut(mesh_handle).unwrap();
185 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
186 let vertex_count = mesh.count_vertices();
187 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
188 mesh.insert_attribute(
189 Mesh::ATTRIBUTE_TANGENT,
190 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
191 );
192 }
193 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
194 mesh.generate_tangents().unwrap();
195 }
196 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
197 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
198 }
199
200 // Prevent rasterization if using pathtracer
201 if args.pathtracer == Some(true) {
202 commands.entity(descendant).remove::<Mesh3d>();
203 }
204
205 // Adjust scene materials to better demo Solari features
206 if material_name.map(|s| s.0.as_str()) == Some("material") {
207 let material = materials.get_mut(material_handle).unwrap();
208 material.emissive = LinearRgba::BLACK;
209 }
210 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
211 let material = materials.get_mut(material_handle).unwrap();
212 material.emissive =
213 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
214 material.alpha_mode = AlphaMode::Opaque;
215 material.specular_transmission = 0.0;
216
217 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
218 }
219 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
220 let material = materials.get_mut(material_handle).unwrap();
221 material.alpha_mode = AlphaMode::Opaque;
222 material.specular_transmission = 0.0;
223 }
224 }
225 }
226}Sourcepub fn with_generated_tangents(self) -> Result<Mesh, GenerateTangentsError>
pub fn with_generated_tangents(self) -> Result<Mesh, GenerateTangentsError>
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 SceneRoot(asset_server.load("models/AnisotropyBarnLamp/AnisotropyBarnLamp.gltf#Scene0")),
110 Transform::from_xyz(0.0, 0.07, -0.13),
111 Scene::BarnLamp,
112 ));
113
114 commands.spawn((
115 Mesh3d(
116 asset_server.add(
117 Mesh::from(Sphere::new(0.1))
118 .with_generated_tangents()
119 .unwrap(),
120 ),
121 ),
122 MeshMaterial3d(asset_server.add(StandardMaterial {
123 base_color: palettes::tailwind::GRAY_300.into(),
124 anisotropy_rotation: 0.5,
125 anisotropy_strength: 1.,
126 ..default()
127 })),
128 Scene::Sphere,
129 Visibility::Hidden,
130 ));
131
132 spawn_text(&mut commands, &app_status);
133}More examples
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.load_with_settings(
210 "textures/parallax_example/cube_normal.png",
211 // The normal map texture is in linear color space. Lighting won't look correct
212 // if `is_srgb` is `true`, which is the default.
213 |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
214 );
215
216 // Camera
217 commands.spawn((
218 Camera3d::default(),
219 Transform::from_xyz(1.5, 1.5, 1.5).looking_at(Vec3::ZERO, Vec3::Y),
220 CameraController,
221 ));
222
223 // represent the light source as a sphere
224 let mesh = meshes.add(Sphere::new(0.05).mesh().ico(3).unwrap());
225
226 // light
227 commands.spawn((
228 PointLight {
229 shadows_enabled: true,
230 ..default()
231 },
232 Transform::from_xyz(2.0, 1.0, -1.1),
233 children![(Mesh3d(mesh), MeshMaterial3d(materials.add(Color::WHITE)))],
234 ));
235
236 // Plane
237 commands.spawn((
238 Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))),
239 MeshMaterial3d(materials.add(StandardMaterial {
240 // standard material derived from dark green, but
241 // with roughness and reflectance set.
242 perceptual_roughness: 0.45,
243 reflectance: 0.18,
244 ..Color::srgb_u8(0, 80, 0).into()
245 })),
246 Transform::from_xyz(0.0, -1.0, 0.0),
247 ));
248
249 let parallax_depth_scale = TargetDepth::default().0;
250 let max_parallax_layer_count = ops::exp2(TargetLayers::default().0);
251 let parallax_mapping_method = CurrentMethod::default();
252 let parallax_material = materials.add(StandardMaterial {
253 perceptual_roughness: 0.4,
254 base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
255 normal_map_texture: Some(normal_handle),
256 // The depth map is a grayscale texture where black is the highest level and
257 // white the lowest.
258 depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
259 parallax_depth_scale,
260 parallax_mapping_method: parallax_mapping_method.0,
261 max_parallax_layer_count,
262 ..default()
263 });
264 commands.spawn((
265 Mesh3d(
266 meshes.add(
267 // NOTE: for normal maps and depth maps to work, the mesh
268 // needs tangents generated.
269 Mesh::from(Cuboid::default())
270 .with_generated_tangents()
271 .unwrap(),
272 ),
273 ),
274 MeshMaterial3d(parallax_material.clone()),
275 Spin { speed: 0.3 },
276 ));
277
278 let background_cube = meshes.add(
279 Mesh::from(Cuboid::new(40.0, 40.0, 40.0))
280 .with_generated_tangents()
281 .unwrap(),
282 );
283
284 let background_cube_bundle = |translation| {
285 (
286 Mesh3d(background_cube.clone()),
287 MeshMaterial3d(parallax_material.clone()),
288 Transform::from_translation(translation),
289 Spin { speed: -0.1 },
290 )
291 };
292 commands.spawn(background_cube_bundle(Vec3::new(45., 0., 0.)));
293 commands.spawn(background_cube_bundle(Vec3::new(-45., 0., 0.)));
294 commands.spawn(background_cube_bundle(Vec3::new(0., 0., 45.)));
295 commands.spawn(background_cube_bundle(Vec3::new(0., 0., -45.)));
296
297 // example instructions
298 commands.spawn((
299 Text::default(),
300 Node {
301 position_type: PositionType::Absolute,
302 top: px(12),
303 left: px(12),
304 ..default()
305 },
306 children![
307 (TextSpan(format!("Parallax depth scale: {parallax_depth_scale:.5}\n"))),
308 (TextSpan(format!("Layers: {max_parallax_layer_count:.0}\n"))),
309 (TextSpan(format!("{parallax_mapping_method}\n"))),
310 (TextSpan::new("\n\n")),
311 (TextSpan::new("Controls:\n")),
312 (TextSpan::new("Left click - Change view angle\n")),
313 (TextSpan::new("1/2 - Decrease/Increase parallax depth scale\n",)),
314 (TextSpan::new("3/4 - Decrease/Increase layer count\n")),
315 (TextSpan::new("Space - Switch parallaxing algorithm\n")),
316 ],
317 ));
318}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.
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.
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.
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.
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.
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.
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.
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.
Sourcepub fn has_morph_targets(&self) -> bool
pub fn has_morph_targets(&self) -> bool
Whether this mesh has morph targets.
Sourcepub fn set_morph_targets(&mut self, morph_targets: Handle<Image>)
pub fn set_morph_targets(&mut self, morph_targets: Handle<Image>)
Set morph targets image for this mesh. This requires a “morph target image”. See MorphTargetImage for info.
pub fn morph_targets(&self) -> Option<&Handle<Image>>
Sourcepub fn with_morph_targets(self, morph_targets: Handle<Image>) -> Mesh
pub fn with_morph_targets(self, morph_targets: Handle<Image>) -> Mesh
Consumes the mesh and returns a mesh with the given morph targets.
This requires a “morph target image”. See MorphTargetImage for info.
(Alternatively, you can use Mesh::set_morph_targets to mutate an existing mesh in-place)
Sourcepub fn set_morph_target_names(&mut self, names: Vec<String>)
pub fn set_morph_target_names(&mut self, names: Vec<String>)
Sets the names of each morph target. This should correspond to the order of the morph targets in set_morph_targets.
Sourcepub fn with_morph_target_names(self, names: Vec<String>) -> Mesh
pub fn with_morph_target_names(self, names: Vec<String>) -> Mesh
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)
Sourcepub fn morph_target_names(&self) -> Option<&[String]>
pub fn morph_target_names(&self) -> Option<&[String]>
Gets a list of all morph target names, if they exist.
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 normalize_joint_weights(&mut self)
pub fn normalize_joint_weights(&mut self)
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?
59fn setup(
60 mut commands: Commands,
61 mut meshes: ResMut<Assets<Mesh>>,
62 mut materials: ResMut<Assets<StandardMaterial>>,
63) -> Result {
64 let mut seeded_rng = ChaCha8Rng::seed_from_u64(19878367467712);
65
66 // Make a plane for establishing space.
67 commands.spawn((
68 Mesh3d(meshes.add(Plane3d::default().mesh().size(12.0, 12.0))),
69 MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
70 Transform::from_xyz(0.0, -2.5, 0.0),
71 ));
72
73 // Spawn a light:
74 commands.spawn((
75 PointLight {
76 shadows_enabled: true,
77 ..default()
78 },
79 Transform::from_xyz(4.0, 8.0, 4.0),
80 ));
81
82 // Spawn a camera:
83 commands.spawn((
84 Camera3d::default(),
85 Transform::from_xyz(-2.0, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
86 ));
87
88 // Create a new sphere mesh:
89 let mut sphere_mesh = Sphere::new(1.0).mesh().ico(7)?;
90 sphere_mesh.generate_tangents()?;
91
92 // Spawn the mesh into the scene:
93 let mut sphere = commands.spawn((
94 Mesh3d(meshes.add(sphere_mesh.clone())),
95 MeshMaterial3d(materials.add(StandardMaterial::default())),
96 Transform::from_xyz(-1.0, 1.0, 0.0),
97 ));
98
99 // Generate random sample points:
100 let triangles = sphere_mesh.triangles()?;
101 let distribution = UniformMeshSampler::try_new(triangles)?;
102
103 // Setup sample points:
104 let point_mesh = meshes.add(Sphere::new(0.01).mesh().ico(3)?);
105 let point_material = materials.add(StandardMaterial {
106 base_color: Srgba::RED.into(),
107 emissive: LinearRgba::rgb(1.0, 0.0, 0.0),
108 ..default()
109 });
110
111 // Add sample points as children of the sphere:
112 for point in distribution.sample_iter(&mut seeded_rng).take(10000) {
113 sphere.with_child((
114 Mesh3d(point_mesh.clone()),
115 MeshMaterial3d(point_material.clone()),
116 Transform::from_translation(point),
117 ));
118 }
119
120 // Indicate the system completed successfully:
121 Ok(())
122}Trait Implementations§
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_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. Read moreSource§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 name_at(&self, index: usize) -> Option<&str>
fn name_at(&self, index: usize) -> Option<&str>
index.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.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))
impl Asset for Mesh
impl StructuralPartialEq for Mesh
Auto Trait Implementations§
impl Freeze for Mesh
impl !RefUnwindSafe for Mesh
impl Send for Mesh
impl Sync for Mesh
impl Unpin 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<A> AssetContainer for Awhere
A: Asset,
impl<A> AssetContainer for Awhere
A: Asset,
fn insert(self: Box<A>, id: UntypedAssetId, world: &mut World)
fn asset_type_name(&self) -> &'static str
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
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
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
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.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,
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.Source§impl<T> Pointable for T
impl<T> Pointable 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.