Alternate wireframe override api (#10023)

# Objective

https://github.com/bevyengine/bevy/pull/7328 introduced an API to
override the global wireframe config. I believe it is flawed for a few
reasons.

This PR uses a non-breaking API. Instead of making the `Wireframe` an
enum I introduced the `NeverRenderWireframe` component. Here's the
reason why I think this is better:
- Easier to migrate since it doesn't change the old behaviour.
Essentially nothing to migrate. Right now this PR is a breaking change
but I don't think it has to be.
- It's similar to other "per mesh" rendering features like
NotShadowCaster/NotShadowReceiver
- It doesn't force new users to also think about global vs not global if
all they want is to render a wireframe
- This would also let you filter at the query definition level instead
of filtering when running the query

## Solution

- Introduce a `NeverRenderWireframe` component that ignores the global
config

---

## Changelog

- Added a `NeverRenderWireframe` component that ignores the global
`WireframeConfig`
This commit is contained in:
IceSentry 2023-10-05 08:12:08 -04:00 committed by GitHub
parent 2e887b856f
commit a962240866
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 34 deletions

View File

@ -1,15 +1,13 @@
use crate::MeshPipeline;
use crate::{ use crate::{
DrawMesh, MeshPipelineKey, RenderMeshInstance, RenderMeshInstances, SetMeshBindGroup, DrawMesh, MeshPipeline, MeshPipelineKey, RenderMeshInstance, RenderMeshInstances,
SetMeshViewBindGroup, SetMeshBindGroup, SetMeshViewBindGroup,
}; };
use bevy_app::Plugin; use bevy_app::Plugin;
use bevy_asset::{load_internal_asset, Handle}; use bevy_asset::{load_internal_asset, Handle};
use bevy_core_pipeline::core_3d::Opaque3d; use bevy_core_pipeline::core_3d::Opaque3d;
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{prelude::*, reflect::ReflectComponent}; use bevy_ecs::{prelude::*, reflect::ReflectComponent};
use bevy_reflect::std_traits::ReflectDefault; use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_reflect::Reflect;
use bevy_render::{ use bevy_render::{
extract_resource::{ExtractResource, ExtractResourcePlugin}, extract_resource::{ExtractResource, ExtractResourcePlugin},
mesh::{Mesh, MeshVertexBufferLayout}, mesh::{Mesh, MeshVertexBufferLayout},
@ -23,8 +21,7 @@ use bevy_render::{
RenderApp, RenderSet, RenderApp, RenderSet,
}; };
use bevy_render::{Extract, ExtractSchedule, Render}; use bevy_render::{Extract, ExtractSchedule, Render};
use bevy_utils::tracing::error; use bevy_utils::{tracing::error, 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);
@ -41,6 +38,7 @@ impl Plugin for WireframePlugin {
); );
app.register_type::<Wireframe>() app.register_type::<Wireframe>()
.register_type::<NoWireframe>()
.register_type::<WireframeConfig>() .register_type::<WireframeConfig>()
.init_resource::<WireframeConfig>() .init_resource::<WireframeConfig>()
.add_plugins((ExtractResourcePlugin::<WireframeConfig>::default(),)); .add_plugins((ExtractResourcePlugin::<WireframeConfig>::default(),));
@ -50,6 +48,7 @@ impl Plugin for WireframePlugin {
.add_render_command::<Opaque3d, DrawWireframes>() .add_render_command::<Opaque3d, DrawWireframes>()
.init_resource::<SpecializedMeshPipelines<WireframePipeline>>() .init_resource::<SpecializedMeshPipelines<WireframePipeline>>()
.init_resource::<Wireframes>() .init_resource::<Wireframes>()
.init_resource::<NoWireframes>()
.add_systems(ExtractSchedule, extract_wireframes) .add_systems(ExtractSchedule, extract_wireframes)
.add_systems(Render, queue_wireframes.in_set(RenderSet::QueueMeshes)); .add_systems(Render, queue_wireframes.in_set(RenderSet::QueueMeshes));
} }
@ -62,38 +61,46 @@ impl Plugin for WireframePlugin {
} }
} }
/// Overrides the global [`WireframeConfig`] for a single mesh. /// Enables wireframe rendering for any entity it is attached to.
/// It will ignore the [`WireframeConfig`] global setting.
///
/// This requires the [`WireframePlugin`] to be enabled.
#[derive(Component, Debug, Clone, Default, Reflect, Eq, PartialEq)] #[derive(Component, Debug, Clone, Default, Reflect, Eq, PartialEq)]
#[reflect(Component, Default)] #[reflect(Component, Default)]
pub enum Wireframe { pub struct Wireframe;
/// Always render the wireframe for this entity, regardless of global config.
#[default] /// Disables wireframe rendering for any entity it is attached to.
AlwaysRender, /// It will ignore the [`WireframeConfig`] global setting.
/// Never render the wireframe for this entity, regardless of global config. ///
NeverRender, /// This requires the [`WireframePlugin`] to be enabled.
} #[derive(Component, Debug, Clone, Default, Reflect, Eq, PartialEq)]
#[reflect(Component, Default)]
pub struct NoWireframe;
#[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. /// Whether to show wireframes for all meshes.
/// Can be overridden for individual meshes by adding a [`Wireframe`] component. /// Can be overridden for individual meshes by adding a [`Wireframe`] or [`NoWireframe`] component.
pub global: bool, pub global: bool,
} }
#[derive(Resource, Default, Deref, DerefMut)] #[derive(Resource, Default, Deref, DerefMut)]
pub struct Wireframes(EntityHashMap<Entity, Wireframe>); pub struct Wireframes(EntityHashSet<Entity>);
#[derive(Resource, Default, Deref, DerefMut)]
pub struct NoWireframes(EntityHashSet<Entity>);
fn extract_wireframes( fn extract_wireframes(
mut wireframes: ResMut<Wireframes>, mut wireframes: ResMut<Wireframes>,
query: Extract<Query<(Entity, &Wireframe)>>, mut no_wireframes: ResMut<NoWireframes>,
wireframe_query: Extract<Query<Entity, With<Wireframe>>>,
no_wireframe_query: Extract<Query<Entity, With<NoWireframe>>>,
) { ) {
wireframes.clear(); wireframes.clear();
wireframes.extend( wireframes.extend(wireframe_query.iter());
query no_wireframes.clear();
.iter() no_wireframes.extend(no_wireframe_query.iter());
.map(|(entity, wireframe)| (entity, wireframe.clone())),
);
} }
#[derive(Resource, Clone)] #[derive(Resource, Clone)]
@ -137,6 +144,7 @@ fn queue_wireframes(
render_meshes: Res<RenderAssets<Mesh>>, render_meshes: Res<RenderAssets<Mesh>>,
render_mesh_instances: Res<RenderMeshInstances>, render_mesh_instances: Res<RenderMeshInstances>,
wireframes: Res<Wireframes>, wireframes: Res<Wireframes>,
no_wireframes: Res<NoWireframes>,
wireframe_config: Res<WireframeConfig>, wireframe_config: Res<WireframeConfig>,
wireframe_pipeline: Res<WireframePipeline>, wireframe_pipeline: Res<WireframePipeline>,
mut pipelines: ResMut<SpecializedMeshPipelines<WireframePipeline>>, mut pipelines: ResMut<SpecializedMeshPipelines<WireframePipeline>>,
@ -185,11 +193,11 @@ fn queue_wireframes(
.entities .entities
.iter() .iter()
.filter_map(|visible_entity| { .filter_map(|visible_entity| {
let wireframe_override = wireframes.get(visible_entity); if no_wireframes.get(visible_entity).is_some() {
return None;
}
if (wireframe_config.global || wireframe_override == Some(&Wireframe::AlwaysRender)) if wireframe_config.global || wireframes.get(visible_entity).is_some() {
&& 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))

View File

@ -1,7 +1,7 @@
//! Showcases wireframe rendering. //! Showcases wireframe rendering.
use bevy::{ use bevy::{
pbr::wireframe::{Wireframe, WireframeConfig, WireframePlugin}, pbr::wireframe::{NoWireframe, Wireframe, WireframeConfig, WireframePlugin},
prelude::*, prelude::*,
render::{render_resource::WgpuFeatures, settings::WgpuSettings, RenderPlugin}, render::{render_resource::WgpuFeatures, settings::WgpuSettings, RenderPlugin},
}; };
@ -48,7 +48,7 @@ fn setup(
transform: Transform::from_xyz(-1.0, 0.5, -1.0), transform: Transform::from_xyz(-1.0, 0.5, -1.0),
..default() ..default()
}) })
.insert(Wireframe::NeverRender); .insert(NoWireframe);
// Orange cube: Follows global wireframe setting // Orange cube: Follows global wireframe setting
commands.spawn(PbrBundle { commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
@ -64,7 +64,7 @@ fn setup(
transform: Transform::from_xyz(1.0, 0.5, 1.0), transform: Transform::from_xyz(1.0, 0.5, 1.0),
..default() ..default()
}) })
.insert(Wireframe::AlwaysRender); .insert(Wireframe);
// light // light
commands.spawn(PointLightBundle { commands.spawn(PointLightBundle {
@ -83,16 +83,16 @@ fn setup(
struct WireframeToggleTimer(Timer); struct WireframeToggleTimer(Timer);
/// Periodically turns the global wireframe setting on and off, to show the differences between /// Periodically turns the global wireframe setting on and off, to show the differences between
/// [`Wireframe::AlwaysRender`], [`Wireframe::NeverRender`], and no override. /// [`Wireframe`], [`NoWireframe`], and just a mesh.
fn toggle_global_wireframe_setting( fn toggle_global_wireframe_setting(
time: Res<Time>, time: Res<Time>,
mut timer: ResMut<WireframeToggleTimer>, mut timer: ResMut<WireframeToggleTimer>,
mut wireframe_config: ResMut<WireframeConfig>, mut wireframe_config: ResMut<WireframeConfig>,
) { ) {
if timer.0.tick(time.delta()).just_finished() { if timer.0.tick(time.delta()).just_finished() {
// The global wireframe config enables drawing of wireframes on every mesh, except those with // The global wireframe config enables drawing of wireframes on every mesh,
// `WireframeOverride::NeverRender`. Meshes with `WireframeOverride::AlwaysRender` will // except those with `NoWireframe`. Meshes with `Wireframe` will always have a wireframe,
// always have a wireframe, regardless of the global configuration. // regardless of the global configuration.
wireframe_config.global = !wireframe_config.global; wireframe_config.global = !wireframe_config.global;
} }
} }