use crate::{Material, MaterialPipeline, MaterialPipelineKey, MaterialPlugin};
use bevy_app::{Plugin, Startup, Update};
use bevy_asset::{load_internal_asset, Asset, Assets, Handle};
use bevy_ecs::prelude::*;
use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
use bevy_render::{
color::Color,
extract_resource::ExtractResource,
mesh::{Mesh, MeshVertexBufferLayout},
prelude::*,
render_resource::*,
};
pub const WIREFRAME_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(192598014480025766);
#[derive(Debug, Default)]
pub struct WireframePlugin;
impl Plugin for WireframePlugin {
fn build(&self, app: &mut bevy_app::App) {
load_internal_asset!(
app,
WIREFRAME_SHADER_HANDLE,
"render/wireframe.wgsl",
Shader::from_wgsl
);
app.register_type::<Wireframe>()
.register_type::<NoWireframe>()
.register_type::<WireframeConfig>()
.register_type::<WireframeColor>()
.init_resource::<WireframeConfig>()
.add_plugins(MaterialPlugin::<WireframeMaterial>::default())
.add_systems(Startup, setup_global_wireframe_material)
.add_systems(
Update,
(
global_color_changed.run_if(resource_changed::<WireframeConfig>),
wireframe_color_changed,
(apply_wireframe_material, apply_global_wireframe_material).chain(),
),
);
}
}
#[derive(Component, Debug, Clone, Default, Reflect, Eq, PartialEq)]
#[reflect(Component, Default)]
pub struct Wireframe;
#[derive(Component, Debug, Clone, Default, Reflect)]
#[reflect(Component, Default)]
pub struct WireframeColor {
pub color: Color,
}
#[derive(Component, Debug, Clone, Default, Reflect, Eq, PartialEq)]
#[reflect(Component, Default)]
pub struct NoWireframe;
#[derive(Resource, Debug, Clone, Default, ExtractResource, Reflect)]
#[reflect(Resource)]
pub struct WireframeConfig {
pub global: bool,
pub default_color: Color,
}
#[derive(Resource)]
struct GlobalWireframeMaterial {
handle: Handle<WireframeMaterial>,
}
fn setup_global_wireframe_material(
mut commands: Commands,
mut materials: ResMut<Assets<WireframeMaterial>>,
config: Res<WireframeConfig>,
) {
commands.insert_resource(GlobalWireframeMaterial {
handle: materials.add(WireframeMaterial {
color: config.default_color,
}),
});
}
fn global_color_changed(
config: Res<WireframeConfig>,
mut materials: ResMut<Assets<WireframeMaterial>>,
global_material: Res<GlobalWireframeMaterial>,
) {
if let Some(global_material) = materials.get_mut(&global_material.handle) {
global_material.color = config.default_color;
}
}
#[allow(clippy::type_complexity)]
fn wireframe_color_changed(
mut materials: ResMut<Assets<WireframeMaterial>>,
mut colors_changed: Query<
(&mut Handle<WireframeMaterial>, &WireframeColor),
(With<Wireframe>, Changed<WireframeColor>),
>,
) {
for (mut handle, wireframe_color) in &mut colors_changed {
*handle = materials.add(WireframeMaterial {
color: wireframe_color.color,
});
}
}
fn apply_wireframe_material(
mut commands: Commands,
mut materials: ResMut<Assets<WireframeMaterial>>,
wireframes: Query<
(Entity, Option<&WireframeColor>),
(With<Wireframe>, Without<Handle<WireframeMaterial>>),
>,
no_wireframes: Query<Entity, (With<NoWireframe>, With<Handle<WireframeMaterial>>)>,
mut removed_wireframes: RemovedComponents<Wireframe>,
global_material: Res<GlobalWireframeMaterial>,
) {
for e in removed_wireframes.read().chain(no_wireframes.iter()) {
if let Some(mut commands) = commands.get_entity(e) {
commands.remove::<Handle<WireframeMaterial>>();
}
}
let mut wireframes_to_spawn = vec![];
for (e, wireframe_color) in &wireframes {
let material = if let Some(wireframe_color) = wireframe_color {
materials.add(WireframeMaterial {
color: wireframe_color.color,
})
} else {
global_material.handle.clone()
};
wireframes_to_spawn.push((e, material));
}
commands.insert_or_spawn_batch(wireframes_to_spawn);
}
type WireframeFilter = (With<Handle<Mesh>>, Without<Wireframe>, Without<NoWireframe>);
fn apply_global_wireframe_material(
mut commands: Commands,
config: Res<WireframeConfig>,
meshes_without_material: Query<Entity, (WireframeFilter, Without<Handle<WireframeMaterial>>)>,
meshes_with_global_material: Query<Entity, (WireframeFilter, With<Handle<WireframeMaterial>>)>,
global_material: Res<GlobalWireframeMaterial>,
) {
if config.global {
let mut material_to_spawn = vec![];
for e in &meshes_without_material {
material_to_spawn.push((e, global_material.handle.clone()));
}
commands.insert_or_spawn_batch(material_to_spawn);
} else {
for e in &meshes_with_global_material {
commands.entity(e).remove::<Handle<WireframeMaterial>>();
}
}
}
#[derive(Default, AsBindGroup, TypePath, Debug, Clone, Asset)]
pub struct WireframeMaterial {
#[uniform(0)]
pub color: Color,
}
impl Material for WireframeMaterial {
fn fragment_shader() -> ShaderRef {
WIREFRAME_SHADER_HANDLE.into()
}
fn specialize(
_pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor,
_layout: &MeshVertexBufferLayout,
_key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> {
descriptor.primitive.polygon_mode = PolygonMode::Line;
descriptor.depth_stencil.as_mut().unwrap().bias.slope_scale = 1.0;
Ok(())
}
}