mirror of
https://github.com/bevyengine/bevy
synced 2024-11-26 06:30:19 +00:00
12a2f83edd
# Objective - This PR aims to make creating meshes a little bit more ergonomic, specifically by removing the need for intermediate mutable variables. ## Solution - We add methods that consume the `Mesh` and return a mesh with the specified changes, so that meshes can be entirely constructed via builder-style calls, without intermediate variables; - Methods are flagged with `#[must_use]` to ensure proper use; - Examples are updated to use the new methods where applicable. Some examples are kept with the mutating methods so that users can still easily discover them, and also where the new methods wouldn't really be an improvement. ## Examples Before: ```rust let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vs); mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vns); mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vts); mesh.set_indices(Some(Indices::U32(tris))); mesh ``` After: ```rust Mesh::new(PrimitiveTopology::TriangleList) .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vs) .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vns) .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vts) .with_indices(Some(Indices::U32(tris))) ``` Before: ```rust let mut cube = Mesh::from(shape::Cube { size: 1.0 }); cube.generate_tangents().unwrap(); PbrBundle { mesh: meshes.add(cube), ..default() } ``` After: ```rust PbrBundle { mesh: meshes.add( Mesh::from(shape::Cube { size: 1.0 }) .with_generated_tangents() .unwrap(), ), ..default() } ``` --- ## Changelog - Added consuming builder methods for more ergonomic `Mesh` creation: `with_inserted_attribute()`, `with_removed_attribute()`, `with_indices()`, `with_duplicated_vertices()`, `with_computed_flat_normals()`, `with_generated_tangents()`, `with_morph_targets()`, `with_morph_target_names()`.
119 lines
3.6 KiB
Rust
119 lines
3.6 KiB
Rust
//! Create a custom material to draw basic lines in 3D
|
|
|
|
use bevy::{
|
|
pbr::{MaterialPipeline, MaterialPipelineKey},
|
|
prelude::*,
|
|
reflect::TypePath,
|
|
render::{
|
|
mesh::{MeshVertexBufferLayout, PrimitiveTopology},
|
|
render_resource::{
|
|
AsBindGroup, PolygonMode, RenderPipelineDescriptor, ShaderRef,
|
|
SpecializedMeshPipelineError,
|
|
},
|
|
},
|
|
};
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins((DefaultPlugins, MaterialPlugin::<LineMaterial>::default()))
|
|
.add_systems(Startup, setup)
|
|
.run();
|
|
}
|
|
|
|
fn setup(
|
|
mut commands: Commands,
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
mut materials: ResMut<Assets<LineMaterial>>,
|
|
) {
|
|
// Spawn a list of lines with start and end points for each lines
|
|
commands.spawn(MaterialMeshBundle {
|
|
mesh: meshes.add(Mesh::from(LineList {
|
|
lines: vec![
|
|
(Vec3::ZERO, Vec3::new(1.0, 1.0, 0.0)),
|
|
(Vec3::new(1.0, 1.0, 0.0), Vec3::new(1.0, 0.0, 0.0)),
|
|
],
|
|
})),
|
|
transform: Transform::from_xyz(-1.5, 0.0, 0.0),
|
|
material: materials.add(LineMaterial {
|
|
color: Color::GREEN,
|
|
}),
|
|
..default()
|
|
});
|
|
|
|
// Spawn a line strip that goes from point to point
|
|
commands.spawn(MaterialMeshBundle {
|
|
mesh: meshes.add(Mesh::from(LineStrip {
|
|
points: vec![
|
|
Vec3::ZERO,
|
|
Vec3::new(1.0, 1.0, 0.0),
|
|
Vec3::new(1.0, 0.0, 0.0),
|
|
],
|
|
})),
|
|
transform: Transform::from_xyz(0.5, 0.0, 0.0),
|
|
material: materials.add(LineMaterial { color: Color::BLUE }),
|
|
..default()
|
|
});
|
|
|
|
// camera
|
|
commands.spawn(Camera3dBundle {
|
|
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
|
..default()
|
|
});
|
|
}
|
|
|
|
#[derive(Asset, TypePath, Default, AsBindGroup, Debug, Clone)]
|
|
struct LineMaterial {
|
|
#[uniform(0)]
|
|
color: Color,
|
|
}
|
|
|
|
impl Material for LineMaterial {
|
|
fn fragment_shader() -> ShaderRef {
|
|
"shaders/line_material.wgsl".into()
|
|
}
|
|
|
|
fn specialize(
|
|
_pipeline: &MaterialPipeline<Self>,
|
|
descriptor: &mut RenderPipelineDescriptor,
|
|
_layout: &MeshVertexBufferLayout,
|
|
_key: MaterialPipelineKey<Self>,
|
|
) -> Result<(), SpecializedMeshPipelineError> {
|
|
// This is the important part to tell bevy to render this material as a line between vertices
|
|
descriptor.primitive.polygon_mode = PolygonMode::Line;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// A list of lines with a start and end position
|
|
#[derive(Debug, Clone)]
|
|
pub struct LineList {
|
|
pub lines: Vec<(Vec3, Vec3)>,
|
|
}
|
|
|
|
impl From<LineList> for Mesh {
|
|
fn from(line: LineList) -> Self {
|
|
let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect();
|
|
|
|
// This tells wgpu that the positions are list of lines
|
|
// where every pair is a start and end point
|
|
Mesh::new(PrimitiveTopology::LineList)
|
|
// Add the vertices positions as an attribute
|
|
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
|
|
}
|
|
}
|
|
|
|
/// A list of points that will have a line drawn between each consecutive points
|
|
#[derive(Debug, Clone)]
|
|
pub struct LineStrip {
|
|
pub points: Vec<Vec3>,
|
|
}
|
|
|
|
impl From<LineStrip> for Mesh {
|
|
fn from(line: LineStrip) -> Self {
|
|
// This tells wgpu that the positions are a list of points
|
|
// where a line will be drawn between each consecutive point
|
|
Mesh::new(PrimitiveTopology::LineStrip)
|
|
// Add the point positions as an attribute
|
|
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, line.points)
|
|
}
|
|
}
|