use bevy::{
asset::{RenderAssetUsages, uuid_handle},
mesh::Indices,
prelude::*,
render::render_resource::PrimitiveTopology,
};
use lyon_tessellation::{self as tess, BuffersBuilder};
use crate::{
draw::{Fill, Stroke},
entity::Shape,
vertex::{VertexBuffers, VertexConstructor},
};
pub(crate) const COLOR_MATERIAL_HANDLE: Handle<ColorMaterial> =
uuid_handle!("7cc661a1-0cd6-c147-129a-2c01882d9580");
pub struct ShapePlugin;
impl Plugin for ShapePlugin {
fn build(&self, app: &mut App) {
let fill_tess = tess::FillTessellator::new();
let stroke_tess = tess::StrokeTessellator::new();
app.insert_resource(FillTessellator(fill_tess))
.insert_resource(StrokeTessellator(stroke_tess))
.configure_sets(
PostUpdate,
BuildShapes
.after(bevy::transform::TransformSystems::Propagate)
.before(bevy::asset::AssetEventSystems),
)
.add_systems(PostUpdate, mesh_shapes_system.in_set(BuildShapes));
if let Some(mut materials) = app.world_mut().get_resource_mut::<Assets<ColorMaterial>>() {
let _ = materials.insert(
&COLOR_MATERIAL_HANDLE,
ColorMaterial {
color: Color::WHITE,
..default()
},
);
} else {
error!("Failed to get Assets<ColorMaterial> resource");
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemSet)]
pub struct BuildShapes;
#[allow(clippy::type_complexity)]
fn mesh_shapes_system(
mut meshes: ResMut<Assets<Mesh>>,
mut fill_tess: ResMut<FillTessellator>,
mut stroke_tess: ResMut<StrokeTessellator>,
mut query: Query<(&Shape, &mut Mesh2d), Changed<Shape>>,
) {
for (shape, mut mesh) in &mut query {
let mut buffers = VertexBuffers::new();
if let Some(fill_mode) = shape.fill {
fill(&mut fill_tess, &shape.path, fill_mode, &mut buffers);
}
if let Some(stroke_mode) = shape.stroke {
stroke(&mut stroke_tess, &shape.path, stroke_mode, &mut buffers);
}
mesh.0 = meshes.add(build_mesh(&buffers));
}
}
#[allow(clippy::trivially_copy_pass_by_ref)] fn fill(
tess: &mut ResMut<FillTessellator>,
path: &tess::path::Path,
mode: Fill,
buffers: &mut VertexBuffers,
) {
if let Err(e) = tess.tessellate_path(
path,
&mode.options,
&mut BuffersBuilder::new(buffers, VertexConstructor { color: mode.color }),
) {
error!("FillTessellator error: {:?}", e);
}
}
#[allow(clippy::trivially_copy_pass_by_ref)] fn stroke(
tess: &mut ResMut<StrokeTessellator>,
path: &tess::path::Path,
mode: Stroke,
buffers: &mut VertexBuffers,
) {
if let Err(e) = tess.tessellate_path(
path,
&mode.options,
&mut BuffersBuilder::new(buffers, VertexConstructor { color: mode.color }),
) {
error!("StrokeTessellator error: {:?}", e);
}
}
fn build_mesh(buffers: &VertexBuffers) -> Mesh {
let mut mesh = Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
);
mesh.insert_indices(Indices::U32(buffers.indices.clone()));
mesh.insert_attribute(
Mesh::ATTRIBUTE_POSITION,
buffers
.vertices
.iter()
.map(|v| [v.position[0], v.position[1], 0.0])
.collect::<Vec<[f32; 3]>>(),
);
mesh.insert_attribute(
Mesh::ATTRIBUTE_COLOR,
buffers
.vertices
.iter()
.map(|v| v.color)
.collect::<Vec<[f32; 4]>>(),
);
mesh
}
#[derive(Resource, Deref, DerefMut)]
struct FillTessellator(lyon_tessellation::FillTessellator);
#[derive(Resource, Deref, DerefMut)]
struct StrokeTessellator(lyon_tessellation::StrokeTessellator);