bevy_mod_gizmos 0.3.1

Visual gizmos to aid with development and debugging in Bevy
Documentation
use crate::{
    interactions::{OnClick, OnClickSystem, OnHover, OnHoverSystem},
    line::Line,
    Gizmo,
};
use bevy::{
    pbr::{NotShadowCaster, NotShadowReceiver},
    prelude::*,
    render::{mesh::Indices, render_resource::PrimitiveTopology},
};
use lazy_static::lazy_static;
use std::sync::RwLock;

lazy_static! {
    pub(crate) static ref GIZMO_SPAWN_BUFFER: RwLock<Vec<Gizmo>> = RwLock::new(vec![]);
    pub(crate) static ref LINE_SPAWN_BUFFER: RwLock<Vec<Line>> = RwLock::new(vec![]);
}

#[derive(Component)]
pub struct GizmoMarker;

pub(crate) fn spawn_gizmos(
    mut commands: Commands,
    mut mesh_handle: Local<Handle<Mesh>>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    if mesh_handle.is_weak() {
        let handle = meshes.add(Mesh::from(shape::Icosphere::default()));
        *mesh_handle = handle;
    }

    if let Ok(mut gizmos) = GIZMO_SPAWN_BUFFER.write() {
        while let Some(gizmo) = gizmos.pop() {
            let mut e = commands.spawn((
                PbrBundle {
                    transform: Transform::from_translation(gizmo.translation)
                        .with_scale(Vec3::splat(gizmo.size)),
                    mesh: mesh_handle.clone(),
                    material: materials.add(StandardMaterial {
                        base_color: gizmo.color,
                        unlit: true,
                        ..Default::default()
                    }),
                    ..Default::default()
                },
                GizmoMarker,
                NotShadowCaster,
                NotShadowReceiver,
            ));

            if let Some(func) = gizmo.interactions.on_hover {
                e.insert(OnHover(func));
            }

            if let Some(func) = gizmo.interactions.on_click {
                e.insert(OnClick(func));
            }

            if let Some(func) = gizmo.interactions.on_hover_system {
                e.insert(OnHoverSystem(func));
            }

            if let Some(func) = gizmo.interactions.on_click_system {
                e.insert(OnClickSystem(func));
            }
        }
    }

    if let Ok(mut lines) = LINE_SPAWN_BUFFER.write() {
        while let Some(line) = lines.pop() {
            let mut mesh = Mesh::new(PrimitiveTopology::LineStrip);

            let mut vertices = vec![];
            for point in &line.points {
                vertices.push([point.x, point.y, point.z]);
            }
            mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertices);

            mesh.insert_attribute(
                Mesh::ATTRIBUTE_NORMAL,
                vec![[0.0, 1.0, 0.0]; line.points.len()],
            );
            mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; line.points.len()]);

            let mut indices = vec![];
            for n in 0..line.points.len() as u16 {
                indices.push(n);
            }
            mesh.set_indices(Some(Indices::U16(indices)));

            commands.spawn((
                PbrBundle {
                    mesh: meshes.add(mesh),
                    material: materials.add(StandardMaterial {
                        base_color: line.color,
                        unlit: true,
                        ..Default::default()
                    }),
                    ..Default::default()
                },
                GizmoMarker,
                NotShadowCaster,
                NotShadowReceiver,
            ));
        }
    }
}

pub(crate) fn cleanup(mut commands: Commands, query: Query<Entity, With<GizmoMarker>>) {
    for entity in query.iter() {
        commands.entity(entity).despawn();
    }
}