Skip to main content

wireframe_2d/
wireframe_2d.rs

1//! Showcases wireframe rendering for 2d meshes.
2//!
3//! Wireframes currently do not work when using webgl or webgpu.
4//! Supported platforms:
5//! - DX12
6//! - Vulkan
7//! - Metal
8//!
9//! This is a native only feature.
10
11use bevy::{
12    color::palettes::basic::{GREEN, RED, WHITE},
13    feathers::{theme::UiTheme, FeathersPlugins},
14    prelude::*,
15    render::{render_resource::WgpuFeatures, settings::WgpuSettings, RenderPlugin},
16    sprite_render::{
17        NoWireframe2d, Wireframe2d, Wireframe2dColor, Wireframe2dConfig, Wireframe2dPlugin,
18    },
19    ui_widgets::{radio_self_update, ValueChange},
20};
21
22use radio::{feathers_option_buttons, main_ui_node_scene, RadioButtonOptionValue};
23
24#[path = "../helpers/radio.rs"]
25mod radio;
26
27#[path = "../helpers/theme.rs"]
28mod theme;
29
30fn main() {
31    App::new()
32        .add_plugins((
33            DefaultPlugins.set(RenderPlugin {
34                render_creation: WgpuSettings {
35                    // WARN this is a native only feature. It will not work with webgl or webgpu
36                    features: WgpuFeatures::POLYGON_MODE_LINE,
37                    ..default()
38                }
39                .into(),
40                ..default()
41            }),
42            // You need to add this plugin to enable wireframe rendering
43            Wireframe2dPlugin::default(),
44            FeathersPlugins,
45        ))
46        // Wireframes can be configured with this resource. This can be changed at runtime.
47        .insert_resource(Wireframe2dConfig {
48            // The global wireframe config enables drawing of wireframes on every mesh,
49            // except those with `NoWireframe2d`. Meshes with `Wireframe2d` will always have a wireframe,
50            // regardless of the global configuration.
51            global: true,
52            // Controls the default color of all wireframes. Used as the default color for global wireframes.
53            // Can be changed per mesh using the `Wireframe2dColor` component.
54            default_color: WHITE.into(),
55        })
56        .insert_resource(UiTheme(theme::basic_example_theme(Color::WHITE)))
57        .add_systems(Startup, setup)
58        .add_observer(update_radio_button)
59        .add_observer(radio_self_update)
60        .run();
61}
62
63/// Whether the global wireframe setting is on or off.
64#[derive(Clone, Copy, Component, Default, PartialEq, Debug)]
65enum GlobalWireframeSetting {
66    #[default]
67    On,
68    Off,
69}
70
71/// Whether the global color setting is red or white.
72#[derive(Clone, Copy, Component, Default, PartialEq, Debug)]
73enum GlobalColorSetting {
74    #[default]
75    Red,
76    White,
77}
78
79impl From<GlobalColorSetting> for Color {
80    fn from(value: GlobalColorSetting) -> Self {
81        match value {
82            GlobalColorSetting::Red => RED.into(),
83            GlobalColorSetting::White => WHITE.into(),
84        }
85    }
86}
87
88/// Whether the color of the circle wireframe is red or green.
89#[derive(Clone, Copy, Component, Default, PartialEq, Debug)]
90enum ColorCircleWireframeSetting {
91    #[default]
92    Red,
93    Green,
94}
95
96impl From<ColorCircleWireframeSetting> for Color {
97    fn from(value: ColorCircleWireframeSetting) -> Self {
98        match value {
99            ColorCircleWireframeSetting::Red => RED.into(),
100            ColorCircleWireframeSetting::Green => GREEN.into(),
101        }
102    }
103}
104
105/// Set up a simple 3D scene
106fn setup(
107    mut commands: Commands,
108    mut meshes: ResMut<Assets<Mesh>>,
109    mut materials: ResMut<Assets<ColorMaterial>>,
110) {
111    commands.spawn_scene(bsn! {
112        @main_ui_node_scene()
113        Children [
114            @feathers_option_buttons("Toggle global",
115                &[
116                    (GlobalWireframeSetting::On, "ON"),
117                    (GlobalWireframeSetting::Off, "OFF"),
118                ], 0)
119            --
120            @feathers_option_buttons("Change global color",
121                &[
122                    (GlobalColorSetting::Red, "RED"),
123                    (GlobalColorSetting::White, "WHITE"),
124                ], 1)
125            --
126            @feathers_option_buttons("Change color of the circle wireframe",
127                &[
128                    (ColorCircleWireframeSetting::Red, "RED"),
129                    (ColorCircleWireframeSetting::Green, "GREEN"),
130                ], 1)
131        ]
132    });
133    // Triangle: Never renders a wireframe
134    commands.spawn((
135        Mesh2d(meshes.add(Triangle2d::new(
136            Vec2::new(0.0, 50.0),
137            Vec2::new(-50.0, -50.0),
138            Vec2::new(50.0, -50.0),
139        ))),
140        MeshMaterial2d(materials.add(Color::BLACK)),
141        Transform::from_xyz(-150.0, 0.0, 0.0),
142        NoWireframe2d,
143    ));
144    // Rectangle: Follows global wireframe setting
145    commands.spawn((
146        Mesh2d(meshes.add(Rectangle::new(100.0, 100.0))),
147        MeshMaterial2d(materials.add(Color::BLACK)),
148        Transform::from_xyz(0.0, 0.0, 0.0),
149    ));
150    // Circle: Always renders a wireframe
151    commands.spawn((
152        Mesh2d(meshes.add(Circle::new(50.0))),
153        MeshMaterial2d(materials.add(Color::BLACK)),
154        Transform::from_xyz(150.0, 0.0, 0.0),
155        Wireframe2d,
156        // This lets you configure the wireframe color of this entity.
157        // If not set, this will use the color in `WireframeConfig`
158        Wireframe2dColor {
159            color: GREEN.into(),
160        },
161    ));
162
163    commands.spawn(Camera2d);
164}
165
166/// This system lets you toggle various wireframe settings
167fn update_radio_button(
168    event: On<ValueChange<Entity>>,
169    toggle_global_query: Query<&RadioButtonOptionValue<GlobalWireframeSetting>>,
170    global_color_query: Query<&RadioButtonOptionValue<GlobalColorSetting>>,
171    color_circle_query: Query<&RadioButtonOptionValue<ColorCircleWireframeSetting>>,
172    mut config: ResMut<Wireframe2dConfig>,
173    mut wireframe_colors: Query<&mut Wireframe2dColor>,
174) {
175    // Toggle showing a wireframe on all meshes
176    if let Ok(RadioButtonOptionValue(toggle_global)) = toggle_global_query.get(event.value) {
177        config.global = matches!(toggle_global, GlobalWireframeSetting::On);
178    }
179
180    // Toggle the global wireframe color
181    if let Ok(RadioButtonOptionValue(global_color)) = global_color_query.get(event.value) {
182        config.default_color = (*global_color).into();
183    }
184
185    // Toggle the color of a wireframe using `Wireframe2dColor` and not the global color
186    if let Ok(RadioButtonOptionValue(color_circle)) = color_circle_query.get(event.value) {
187        for mut color in &mut wireframe_colors {
188            color.color = (*color_circle).into();
189        }
190    }
191}