From f9e50e767b76382527bff93385247829461758a0 Mon Sep 17 00:00:00 2001 From: Wybe Westra Date: Wed, 4 Oct 2023 04:34:44 +0200 Subject: [PATCH] Allow overriding global wireframe setting. (#7328) # Objective Allow the user to choose between "Add wireframes to these specific entities" or "Add wireframes to everything _except_ these specific entities". Fixes #7309 # Solution Make the `Wireframe` component act like an override to the global configuration. Having `global` set to `false`, and adding a `Wireframe` with `enable: true` acts exactly as before. But now the opposite is also possible: Set `global` to `true` and add a `Wireframe` with `enable: false` will draw wireframes for everything _except_ that entity. Updated the example to show how overriding the global config works. --- crates/bevy_pbr/src/wireframe.rs | 63 ++++++++++++++++--------------- examples/3d/wireframe.rs | 65 +++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 44 deletions(-) diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 9566afffb9..9d29fcdbee 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -24,7 +24,7 @@ use bevy_render::{ }; use bevy_render::{Extract, ExtractSchedule, Render}; use bevy_utils::tracing::error; -use bevy_utils::EntityHashSet; +use bevy_utils::EntityHashMap; pub const WIREFRAME_SHADER_HANDLE: Handle = Handle::weak_from_u128(192598014480025766); @@ -62,27 +62,38 @@ impl Plugin for WireframePlugin { } } -/// Controls whether an entity should rendered in wireframe-mode if the [`WireframePlugin`] is enabled -#[derive(Component, Debug, Clone, Default, Reflect)] +/// Overrides the global [`WireframeConfig`] for a single mesh. +#[derive(Component, Debug, Clone, Default, Reflect, Eq, PartialEq)] #[reflect(Component, Default)] -pub struct Wireframe; +pub enum Wireframe { + /// Always render the wireframe for this entity, regardless of global config. + #[default] + AlwaysRender, + /// Never render the wireframe for this entity, regardless of global config. + NeverRender, +} #[derive(Resource, Debug, Clone, Default, ExtractResource, Reflect)] #[reflect(Resource)] pub struct WireframeConfig { - /// Whether to show wireframes for all meshes. If `false`, only meshes with a [`Wireframe`] component will be rendered. + /// Whether to show wireframes for all meshes. + /// Can be overridden for individual meshes by adding a [`Wireframe`] component. pub global: bool, } #[derive(Resource, Default, Deref, DerefMut)] -pub struct Wireframes(EntityHashSet); +pub struct Wireframes(EntityHashMap); fn extract_wireframes( mut wireframes: ResMut, - query: Extract>>, + query: Extract>, ) { wireframes.clear(); - wireframes.extend(&query); + wireframes.extend( + query + .iter() + .map(|(entity, wireframe)| (entity, wireframe.clone())), + ); } #[derive(Resource, Clone)] @@ -170,31 +181,23 @@ fn queue_wireframes( }); }; - if wireframe_config.global { - visible_entities - .entities - .iter() - .filter_map(|visible_entity| { + visible_entities + .entities + .iter() + .filter_map(|visible_entity| { + let wireframe_override = wireframes.get(visible_entity); + + if (wireframe_config.global || wireframe_override == Some(&Wireframe::AlwaysRender)) + && wireframe_override != Some(&Wireframe::NeverRender) + { render_mesh_instances .get(visible_entity) .map(|mesh_instance| (*visible_entity, mesh_instance)) - }) - .for_each(add_render_phase); - } else { - visible_entities - .entities - .iter() - .filter_map(|visible_entity| { - if wireframes.contains(visible_entity) { - render_mesh_instances - .get(visible_entity) - .map(|mesh_instance| (*visible_entity, mesh_instance)) - } else { - None - } - }) - .for_each(add_render_phase); - } + } else { + None + } + }) + .for_each(add_render_phase); } } diff --git a/examples/3d/wireframe.rs b/examples/3d/wireframe.rs index 0192b6ee43..bdab092afc 100644 --- a/examples/3d/wireframe.rs +++ b/examples/3d/wireframe.rs @@ -18,36 +18,54 @@ fn main() { }), WireframePlugin, )) + .insert_resource(WireframeToggleTimer(Timer::from_seconds( + 1.0, + TimerMode::Repeating, + ))) .add_systems(Startup, setup) + .add_systems(Update, toggle_global_wireframe_setting) .run(); } /// set up a simple 3D scene fn setup( mut commands: Commands, - mut wireframe_config: ResMut, mut meshes: ResMut>, mut materials: ResMut>, ) { - // To draw the wireframe on all entities, set this to 'true' - wireframe_config.global = false; // plane commands.spawn(PbrBundle { - mesh: meshes.add(shape::Plane::from_size(5.0).into()), - material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), + mesh: meshes.add(Mesh::from(shape::Plane::from_size(5.0))), + material: materials.add(Color::rgb(0.3, 0.3, 0.5).into()), ..default() }); - // cube - commands.spawn(( - PbrBundle { + + // Red cube: Never renders a wireframe + commands + .spawn(PbrBundle { mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), - material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), - transform: Transform::from_xyz(0.0, 0.5, 0.0), + material: materials.add(Color::rgb(0.8, 0.1, 0.1).into()), + transform: Transform::from_xyz(-1.0, 0.5, -1.0), ..default() - }, - // This enables wireframe drawing on this entity - Wireframe, - )); + }) + .insert(Wireframe::NeverRender); + // Orange cube: Follows global wireframe setting + commands.spawn(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + material: materials.add(Color::rgb(0.8, 0.8, 0.1).into()), + transform: Transform::from_xyz(0.0, 0.5, 0.0), + ..default() + }); + // Green cube: Always renders a wireframe + commands + .spawn(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + material: materials.add(Color::rgb(0.1, 0.8, 0.1).into()), + transform: Transform::from_xyz(1.0, 0.5, 1.0), + ..default() + }) + .insert(Wireframe::AlwaysRender); + // light commands.spawn(PointLightBundle { transform: Transform::from_xyz(4.0, 8.0, 4.0), @@ -59,3 +77,22 @@ fn setup( ..default() }); } + +/// This timer is used to periodically toggle the wireframe rendering. +#[derive(Resource)] +struct WireframeToggleTimer(Timer); + +/// Periodically turns the global wireframe setting on and off, to show the differences between +/// [`Wireframe::AlwaysRender`], [`Wireframe::NeverRender`], and no override. +fn toggle_global_wireframe_setting( + time: Res