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,
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            ..default()
46        })
47        .add_systems(Startup, setup)
48        .add_systems(Update, update_colors)
49        .run();
50}
51
52#[derive(Component)]
53struct ColorToggleCube;
54
55/// set up a simple 3D scene
56fn setup(
57    mut commands: Commands,
58    mut meshes: ResMut<Assets<Mesh>>,
59    mut materials: ResMut<Assets<StandardMaterial>>,
60) {
61    // Red cube: Never renders a wireframe
62    commands.spawn((
63        Mesh3d(meshes.add(Cuboid::default())),
64        MeshMaterial3d(materials.add(Color::from(RED))),
65        Transform::from_xyz(-1.5, 0.5, -1.5),
66        NoWireframe,
67    ));
68    // Orange cube: Follows global wireframe setting
69    commands.spawn((
70        Mesh3d(meshes.add(Cuboid::default())),
71        MeshMaterial3d(materials.add(Color::from(ORANGE))),
72        Transform::from_xyz(-0.5, 0.5, -0.5),
73    ));
74    // Green cube: Always renders a wireframe with custom color
75    commands.spawn((
76        Mesh3d(meshes.add(Cuboid::default())),
77        MeshMaterial3d(materials.add(Color::from(LIME))),
78        Transform::from_xyz(0.5, 0.5, 0.5),
79        Wireframe,
80        // This lets you configure the wireframe color of this entity.
81        // If not set, this will use the color in `WireframeConfig`
82        WireframeColor { color: LIME.into() },
83        ColorToggleCube,
84    ));
85
86    // Purple cube: wireframe with explicit Quads topology override
87    commands.spawn((
88        Mesh3d(meshes.add(Cuboid::default())),
89        MeshMaterial3d(materials.add(Color::from(PURPLE))),
90        Transform::from_xyz(1.5, 0.5, 1.5),
91        Wireframe,
92        WireframeColor {
93            color: YELLOW.into(),
94        },
95        WireframeLineWidth { width: 3.0 },
96        WireframeTopology::Quads,
97    ));
98
99    // plane
100    commands.spawn((
101        Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))),
102        MeshMaterial3d(materials.add(Color::from(BLUE))),
103        // You can insert this component without the `Wireframe` component
104        // to override the color of the global wireframe for this mesh
105        WireframeColor {
106            color: BLACK.into(),
107        },
108    ));
109
110    // light
111    commands.spawn((PointLight::default(), Transform::from_xyz(2.0, 4.0, 2.0)));
112
113    // camera
114    commands.spawn((
115        Camera3d::default(),
116        Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
117    ));
118
119    // Text used to show controls
120    commands.spawn((
121        Text::default(),
122        Node {
123            position_type: PositionType::Absolute,
124            top: px(12),
125            left: px(12),
126            ..default()
127        },
128    ));
129}
130
131/// This system let's you toggle various wireframe settings
132fn update_colors(
133    keyboard_input: Res<ButtonInput<KeyCode>>,
134    mut config: ResMut<WireframeConfig>,
135    mut wireframe_colors: Query<&mut WireframeColor, With<ColorToggleCube>>,
136    mut wireframe_widths: Query<&mut WireframeLineWidth>,
137    mut text: Single<&mut Text>,
138) {
139    let current_width = wireframe_widths
140        .iter()
141        .next()
142        .map(|w| w.width)
143        .unwrap_or(1.0);
144
145    text.0 = format!(
146        "Controls
147---------------
148Z - Toggle global
149X - Change global color
150C - Change color of the green cube wireframe
151V - Line width (current: {current_width:.1}px)
152B - Toggle topology (current: {:?})
153
154WireframeConfig
155-------------
156Global: {}
157Color: {:?}",
158        config.default_topology, config.global, config.default_color,
159    );
160
161    // Toggle showing a wireframe on all meshes
162    if keyboard_input.just_pressed(KeyCode::KeyZ) {
163        config.global = !config.global;
164    }
165
166    // Toggle the global wireframe color
167    if keyboard_input.just_pressed(KeyCode::KeyX) {
168        config.default_color = if config.default_color == WHITE.into() {
169            DEEP_PINK.into()
170        } else {
171            WHITE.into()
172        };
173    }
174
175    // Toggle the color of a wireframe using WireframeColor and not the global color
176    if keyboard_input.just_pressed(KeyCode::KeyC) {
177        for mut color in &mut wireframe_colors {
178            color.color = if color.color == LIME.into() {
179                RED.into()
180            } else {
181                LIME.into()
182            };
183        }
184    }
185
186    if keyboard_input.just_pressed(KeyCode::KeyV) {
187        for mut width in &mut wireframe_widths {
188            width.width = match width.width as u32 {
189                0..=2 => 3.0,
190                3..=4 => 5.0,
191                5..=7 => 10.0,
192                _ => 2.0,
193            };
194        }
195    }
196
197    if keyboard_input.just_pressed(KeyCode::KeyB) {
198        config.default_topology = match config.default_topology {
199            WireframeTopology::Triangles => WireframeTopology::Quads,
200            WireframeTopology::Quads => WireframeTopology::Triangles,
201        };
202    }
203}