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_utils::tracing::error;
use bevy_utils::EntityHashSet;
use bevy_utils::EntityHashMap;
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
#[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<Entity>);
pub struct Wireframes(EntityHashMap<Entity, Wireframe>);
fn extract_wireframes(
mut wireframes: ResMut<Wireframes>,
query: Extract<Query<Entity, With<Wireframe>>>,
query: Extract<Query<(Entity, &Wireframe)>>,
) {
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);
}
}

View file

@ -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<WireframeConfig>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// 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<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;
}
}