Skip to content

Commit

Permalink
Allow overriding global wireframe setting. (#7328)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
Wcubed committed Oct 4, 2023
1 parent 7c5b324 commit f9e50e7
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 44 deletions.
63 changes: 33 additions & 30 deletions crates/bevy_pbr/src/wireframe.rs
Expand Up @@ -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);

Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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);
}
}

Expand Down
65 changes: 51 additions & 14 deletions examples/3d/wireframe.rs
Expand Up @@ -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()
});

// 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.1, 0.1).into()),
transform: Transform::from_xyz(-1.0, 0.5, -1.0),
..default()
})
.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()
});
// cube
commands.spawn((
PbrBundle {
// 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.8, 0.7, 0.6).into()),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
material: materials.add(Color::rgb(0.1, 0.8, 0.1).into()),
transform: Transform::from_xyz(1.0, 0.5, 1.0),
..default()
},
// This enables wireframe drawing on this entity
Wireframe,
));
})
.insert(Wireframe::AlwaysRender);

// light
commands.spawn(PointLightBundle {
transform: Transform::from_xyz(4.0, 8.0, 4.0),
Expand All @@ -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;
}
}

0 comments on commit f9e50e7

Please sign in to comment.