docs/bevy/examples/3d/lines.rs 3.4 K · 118 lines · raw · history

1 //! Create a custom material to draw basic lines in 3D
2
3 use bevy::{
4 asset::RenderAssetUsages,
5 mesh::{Indices, PrimitiveTopology},
6 prelude::*,
7 reflect::TypePath,
8 render::render_resource::AsBindGroup,
9 shader::ShaderRef,
10 };
11
12 /// This example uses a shader source file from the assets subdirectory
13 const SHADER_ASSET_PATH: &str = "shaders/line_material.wesl";
14
15 fn main() {
16 App::new()
17 .add_plugins((DefaultPlugins, MaterialPlugin::<LineMaterial>::default()))
18 .add_systems(Startup, setup)
19 .run();
20 }
21
22 fn setup(
23 mut commands: Commands,
24 mut meshes: ResMut<Assets<Mesh>>,
25 mut materials: ResMut<Assets<LineMaterial>>,
26 ) {
27 // Spawn a list of lines with start and end points for each lines
28 commands.spawn((
29 Mesh3d(meshes.add(LineList {
30 lines: vec![
31 (Vec3::ZERO, Vec3::new(1.0, 1.0, 0.0)),
32 (Vec3::new(1.0, 1.0, 0.0), Vec3::new(1.0, 0.0, 0.0)),
33 ],
34 })),
35 MeshMaterial3d(materials.add(LineMaterial {
36 color: LinearRgba::GREEN,
37 })),
38 Transform::from_xyz(-1.5, 0.0, 0.0),
39 ));
40
41 // Spawn a line strip that goes from point to point
42 commands.spawn((
43 Mesh3d(meshes.add(LineStrip {
44 points: vec![
45 Vec3::ZERO,
46 Vec3::new(1.0, 1.0, 0.0),
47 Vec3::new(2.0, 0.0, 0.0),
48 Vec3::new(2.0, 1.0, 0.0),
49 Vec3::new(3.0, 1.0, 0.0),
50 ],
51 indices: Indices::U16(vec![0, 1, u16::MAX /* primitive restart */, 2, 3, 4]),
52 })),
53 MeshMaterial3d(materials.add(LineMaterial {
54 color: LinearRgba::BLUE,
55 })),
56 Transform::from_xyz(0.5, 0.0, 0.0),
57 ));
58
59 // camera
60 commands.spawn((
61 Camera3d::default(),
62 Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
63 ));
64 }
65
66 #[derive(Asset, TypePath, Default, AsBindGroup, Debug, Clone)]
67 struct LineMaterial {
68 #[uniform(0)]
69 color: LinearRgba,
70 }
71
72 impl Material for LineMaterial {
73 fn fragment_shader() -> ShaderRef {
74 SHADER_ASSET_PATH.into()
75 }
76 }
77
78 /// A list of lines with a start and end position
79 #[derive(Debug, Clone)]
80 struct LineList {
81 lines: Vec<(Vec3, Vec3)>,
82 }
83
84 impl From<LineList> for Mesh {
85 fn from(line: LineList) -> Self {
86 let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect();
87
88 Mesh::new(
89 // This tells wgpu that the positions are list of lines
90 // where every pair is a start and end point
91 PrimitiveTopology::LineList,
92 RenderAssetUsages::RENDER_WORLD,
93 )
94 // Add the vertices positions as an attribute
95 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
96 }
97 }
98
99 /// A list of points that will have a line drawn between each consecutive points
100 #[derive(Debug, Clone)]
101 struct LineStrip {
102 points: Vec<Vec3>,
103 indices: Indices,
104 }
105
106 impl From<LineStrip> for Mesh {
107 fn from(line: LineStrip) -> Self {
108 Mesh::new(
109 // This tells wgpu that the positions are a list of points
110 // where a line will be drawn between each consecutive point
111 PrimitiveTopology::LineStrip,
112 RenderAssetUsages::RENDER_WORLD,
113 )
114 // Add the point positions as an attribute
115 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, line.points)
116 .with_inserted_indices(line.indices)
117 }
118 }