//! Create a custom material to draw basic lines in 3D use bevy::{ asset::RenderAssetUsages, mesh::{Indices, PrimitiveTopology}, prelude::*, reflect::TypePath, render::render_resource::AsBindGroup, shader::ShaderRef, }; /// This example uses a shader source file from the assets subdirectory const SHADER_ASSET_PATH: &str = "shaders/line_material.wesl"; fn main() { App::new() .add_plugins((DefaultPlugins, MaterialPlugin::::default())) .add_systems(Startup, setup) .run(); } fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { // Spawn a list of lines with start and end points for each lines commands.spawn(( Mesh3d(meshes.add(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)), ], })), MeshMaterial3d(materials.add(LineMaterial { color: LinearRgba::GREEN, })), Transform::from_xyz(-1.5, 0.0, 0.0), )); // Spawn a line strip that goes from point to point commands.spawn(( Mesh3d(meshes.add(LineStrip { points: vec![ Vec3::ZERO, Vec3::new(1.0, 1.0, 0.0), Vec3::new(2.0, 0.0, 0.0), Vec3::new(2.0, 1.0, 0.0), Vec3::new(3.0, 1.0, 0.0), ], indices: Indices::U16(vec![0, 1, u16::MAX /* primitive restart */, 2, 3, 4]), })), MeshMaterial3d(materials.add(LineMaterial { color: LinearRgba::BLUE, })), Transform::from_xyz(0.5, 0.0, 0.0), )); // camera commands.spawn(( Camera3d::default(), Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), )); } #[derive(Asset, TypePath, Default, AsBindGroup, Debug, Clone)] struct LineMaterial { #[uniform(0)] color: LinearRgba, } impl Material for LineMaterial { fn fragment_shader() -> ShaderRef { SHADER_ASSET_PATH.into() } } /// A list of lines with a start and end position #[derive(Debug, Clone)] struct LineList { lines: Vec<(Vec3, Vec3)>, } impl From for Mesh { fn from(line: LineList) -> Self { let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect(); Mesh::new( // This tells wgpu that the positions are list of lines // where every pair is a start and end point PrimitiveTopology::LineList, RenderAssetUsages::RENDER_WORLD, ) // 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)] struct LineStrip { points: Vec, indices: Indices, } impl From for Mesh { fn from(line: LineStrip) -> Self { Mesh::new( // This tells wgpu that the positions are a list of points // where a line will be drawn between each consecutive point PrimitiveTopology::LineStrip, RenderAssetUsages::RENDER_WORLD, ) // Add the point positions as an attribute .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, line.points) .with_inserted_indices(line.indices) } }