1use bevy::{
4 camera_controller::free_camera::{FreeCamera, FreeCameraPlugin},
5 color::palettes::css::*,
6 prelude::*,
7};
8use std::f32::consts::PI;
9
10fn main() {
11 App::new()
12 .add_plugins((DefaultPlugins, FreeCameraPlugin))
13 .init_gizmo_group::<MyRoundGizmos>()
14 .add_systems(Startup, setup)
15 .add_systems(
16 Update,
17 (
18 draw_example_collection,
19 update_config,
20 drive_gizmos_animation,
21 ),
22 )
23 .run();
24}
25
26#[derive(Default, Reflect, GizmoConfigGroup)]
28struct MyRoundGizmos;
29
30fn setup(
31 mut commands: Commands,
32 mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
33 mut meshes: ResMut<Assets<Mesh>>,
34 mut materials: ResMut<Assets<StandardMaterial>>,
35) {
36 let mut gizmo = GizmoAsset::new();
37
38 gizmo
44 .sphere(Isometry3d::IDENTITY, 0.5, CRIMSON)
45 .resolution(30_000 / 3);
46
47 commands.spawn((
48 Gizmo {
49 handle: gizmo_assets.add(gizmo),
50 line_config: GizmoLineConfig {
51 width: 5.,
52 ..default()
53 },
54 ..default()
55 },
56 Transform::from_xyz(4., 1., 0.),
57 ));
58
59 commands.spawn((
60 Camera3d::default(),
61 Transform::from_xyz(0., 1.5, 6.).looking_at(Vec3::ZERO, Vec3::Y),
62 FreeCamera::default(),
63 ));
64 commands.spawn((
66 Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))),
67 MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
68 ));
69 commands.spawn((
71 Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
72 MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))),
73 Transform::from_xyz(0.0, 0.5, 0.0),
74 ));
75 commands.spawn((
77 PointLight {
78 shadow_maps_enabled: true,
79 ..default()
80 },
81 Transform::from_xyz(4.0, 8.0, 4.0),
82 ));
83
84 commands.spawn((
86 Text::new(
87 "Press 'T' to toggle drawing gizmos on top of everything else in the scene\n\
88 Press 'P' to toggle perspective for line gizmos\n\
89 Hold 'Left' or 'Right' to change the line width of straight gizmos\n\
90 Hold 'Up' or 'Down' to change the line width of round gizmos\n\
91 Press '1' or '2' to toggle the visibility of straight gizmos or round gizmos\n\
92 Press 'B' to show all AABB boxes\n\
93 Press 'U' or 'I' to cycle through line styles for straight or round gizmos\n\
94 Press 'J' or 'K' to cycle through line joins for straight or round gizmos\n\
95 Press 'L' to cycle through gizmos animations (only for dotted/dashed round gizmos)\n\
96 Press 'Spacebar' to toggle pause",
97 ),
98 Node {
99 position_type: PositionType::Absolute,
100 top: px(12),
101 left: px(12),
102 ..default()
103 },
104 ));
105}
106
107fn draw_example_collection(
108 mut gizmos: Gizmos,
109 mut my_gizmos: Gizmos<MyRoundGizmos>,
110 time: Res<Time>,
111) {
112 gizmos.grid(
113 Quat::from_rotation_x(PI / 2.),
114 UVec2::splat(20),
115 Vec2::new(2., 2.),
116 LinearRgba::gray(0.65),
118 );
119 gizmos.grid(
120 Isometry3d::new(Vec3::splat(10.0), Quat::from_rotation_x(PI / 3. * 2.)),
121 UVec2::splat(20),
122 Vec2::new(2., 2.),
123 PURPLE,
124 );
125 gizmos.sphere(Vec3::splat(10.0), 1.0, PURPLE);
126
127 gizmos
128 .primitive_3d(
129 &Plane3d {
130 normal: Dir3::Y,
131 half_size: Vec2::splat(1.0),
132 },
133 Isometry3d::new(
134 Vec3::splat(4.0) + Vec2::from(ops::sin_cos(time.elapsed_secs())).extend(0.0),
135 Quat::from_rotation_x(PI / 2. + time.elapsed_secs()),
136 ),
137 GREEN,
138 )
139 .cell_count(UVec2::new(5, 10))
140 .spacing(Vec2::new(0.2, 0.1));
141
142 gizmos.cube(
143 Transform::from_translation(Vec3::Y * 0.5).with_scale(Vec3::splat(1.25)),
144 BLACK,
145 );
146 gizmos.rect(
147 Isometry3d::new(
148 Vec3::new(ops::cos(time.elapsed_secs()) * 2.5, 1., 0.),
149 Quat::from_rotation_y(PI / 2.),
150 ),
151 Vec2::splat(2.),
152 LIME,
153 );
154
155 gizmos.cross(Vec3::new(-1., 1., 1.), 0.5, FUCHSIA);
156
157 let domain = Interval::EVERYWHERE;
158 let curve = FunctionCurve::new(domain, |t| {
159 (Vec2::from(ops::sin_cos(t * 10.0))).extend(t - 6.0)
160 });
161 let resolution = ((ops::sin(time.elapsed_secs()) + 1.0) * 100.0) as usize;
162 let times_and_colors = (0..=resolution)
163 .map(|n| n as f32 / resolution as f32)
164 .map(|t| t * 5.0)
165 .map(|t| (t, TEAL.mix(&HOT_PINK, t / 5.0)));
166 gizmos.curve_gradient_3d(curve, times_and_colors);
167
168 gizmos.primitive_3d(
169 &Capsule3d::new(0.35, 0.5),
170 Isometry3d::new(
171 Vec3::new(-1.75, 0.75, 0.75),
172 Quat::from_rotation_y(ops::cos(time.elapsed_secs() / 2.0) * 10.0)
173 * Quat::from_rotation_z(PI / 3.0),
174 ),
175 YELLOW_GREEN,
176 );
177
178 my_gizmos.sphere(Vec3::new(1., 0.5, 0.), 0.5, RED);
179
180 my_gizmos
181 .rounded_cuboid(Vec3::new(-2.0, 0.75, -0.75), Vec3::splat(0.9), TURQUOISE)
182 .edge_radius(0.1)
183 .arc_resolution(4);
184
185 for y in [0., 0.5, 1.] {
186 gizmos.ray(
187 Vec3::new(1., y, 0.),
188 Vec3::new(-3., ops::sin(time.elapsed_secs() * 3.), 0.),
189 BLUE,
190 );
191 }
192
193 my_gizmos
194 .arc_3d(
195 180.0_f32.to_radians(),
196 0.2,
197 Isometry3d::new(
198 Vec3::ONE,
199 Quat::from_rotation_arc(Vec3::Y, Vec3::ONE.normalize()),
200 ),
201 ORANGE,
202 )
203 .resolution(10);
204
205 my_gizmos.circle(Quat::from_rotation_arc(Vec3::Z, Vec3::Y), 3., BLACK);
207
208 my_gizmos
210 .circle(Quat::from_rotation_arc(Vec3::Z, Vec3::Y), 3.1, NAVY)
211 .resolution(64);
212 my_gizmos
213 .sphere(Isometry3d::IDENTITY, 3.2, BLACK)
214 .resolution(64);
215
216 gizmos.arrow(Vec3::ZERO, Vec3::splat(1.5), YELLOW);
217
218 gizmos
220 .arrow(Vec3::new(2., 0., 2.), Vec3::new(2., 2., 2.), ORANGE_RED)
221 .with_double_end()
222 .with_tip_length(0.5);
223
224 let from = Vec3::new(1.0, 2.0, 3.0);
225 let to = Vec3::new(3.0, 2.5, 4.0);
226 gizmos.rect(from, Vec2::ONE, RED);
227 my_gizmos.short_arc_3d_between((from + to) / 2.0, from, to, YELLOW_GREEN);
228 gizmos.rect(to, Vec2::ONE, RED);
229}
230
231fn update_config(
232 mut config_store: ResMut<GizmoConfigStore>,
233 keyboard: Res<ButtonInput<KeyCode>>,
234 real_time: Res<Time<Real>>,
235 mut virtual_time: ResMut<Time<Virtual>>,
236) {
237 if keyboard.just_pressed(KeyCode::KeyT) {
238 for (_, config, _) in config_store.iter_mut() {
239 config.depth_bias = if config.depth_bias == 0. { -1. } else { 0. };
240 }
241 }
242 if keyboard.just_pressed(KeyCode::KeyP) {
243 for (_, config, _) in config_store.iter_mut() {
244 config.line.perspective ^= true;
246 config.line.width *= if config.line.perspective { 5. } else { 1. / 5. };
248 }
249 }
250
251 let (config, _) = config_store.config_mut::<DefaultGizmoConfigGroup>();
252 if keyboard.pressed(KeyCode::ArrowRight) {
253 config.line.width += 5. * real_time.delta_secs();
254 config.line.width = config.line.width.clamp(0., 50.);
255 }
256 if keyboard.pressed(KeyCode::ArrowLeft) {
257 config.line.width -= 5. * real_time.delta_secs();
258 config.line.width = config.line.width.clamp(0., 50.);
259 }
260 if keyboard.just_pressed(KeyCode::Digit1) {
261 config.enabled ^= true;
262 }
263 if keyboard.just_pressed(KeyCode::KeyU) {
264 config.line.style = match config.line.style {
265 GizmoLineStyle::Solid => GizmoLineStyle::Dotted,
266 GizmoLineStyle::Dotted => GizmoLineStyle::Dashed {
267 gap_scale: 3.0,
268 line_scale: 5.0,
269 },
270 _ => GizmoLineStyle::Solid,
271 };
272 }
273 if keyboard.just_pressed(KeyCode::KeyJ) {
274 config.line.joints = match config.line.joints {
275 GizmoLineJoint::Bevel => GizmoLineJoint::Miter,
276 GizmoLineJoint::Miter => GizmoLineJoint::Round(4),
277 GizmoLineJoint::Round(_) => GizmoLineJoint::None,
278 GizmoLineJoint::None => GizmoLineJoint::Bevel,
279 };
280 }
281
282 let (my_config, _) = config_store.config_mut::<MyRoundGizmos>();
283 if keyboard.pressed(KeyCode::ArrowUp) {
284 my_config.line.width += 5. * real_time.delta_secs();
285 my_config.line.width = my_config.line.width.clamp(0., 50.);
286 }
287 if keyboard.pressed(KeyCode::ArrowDown) {
288 my_config.line.width -= 5. * real_time.delta_secs();
289 my_config.line.width = my_config.line.width.clamp(0., 50.);
290 }
291 if keyboard.just_pressed(KeyCode::Digit2) {
292 my_config.enabled ^= true;
293 }
294 if keyboard.just_pressed(KeyCode::KeyI) {
295 my_config.line.style = match my_config.line.style {
296 GizmoLineStyle::Solid => GizmoLineStyle::Dotted,
297 GizmoLineStyle::Dotted => GizmoLineStyle::Dashed {
298 gap_scale: 3.0,
299 line_scale: 5.0,
300 },
301 _ => GizmoLineStyle::Solid,
302 };
303 }
304 if keyboard.just_pressed(KeyCode::KeyK) {
305 my_config.line.joints = match my_config.line.joints {
306 GizmoLineJoint::Bevel => GizmoLineJoint::Miter,
307 GizmoLineJoint::Miter => GizmoLineJoint::Round(4),
308 GizmoLineJoint::Round(_) => GizmoLineJoint::None,
309 GizmoLineJoint::None => GizmoLineJoint::Bevel,
310 };
311 }
312
313 if keyboard.just_pressed(KeyCode::KeyB) {
314 config_store.config_mut::<AabbGizmoConfigGroup>().1.draw_all ^= true;
317 }
318 if keyboard.just_pressed(KeyCode::Space) {
319 virtual_time.toggle();
320 }
321}
322
323enum GizmosAnimationType {
324 Linear,
325 BackAndForth,
326 Stutter,
327}
328
329fn drive_gizmos_animation(
330 mut config_store: ResMut<GizmoConfigStore>,
331 virtual_time: ResMut<Time<Virtual>>,
332 keyboard: Res<ButtonInput<KeyCode>>,
333 mut animation_type: Local<Option<GizmosAnimationType>>,
334) {
335 if keyboard.just_pressed(KeyCode::KeyL) {
336 *animation_type = match *animation_type {
337 None => Some(GizmosAnimationType::Linear),
338 Some(GizmosAnimationType::Linear) => Some(GizmosAnimationType::BackAndForth),
339 Some(GizmosAnimationType::BackAndForth) => Some(GizmosAnimationType::Stutter),
340 Some(GizmosAnimationType::Stutter) => None,
341 };
342 }
343
344 if let Some(animation_type) = animation_type.as_ref() {
345 let (my_config, _) = config_store.config_mut::<MyRoundGizmos>();
346 match animation_type {
347 GizmosAnimationType::Linear => {
348 my_config.line.animation_offset = virtual_time.elapsed_secs() * 10.0;
349 }
350 GizmosAnimationType::BackAndForth => {
351 my_config.line.animation_offset = ops::sin(virtual_time.elapsed_secs()) * 10.0;
352 }
353 GizmosAnimationType::Stutter => {
354 my_config.line.animation_offset = (virtual_time.elapsed_secs() * 4.0).round();
355 }
356 }
357 }
358}