lines/
lines.rs

1//! Create a custom material to draw basic lines in 3D
2
3use bevy::{
4    asset::RenderAssetUsages,
5    mesh::{MeshVertexBufferLayoutRef, PrimitiveTopology},
6    pbr::{MaterialPipeline, MaterialPipelineKey},
7    prelude::*,
8    reflect::TypePath,
9    render::render_resource::{
10        AsBindGroup, PolygonMode, RenderPipelineDescriptor, SpecializedMeshPipelineError,
11    },
12    shader::ShaderRef,
13};
14
15/// This example uses a shader source file from the assets subdirectory
16const SHADER_ASSET_PATH: &str = "shaders/line_material.wgsl";
17
18fn main() {
19    App::new()
20        .add_plugins((DefaultPlugins, MaterialPlugin::<LineMaterial>::default()))
21        .add_systems(Startup, setup)
22        .run();
23}
24
25fn setup(
26    mut commands: Commands,
27    mut meshes: ResMut<Assets<Mesh>>,
28    mut materials: ResMut<Assets<LineMaterial>>,
29) {
30    // Spawn a list of lines with start and end points for each lines
31    commands.spawn((
32        Mesh3d(meshes.add(LineList {
33            lines: vec![
34                (Vec3::ZERO, Vec3::new(1.0, 1.0, 0.0)),
35                (Vec3::new(1.0, 1.0, 0.0), Vec3::new(1.0, 0.0, 0.0)),
36            ],
37        })),
38        MeshMaterial3d(materials.add(LineMaterial {
39            color: LinearRgba::GREEN,
40        })),
41        Transform::from_xyz(-1.5, 0.0, 0.0),
42    ));
43
44    // Spawn a line strip that goes from point to point
45    commands.spawn((
46        Mesh3d(meshes.add(LineStrip {
47            points: vec![
48                Vec3::ZERO,
49                Vec3::new(1.0, 1.0, 0.0),
50                Vec3::new(1.0, 0.0, 0.0),
51            ],
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)]
67struct LineMaterial {
68    #[uniform(0)]
69    color: LinearRgba,
70}
71
72impl Material for LineMaterial {
73    fn fragment_shader() -> ShaderRef {
74        SHADER_ASSET_PATH.into()
75    }
76
77    fn specialize(
78        _pipeline: &MaterialPipeline,
79        descriptor: &mut RenderPipelineDescriptor,
80        _layout: &MeshVertexBufferLayoutRef,
81        _key: MaterialPipelineKey<Self>,
82    ) -> Result<(), SpecializedMeshPipelineError> {
83        // This is the important part to tell bevy to render this material as a line between vertices
84        descriptor.primitive.polygon_mode = PolygonMode::Line;
85        Ok(())
86    }
87}
88
89/// A list of lines with a start and end position
90#[derive(Debug, Clone)]
91struct LineList {
92    lines: Vec<(Vec3, Vec3)>,
93}
94
95impl From<LineList> for Mesh {
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    }
127}