Skip to content

Commit c407f7b

Browse files
committed
colored wireframe
1 parent ceb163d commit c407f7b

File tree

3 files changed

+169
-40
lines changed

3 files changed

+169
-40
lines changed
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
2+
struct WireframeMaterial {
3+
color: vec4<f32>,
4+
};
25

6+
@group(1) @binding(0)
7+
var<uniform> material: WireframeMaterial;
38
@fragment
49
fn fragment(in: MeshVertexOutput) -> @location(0) vec4<f32> {
5-
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
10+
return material.color;
611
}

crates/bevy_pbr/src/wireframe.rs

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use bevy_asset::{load_internal_asset, Asset, Assets, Handle};
44
use bevy_ecs::prelude::*;
55
use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath, TypeUuid};
66
use bevy_render::{
7+
color::Color,
78
extract_resource::ExtractResource,
89
mesh::{Mesh, MeshVertexBufferLayout},
910
prelude::Shader,
@@ -25,7 +26,6 @@ pub const WIREFRAME_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(19259
2526
/// This is a native only feature.
2627
#[derive(Debug, Default)]
2728
pub struct WireframePlugin;
28-
2929
impl Plugin for WireframePlugin {
3030
fn build(&self, app: &mut bevy_app::App) {
3131
load_internal_asset!(
@@ -43,7 +43,12 @@ impl Plugin for WireframePlugin {
4343
.add_systems(Startup, setup_global_wireframe_material)
4444
.add_systems(
4545
Update,
46-
(apply_global_wireframe_material, apply_wireframe_material),
46+
(
47+
global_color_changed,
48+
apply_global_wireframe_material,
49+
wireframe_color_changed,
50+
apply_wireframe_material,
51+
),
4752
);
4853
}
4954
}
@@ -56,6 +61,17 @@ impl Plugin for WireframePlugin {
5661
#[reflect(Component, Default)]
5762
pub struct Wireframe;
5863

64+
/// Sets the color of the [`Wireframe`] of the entity it is attached to.
65+
/// If this component is present but there's no [`Wireframe`] component,
66+
/// it will still affect the color of the wireframe when [`WireframeConfig::global`] is set to true.
67+
///
68+
/// This overrides the [`WireframeConfig::color`].
69+
#[derive(Component, Debug, Clone, Default, Reflect)]
70+
#[reflect(Component, Default)]
71+
pub struct WireframeColor {
72+
pub color: Color,
73+
}
74+
5975
/// Disables wireframe rendering for any entity it is attached to.
6076
/// It will ignore the [`WireframeConfig`] global setting.
6177
///
@@ -70,6 +86,10 @@ pub struct WireframeConfig {
7086
/// Whether to show wireframes for all meshes.
7187
/// Can be overridden for individual meshes by adding a [`Wireframe`] or [`NoWireframe`] component.
7288
pub global: bool,
89+
/// If [`Self::global`] is set, any [`Entity`] that does not have a [`Wireframe`] component attached to it will have
90+
/// wireframes using this color. Otherwise, this will be the fallback color for any entity that has a [`Wireframe`],
91+
/// but no [`WireframeColor`].
92+
pub default_color: Color,
7393
}
7494

7595
#[derive(Resource)]
@@ -81,19 +101,43 @@ struct GlobalWireframeMaterial {
81101
fn setup_global_wireframe_material(
82102
mut commands: Commands,
83103
mut materials: ResMut<Assets<WireframeMaterial>>,
104+
config: Res<WireframeConfig>,
84105
) {
85106
// Create the handle used for the global material
86107
commands.insert_resource(GlobalWireframeMaterial {
87-
handle: materials.add(WireframeMaterial {}),
108+
handle: materials.add(WireframeMaterial {
109+
color: config.default_color,
110+
}),
88111
});
89112
}
90113

114+
/// Updates the wireframe material when the color in [`WireframeColor`] changes
115+
#[allow(clippy::type_complexity)]
116+
fn wireframe_color_changed(
117+
mut materials: ResMut<Assets<WireframeMaterial>>,
118+
mut colors_changed: Query<
119+
(&mut Handle<WireframeMaterial>, &WireframeColor),
120+
(With<Wireframe>, Changed<WireframeColor>),
121+
>,
122+
) {
123+
for (mut handle, wireframe_color) in &mut colors_changed {
124+
*handle = materials.add(WireframeMaterial {
125+
color: wireframe_color.color,
126+
});
127+
}
128+
}
129+
91130
/// Applies or remove the wireframe material to any mesh with a [`Wireframe`] component.
92131
fn apply_wireframe_material(
93132
mut commands: Commands,
94133
mut materials: ResMut<Assets<WireframeMaterial>>,
95-
wireframes: Query<Entity, (With<Wireframe>, Without<Handle<WireframeMaterial>>)>,
134+
wireframes: Query<
135+
(Entity, Option<&WireframeColor>),
136+
(With<Wireframe>, Without<Handle<WireframeMaterial>>),
137+
>,
96138
mut removed_wireframes: RemovedComponents<Wireframe>,
139+
config: Res<WireframeConfig>,
140+
global_material: Res<GlobalWireframeMaterial>,
97141
) {
98142
for e in removed_wireframes.read() {
99143
if let Some(mut commands) = commands.get_entity(e) {
@@ -102,12 +146,34 @@ fn apply_wireframe_material(
102146
}
103147

104148
let mut wireframes_to_spawn = vec![];
105-
for e in &wireframes {
106-
wireframes_to_spawn.push((e, materials.add(WireframeMaterial {})));
149+
for (e, wireframe_color) in &wireframes {
150+
let material = if let Some(wireframe_color) = wireframe_color {
151+
materials.add(WireframeMaterial {
152+
color: wireframe_color.color,
153+
})
154+
} else {
155+
// If there's no color specified we can use the global material since it's already set to use the default_color
156+
global_material.handle.clone()
157+
};
158+
wireframes_to_spawn.push((e, material));
107159
}
108160
commands.insert_or_spawn_batch(wireframes_to_spawn);
109161
}
110162

163+
/// Updates the wireframe material of all entities without a [`WireframeColor`] or without a [`Wireframe`] component
164+
fn global_color_changed(
165+
config: Res<WireframeConfig>,
166+
mut materials: ResMut<Assets<WireframeMaterial>>,
167+
global_material: Res<GlobalWireframeMaterial>,
168+
) {
169+
if !config.is_changed() {
170+
return;
171+
}
172+
if let Some(global_material) = materials.get_mut(&global_material.handle) {
173+
global_material.color = config.default_color;
174+
}
175+
}
176+
111177
/// Applies or removes a wireframe material on any mesh without a [`Wireframe`] component.
112178
#[allow(clippy::type_complexity)]
113179
fn apply_global_wireframe_material(
@@ -154,7 +220,10 @@ fn apply_global_wireframe_material(
154220

155221
#[derive(Default, AsBindGroup, TypeUuid, TypePath, Debug, Clone, Asset)]
156222
#[uuid = "9e694f70-9963-4418-8bc1-3474c66b13b8"]
157-
struct WireframeMaterial {}
223+
struct WireframeMaterial {
224+
#[uniform(0)]
225+
pub color: Color,
226+
}
158227

159228
impl Material for WireframeMaterial {
160229
fn fragment_shader() -> ShaderRef {

examples/3d/wireframe.rs

Lines changed: 87 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use bevy::{
1717
RenderPlugin,
1818
},
1919
};
20+
use bevy_internal::pbr::wireframe::WireframeColor;
2021

2122
fn main() {
2223
App::new()
@@ -31,12 +32,18 @@ fn main() {
3132
// You need to add this plugin to enable wireframe rendering
3233
WireframePlugin,
3334
))
34-
.insert_resource(WireframeToggleTimer(Timer::from_seconds(
35-
1.0,
36-
TimerMode::Repeating,
37-
)))
35+
// Wireframes can be configured with this resource. This can be changed at runtime.
36+
.insert_resource(WireframeConfig {
37+
// The global wireframe config enables drawing of wireframes on every mesh,
38+
// except those with `NoWireframe`. Meshes with `Wireframe` will always have a wireframe,
39+
// regardless of the global configuration.
40+
global: true,
41+
// Controls the default color of all wireframes. Used as the default color for global wireframes.
42+
// Can be changed per mesh using the `WireframeColor` component.
43+
default_color: Color::WHITE,
44+
})
3845
.add_systems(Startup, setup)
39-
.add_systems(Update, toggle_global_wireframe_setting)
46+
.add_systems(Update, update_colors)
4047
.run();
4148
}
4249

@@ -49,63 +56,111 @@ fn setup(
4956
// plane
5057
commands.spawn(PbrBundle {
5158
mesh: meshes.add(Mesh::from(shape::Plane::from_size(5.0))),
52-
material: materials.add(Color::rgb(0.3, 0.3, 0.5).into()),
59+
material: materials.add(Color::BLUE.into()),
5360
..default()
5461
});
5562

5663
// Red cube: Never renders a wireframe
57-
commands
58-
.spawn(PbrBundle {
64+
commands.spawn((
65+
PbrBundle {
5966
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
60-
material: materials.add(Color::rgb(0.8, 0.1, 0.1).into()),
67+
material: materials.add(Color::RED.into()),
6168
transform: Transform::from_xyz(-1.0, 0.5, -1.0),
6269
..default()
63-
})
64-
.insert(NoWireframe);
70+
},
71+
NoWireframe,
72+
));
6573
// Orange cube: Follows global wireframe setting
6674
commands.spawn(PbrBundle {
6775
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
68-
material: materials.add(Color::rgb(0.8, 0.8, 0.1).into()),
76+
material: materials.add(Color::ORANGE.into()),
6977
transform: Transform::from_xyz(0.0, 0.5, 0.0),
7078
..default()
7179
});
7280
// Green cube: Always renders a wireframe
73-
commands
74-
.spawn(PbrBundle {
81+
commands.spawn((
82+
PbrBundle {
7583
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
76-
material: materials.add(Color::rgb(0.1, 0.8, 0.1).into()),
84+
material: materials.add(Color::GREEN.into()),
7785
transform: Transform::from_xyz(1.0, 0.5, 1.0),
7886
..default()
79-
})
80-
.insert(Wireframe);
87+
},
88+
Wireframe,
89+
// This lets you configure the wireframe color of this entity.
90+
// If not set, this will use the color in `WireframeConfig`
91+
WireframeColor {
92+
color: Color::GREEN,
93+
},
94+
));
8195

8296
// light
8397
commands.spawn(PointLightBundle {
8498
transform: Transform::from_xyz(4.0, 8.0, 4.0),
8599
..default()
86100
});
101+
87102
// camera
88103
commands.spawn(Camera3dBundle {
89104
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
90105
..default()
91106
});
92-
}
93107

94-
/// This timer is used to periodically toggle the wireframe rendering.
95-
#[derive(Resource)]
96-
struct WireframeToggleTimer(Timer);
108+
// Text used to show controls
109+
commands.spawn(
110+
TextBundle::from_section("", TextStyle::default()).with_style(Style {
111+
position_type: PositionType::Absolute,
112+
top: Val::Px(10.0),
113+
left: Val::Px(10.0),
114+
..default()
115+
}),
116+
);
117+
}
97118

98-
/// Periodically turns the global wireframe setting on and off, to show the differences between
99-
/// [`Wireframe`], [`NoWireframe`], and just a mesh.
100-
fn toggle_global_wireframe_setting(
101-
time: Res<Time>,
102-
mut timer: ResMut<WireframeToggleTimer>,
103-
mut wireframe_config: ResMut<WireframeConfig>,
119+
/// This system let's you toggle various wireframe settings
120+
fn update_colors(
121+
keyboard_input: Res<Input<KeyCode>>,
122+
mut config: ResMut<WireframeConfig>,
123+
mut wireframe_colors: Query<&mut WireframeColor>,
124+
mut text: Query<&mut Text>,
104125
) {
105-
if timer.0.tick(time.delta()).just_finished() {
106-
// The global wireframe config enables drawing of wireframes on every mesh,
107-
// except those with `NoWireframe`. Meshes with `Wireframe` will always have a wireframe,
108-
// regardless of the global configuration.
109-
wireframe_config.global = !wireframe_config.global;
126+
text.single_mut().sections[0].value = format!(
127+
"
128+
Controls
129+
---------------
130+
Z - Toggle global
131+
X - Change global color
132+
C - Change color of the green cube wireframe
133+
134+
WireframeConfig
135+
-------------
136+
Global: {}
137+
Color: {:?}
138+
",
139+
config.global, config.default_color,
140+
);
141+
142+
// Toggle showing a wireframe on all meshes
143+
if keyboard_input.just_pressed(KeyCode::Z) {
144+
config.global = !config.global;
145+
}
146+
147+
// Toggle the global wireframe color
148+
if keyboard_input.just_pressed(KeyCode::X) {
149+
config.default_color = if config.default_color == Color::WHITE {
150+
Color::PINK
151+
} else {
152+
Color::WHITE
153+
};
154+
}
155+
156+
// Toggle the color of a wireframe using WireframeColor and not the global color
157+
if keyboard_input.just_pressed(KeyCode::C) {
158+
for mut color in &mut wireframe_colors {
159+
color.color = if color.color == Color::GREEN {
160+
Color::RED
161+
} else {
162+
Color::GREEN
163+
};
164+
}
110165
}
111166
}

0 commit comments

Comments
 (0)