Skip to main content

wireframe/
wireframe.rs

1//! Showcases wireframe rendering.
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::css::*,
13    pbr::wireframe::{
14        NoWireframe, Wireframe, WireframeColor, WireframeConfig, WireframeLineWidth,
15        WireframePlugin, WireframeTopology, WireframeXray,
16    },
17    prelude::*,
18    render::{render_resource::WgpuFeatures, settings::WgpuSettings, RenderPlugin},
19};
20
21fn main() {
22    App::new()
23        .add_plugins((
24            DefaultPlugins.set(RenderPlugin {
25                render_creation: WgpuSettings {
26                    // WARN this is a native only feature. It will not work with webgl or webgpu
27                    features: WgpuFeatures::POLYGON_MODE_LINE,
28                    ..default()
29                }
30                .into(),
31                ..default()
32            }),
33            // You need to add this plugin to enable wireframe rendering
34            WireframePlugin::default(),
35        ))
36        // Wireframes can be configured with this resource. This can be changed at runtime.
37        .insert_resource(WireframeConfig {
38            // The global wireframe config enables drawing of wireframes on every mesh,
39            // except those with `NoWireframe`. Meshes with `Wireframe` will always have a wireframe,
40            // regardless of the global configuration.
41            global: true,
42            // Controls the default color of all wireframes. Used as the default color for global wireframes.
43            // Can be changed per mesh using the `WireframeColor` component.
44            default_color: WHITE.into(),
45            xray_mode: false,
46            ..default()
47        })
48        .add_systems(Startup, setup)
49        .add_systems(Update, update_colors)
50        .run();
51}
52
53#[derive(Component)]
54struct ColorToggleCube;
55
56/// set up a simple 3D scene
57fn setup(
58    mut commands: Commands,
59    mut meshes: ResMut<Assets<Mesh>>,
60    mut materials: ResMut<Assets<StandardMaterial>>,
61) {
62    // Red cube: Never renders a wireframe
63    commands.spawn((
64        Mesh3d(meshes.add(Cuboid::default())),
65        MeshMaterial3d(materials.add(Color::from(RED))),
66        Transform::from_xyz(-1.5, 0.5, -1.5),
67        NoWireframe,
68    ));
69    // Orange cube: Follows global wireframe setting
70    commands.spawn((
71        Mesh3d(meshes.add(Cuboid::default())),
72        MeshMaterial3d(materials.add(Color::from(ORANGE))),
73        Transform::from_xyz(-0.5, 0.5, -0.5),
74    ));
75    // Green cube: Always renders a wireframe with custom color
76    commands.spawn((
77        Mesh3d(meshes.add(Cuboid::default())),
78        MeshMaterial3d(materials.add(Color::from(LIME))),
79        Transform::from_xyz(0.5, 0.5, 0.5),
80        Wireframe,
81        // This lets you configure the wireframe color of this entity.
82        // If not set, this will use the color in `WireframeConfig`
83        WireframeColor { color: LIME.into() },
84        WireframeXray(false),
85        ColorToggleCube,
86    ));
87
88    // Purple cube: wireframe with explicit Quads topology override
89    commands.spawn((
90        Mesh3d(meshes.add(Cuboid::default())),
91        MeshMaterial3d(materials.add(Color::from(PURPLE))),
92        Transform::from_xyz(1.5, 0.5, 1.5),
93        Wireframe,
94        WireframeColor {
95            color: YELLOW.into(),
96        },
97        WireframeLineWidth { width: 3.0 },
98        WireframeTopology::Quads,
99    ));
100
101    // plane
102    commands.spawn((
103        Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))),
104        MeshMaterial3d(materials.add(Color::from(BLUE))),
105        // You can insert this component without the `Wireframe` component
106        // to override the color of the global wireframe for this mesh
107        WireframeColor {
108            color: BLACK.into(),
109        },
110    ));
111
112    // light
113    commands.spawn((PointLight::default(), Transform::from_xyz(2.0, 4.0, 2.0)));
114
115    // camera
116    commands.spawn((
117        Camera3d::default(),
118        Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
119    ));
120
121    // Text used to show controls
122    commands.spawn((
123        Text::default(),
124        Node {
125            position_type: PositionType::Absolute,
126            top: px(12),
127            left: px(12),
128            ..default()
129        },
130    ));
131}
132
133/// This system let's you toggle various wireframe settings
134fn update_colors(
135    keyboard_input: Res<ButtonInput<KeyCode>>,
136    mut config: ResMut<WireframeConfig>,
137    mut wireframe_colors: Query<&mut WireframeColor, With<ColorToggleCube>>,
138    mut xray_cubes: Query<&mut WireframeXray, With<ColorToggleCube>>,
139    mut wireframe_widths: Query<&mut WireframeLineWidth>,
140    mut text: Single<&mut Text>,
141) {
142    let current_width = wireframe_widths
143        .iter()
144        .next()
145        .map(|w| w.width)
146        .unwrap_or(1.0);
147    let green_cube_xray = xray_cubes.iter().any(|xray| xray.0);
148
149    text.0 = format!(
150        "Controls
151---------------
152Z - Toggle global
153X - Change global color
154C - Change color of the green cube wireframe
155V - Line width (current: {current_width:.1}px)
156B - Toggle topology (current: {:?})
157N - Toggle global x-ray mode (current: {:?})
158M - Toggle x-ray mode for the green cube (current: {green_cube_xray})
159
160WireframeConfig
161-------------
162Global: {}
163Color: {:?}",
164        config.default_topology, config.xray_mode, config.global, config.default_color,
165    );
166
167    // Toggle showing a wireframe on all meshes
168    if keyboard_input.just_pressed(KeyCode::KeyZ) {
169        config.global = !config.global;
170    }
171
172    // Toggle the global wireframe color
173    if keyboard_input.just_pressed(KeyCode::KeyX) {
174        config.default_color = if config.default_color == WHITE.into() {
175            DEEP_PINK.into()
176        } else {
177            WHITE.into()
178        };
179    }
180
181    // Toggle the color of a wireframe using WireframeColor and not the global color
182    if keyboard_input.just_pressed(KeyCode::KeyC) {
183        for mut color in &mut wireframe_colors {
184            color.color = if color.color == LIME.into() {
185                RED.into()
186            } else {
187                LIME.into()
188            };
189        }
190    }
191
192    if keyboard_input.just_pressed(KeyCode::KeyV) {
193        for mut width in &mut wireframe_widths {
194            width.width = match width.width as u32 {
195                0..=2 => 3.0,
196                3..=4 => 5.0,
197                5..=7 => 10.0,
198                _ => 2.0,
199            };
200        }
201    }
202
203    if keyboard_input.just_pressed(KeyCode::KeyB) {
204        config.default_topology = match config.default_topology {
205            WireframeTopology::Triangles => WireframeTopology::Quads,
206            WireframeTopology::Quads => WireframeTopology::Triangles,
207        };
208    }
209
210    if keyboard_input.just_pressed(KeyCode::KeyN) {
211        config.xray_mode = !config.xray_mode;
212    }
213
214    if keyboard_input.just_pressed(KeyCode::KeyM) {
215        for mut xray in &mut xray_cubes {
216            xray.0 = !xray.0;
217        }
218    }
219}