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:
parent
2e887b856f
commit
a962240866
@ -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))
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user