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.
This commit is contained in:
Wybe Westra 2023-10-04 04:34:44 +02:00 committed by GitHub
parent 7c5b324484
commit f9e50e767b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 84 additions and 44 deletions

View file

@ -24,7 +24,7 @@ use bevy_render::{
}; };
use bevy_render::{Extract, ExtractSchedule, Render}; use bevy_render::{Extract, ExtractSchedule, Render};
use bevy_utils::tracing::error; use bevy_utils::tracing::error;
use bevy_utils::EntityHashSet; use bevy_utils::EntityHashMap;
pub const WIREFRAME_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(192598014480025766); pub const WIREFRAME_SHADER_HANDLE: Handle<Shader> = 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 /// Overrides the global [`WireframeConfig`] for a single mesh.
#[derive(Component, Debug, Clone, Default, Reflect)] #[derive(Component, Debug, Clone, Default, Reflect, Eq, PartialEq)]
#[reflect(Component, Default)] #[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)] #[derive(Resource, Debug, Clone, Default, ExtractResource, Reflect)]
#[reflect(Resource)] #[reflect(Resource)]
pub struct WireframeConfig { 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, pub global: bool,
} }
#[derive(Resource, Default, Deref, DerefMut)] #[derive(Resource, Default, Deref, DerefMut)]
pub struct Wireframes(EntityHashSet<Entity>); pub struct Wireframes(EntityHashMap<Entity, Wireframe>);
fn extract_wireframes( fn extract_wireframes(
mut wireframes: ResMut<Wireframes>, mut wireframes: ResMut<Wireframes>,
query: Extract<Query<Entity, With<Wireframe>>>, query: Extract<Query<(Entity, &Wireframe)>>,
) { ) {
wireframes.clear(); wireframes.clear();
wireframes.extend(&query); wireframes.extend(
query
.iter()
.map(|(entity, wireframe)| (entity, wireframe.clone())),
);
} }
#[derive(Resource, Clone)] #[derive(Resource, Clone)]
@ -170,31 +181,23 @@ fn queue_wireframes(
}); });
}; };
if wireframe_config.global { visible_entities
visible_entities .entities
.entities .iter()
.iter() .filter_map(|visible_entity| {
.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 render_mesh_instances
.get(visible_entity) .get(visible_entity)
.map(|mesh_instance| (*visible_entity, mesh_instance)) .map(|mesh_instance| (*visible_entity, mesh_instance))
}) } else {
.for_each(add_render_phase); None
} else { }
visible_entities })
.entities .for_each(add_render_phase);
.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);
}
} }
} }

View file

@ -18,36 +18,54 @@ fn main() {
}), }),
WireframePlugin, WireframePlugin,
)) ))
.insert_resource(WireframeToggleTimer(Timer::from_seconds(
1.0,
TimerMode::Repeating,
)))
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, toggle_global_wireframe_setting)
.run(); .run();
} }
/// set up a simple 3D scene /// set up a simple 3D scene
fn setup( fn setup(
mut commands: Commands, mut commands: Commands,
mut wireframe_config: ResMut<WireframeConfig>,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
// To draw the wireframe on all entities, set this to 'true'
wireframe_config.global = false;
// plane // plane
commands.spawn(PbrBundle { commands.spawn(PbrBundle {
mesh: meshes.add(shape::Plane::from_size(5.0).into()), mesh: meshes.add(Mesh::from(shape::Plane::from_size(5.0))),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), material: materials.add(Color::rgb(0.3, 0.3, 0.5).into()),
..default() ..default()
}); });
// cube
commands.spawn(( // Red cube: Never renders a wireframe
PbrBundle { commands
.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), material: materials.add(Color::rgb(0.8, 0.1, 0.1).into()),
transform: Transform::from_xyz(0.0, 0.5, 0.0), transform: Transform::from_xyz(-1.0, 0.5, -1.0),
..default() ..default()
}, })
// This enables wireframe drawing on this entity .insert(Wireframe::NeverRender);
Wireframe, // 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 // light
commands.spawn(PointLightBundle { commands.spawn(PointLightBundle {
transform: Transform::from_xyz(4.0, 8.0, 4.0), transform: Transform::from_xyz(4.0, 8.0, 4.0),
@ -59,3 +77,22 @@ fn setup(
..default() ..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<Time>,
mut timer: ResMut<WireframeToggleTimer>,
mut wireframe_config: ResMut<WireframeConfig>,
) {
if timer.0.tick(time.delta()).just_finished() {
// The global wireframe config enables drawing of wireframes on every mesh, except those with
// `WireframeOverride::NeverRender`. Meshes with `WireframeOverride::AlwaysRender` will
// always have a wireframe, regardless of the global configuration.
wireframe_config.global = !wireframe_config.global;
}
}