render_primitives/
render_primitives.rs

1//! This example demonstrates how each of Bevy's math primitives look like in 2D and 3D with meshes
2//! and with gizmos
3
4use bevy::{input::common_conditions::input_just_pressed, math::Isometry2d, prelude::*};
5
6const LEFT_RIGHT_OFFSET_2D: f32 = 200.0;
7const LEFT_RIGHT_OFFSET_3D: f32 = 2.0;
8
9fn main() {
10    let mut app = App::new();
11
12    app.add_plugins(DefaultPlugins)
13        .init_state::<PrimitiveSelected>()
14        .init_state::<CameraActive>();
15
16    // cameras
17    app.add_systems(Startup, (setup_cameras, setup_lights, setup_ambient_light))
18        .add_systems(
19            Update,
20            (
21                update_active_cameras.run_if(state_changed::<CameraActive>),
22                switch_cameras.run_if(input_just_pressed(KeyCode::KeyC)),
23            ),
24        );
25
26    // text
27
28    // PostStartup since we need the cameras to exist
29    app.add_systems(PostStartup, setup_text);
30    app.add_systems(
31        Update,
32        (update_text.run_if(state_changed::<PrimitiveSelected>),),
33    );
34
35    // primitives
36    app.add_systems(Startup, (spawn_primitive_2d, spawn_primitive_3d))
37        .add_systems(
38            Update,
39            (
40                switch_to_next_primitive.run_if(input_just_pressed(KeyCode::ArrowUp)),
41                switch_to_previous_primitive.run_if(input_just_pressed(KeyCode::ArrowDown)),
42                draw_gizmos_2d.run_if(in_mode(CameraActive::Dim2)),
43                draw_gizmos_3d.run_if(in_mode(CameraActive::Dim3)),
44                update_primitive_meshes
45                    .run_if(state_changed::<PrimitiveSelected>.or(state_changed::<CameraActive>)),
46                rotate_primitive_2d_meshes,
47                rotate_primitive_3d_meshes,
48            ),
49        );
50
51    app.run();
52}
53
54/// State for tracking which of the two cameras (2D & 3D) is currently active
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default, Reflect)]
56enum CameraActive {
57    #[default]
58    /// 2D Camera is active
59    Dim2,
60    /// 3D Camera is active
61    Dim3,
62}
63
64/// State for tracking which primitives are currently displayed
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default, Reflect)]
66enum PrimitiveSelected {
67    #[default]
68    RectangleAndCuboid,
69    CircleAndSphere,
70    Ellipse,
71    Triangle,
72    Plane,
73    Line,
74    Segment,
75    Polyline,
76    Polygon,
77    RegularPolygon,
78    Capsule,
79    Cylinder,
80    Cone,
81    ConicalFrustum,
82    Torus,
83    Tetrahedron,
84    Arc,
85    CircularSector,
86    CircularSegment,
87}
88
89impl std::fmt::Display for PrimitiveSelected {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        let name = match self {
92            PrimitiveSelected::RectangleAndCuboid => String::from("Rectangle/Cuboid"),
93            PrimitiveSelected::CircleAndSphere => String::from("Circle/Sphere"),
94            other => format!("{other:?}"),
95        };
96        write!(f, "{name}")
97    }
98}
99
100impl PrimitiveSelected {
101    const ALL: [Self; 19] = [
102        Self::RectangleAndCuboid,
103        Self::CircleAndSphere,
104        Self::Ellipse,
105        Self::Triangle,
106        Self::Plane,
107        Self::Line,
108        Self::Segment,
109        Self::Polyline,
110        Self::Polygon,
111        Self::RegularPolygon,
112        Self::Capsule,
113        Self::Cylinder,
114        Self::Cone,
115        Self::ConicalFrustum,
116        Self::Torus,
117        Self::Tetrahedron,
118        Self::Arc,
119        Self::CircularSector,
120        Self::CircularSegment,
121    ];
122
123    fn next(self) -> Self {
124        Self::ALL
125            .into_iter()
126            .cycle()
127            .skip_while(|&x| x != self)
128            .nth(1)
129            .unwrap()
130    }
131
132    fn previous(self) -> Self {
133        Self::ALL
134            .into_iter()
135            .rev()
136            .cycle()
137            .skip_while(|&x| x != self)
138            .nth(1)
139            .unwrap()
140    }
141}
142
143const SMALL_2D: f32 = 50.0;
144const BIG_2D: f32 = 100.0;
145
146const SMALL_3D: f32 = 0.5;
147const BIG_3D: f32 = 1.0;
148
149// primitives
150const RECTANGLE: Rectangle = Rectangle {
151    half_size: Vec2::new(SMALL_2D, BIG_2D),
152};
153const CUBOID: Cuboid = Cuboid {
154    half_size: Vec3::new(BIG_3D, SMALL_3D, BIG_3D),
155};
156
157const CIRCLE: Circle = Circle { radius: BIG_2D };
158const SPHERE: Sphere = Sphere { radius: BIG_3D };
159
160const ELLIPSE: Ellipse = Ellipse {
161    half_size: Vec2::new(BIG_2D, SMALL_2D),
162};
163
164const TRIANGLE_2D: Triangle2d = Triangle2d {
165    vertices: [
166        Vec2::new(BIG_2D, 0.0),
167        Vec2::new(0.0, BIG_2D),
168        Vec2::new(-BIG_2D, 0.0),
169    ],
170};
171
172const TRIANGLE_3D: Triangle3d = Triangle3d {
173    vertices: [
174        Vec3::new(BIG_3D, 0.0, 0.0),
175        Vec3::new(0.0, BIG_3D, 0.0),
176        Vec3::new(-BIG_3D, 0.0, 0.0),
177    ],
178};
179
180const PLANE_2D: Plane2d = Plane2d { normal: Dir2::Y };
181const PLANE_3D: Plane3d = Plane3d {
182    normal: Dir3::Y,
183    half_size: Vec2::new(BIG_3D, BIG_3D),
184};
185
186const LINE2D: Line2d = Line2d { direction: Dir2::X };
187const LINE3D: Line3d = Line3d { direction: Dir3::X };
188
189const SEGMENT_2D: Segment2d = Segment2d {
190    vertices: [Vec2::new(-BIG_2D / 2., 0.), Vec2::new(BIG_2D / 2., 0.)],
191};
192
193const SEGMENT_3D: Segment3d = Segment3d {
194    vertices: [
195        Vec3::new(-BIG_3D / 2., 0., 0.),
196        Vec3::new(BIG_3D / 2., 0., 0.),
197    ],
198};
199
200const REGULAR_POLYGON: RegularPolygon = RegularPolygon {
201    circumcircle: Circle { radius: BIG_2D },
202    sides: 5,
203};
204
205const CAPSULE_2D: Capsule2d = Capsule2d {
206    radius: SMALL_2D,
207    half_length: SMALL_2D,
208};
209const CAPSULE_3D: Capsule3d = Capsule3d {
210    radius: SMALL_3D,
211    half_length: SMALL_3D,
212};
213
214const CYLINDER: Cylinder = Cylinder {
215    radius: SMALL_3D,
216    half_height: SMALL_3D,
217};
218
219const CONE: Cone = Cone {
220    radius: BIG_3D,
221    height: BIG_3D,
222};
223
224const CONICAL_FRUSTUM: ConicalFrustum = ConicalFrustum {
225    radius_top: BIG_3D,
226    radius_bottom: SMALL_3D,
227    height: BIG_3D,
228};
229
230const ANNULUS: Annulus = Annulus {
231    inner_circle: Circle { radius: SMALL_2D },
232    outer_circle: Circle { radius: BIG_2D },
233};
234
235const TORUS: Torus = Torus {
236    minor_radius: SMALL_3D / 2.0,
237    major_radius: SMALL_3D * 1.5,
238};
239
240const TETRAHEDRON: Tetrahedron = Tetrahedron {
241    vertices: [
242        Vec3::new(-BIG_3D, 0.0, 0.0),
243        Vec3::new(BIG_3D, 0.0, 0.0),
244        Vec3::new(0.0, 0.0, -BIG_3D * 1.67),
245        Vec3::new(0.0, BIG_3D * 1.67, -BIG_3D * 0.5),
246    ],
247};
248
249const ARC: Arc2d = Arc2d {
250    radius: BIG_2D,
251    half_angle: std::f32::consts::FRAC_PI_4,
252};
253
254const CIRCULAR_SECTOR: CircularSector = CircularSector {
255    arc: Arc2d {
256        radius: BIG_2D,
257        half_angle: std::f32::consts::FRAC_PI_4,
258    },
259};
260
261const CIRCULAR_SEGMENT: CircularSegment = CircularSegment {
262    arc: Arc2d {
263        radius: BIG_2D,
264        half_angle: std::f32::consts::FRAC_PI_4,
265    },
266};
267
268fn setup_cameras(mut commands: Commands) {
269    let start_in_2d = true;
270    let make_camera = |is_active| Camera {
271        is_active,
272        ..Default::default()
273    };
274
275    commands.spawn((Camera2d, make_camera(start_in_2d)));
276
277    commands.spawn((
278        Camera3d::default(),
279        make_camera(!start_in_2d),
280        Transform::from_xyz(0.0, 10.0, 0.0).looking_at(Vec3::ZERO, Vec3::Z),
281    ));
282}
283
284fn setup_ambient_light(mut ambient_light: ResMut<AmbientLight>) {
285    ambient_light.brightness = 50.0;
286}
287
288fn setup_lights(mut commands: Commands) {
289    commands.spawn((
290        PointLight {
291            intensity: 5000.0,
292            ..default()
293        },
294        Transform::from_translation(Vec3::new(-LEFT_RIGHT_OFFSET_3D, 2.0, 0.0))
295            .looking_at(Vec3::new(-LEFT_RIGHT_OFFSET_3D, 0.0, 0.0), Vec3::Y),
296    ));
297}
298
299/// Marker component for header text
300#[derive(Debug, Clone, Component, Default, Reflect)]
301pub struct HeaderText;
302
303/// Marker component for header node
304#[derive(Debug, Clone, Component, Default, Reflect)]
305pub struct HeaderNode;
306
307fn update_active_cameras(
308    state: Res<State<CameraActive>>,
309    camera_2d: Single<(Entity, &mut Camera), With<Camera2d>>,
310    camera_3d: Single<(Entity, &mut Camera), (With<Camera3d>, Without<Camera2d>)>,
311    mut text: Query<&mut UiTargetCamera, With<HeaderNode>>,
312) {
313    let (entity_2d, mut cam_2d) = camera_2d.into_inner();
314    let (entity_3d, mut cam_3d) = camera_3d.into_inner();
315    let is_camera_2d_active = matches!(*state.get(), CameraActive::Dim2);
316
317    cam_2d.is_active = is_camera_2d_active;
318    cam_3d.is_active = !is_camera_2d_active;
319
320    let active_camera = if is_camera_2d_active {
321        entity_2d
322    } else {
323        entity_3d
324    };
325
326    text.iter_mut().for_each(|mut target_camera| {
327        *target_camera = UiTargetCamera(active_camera);
328    });
329}
330
331fn switch_cameras(current: Res<State<CameraActive>>, mut next: ResMut<NextState<CameraActive>>) {
332    let next_state = match current.get() {
333        CameraActive::Dim2 => CameraActive::Dim3,
334        CameraActive::Dim3 => CameraActive::Dim2,
335    };
336    next.set(next_state);
337}
338
339fn setup_text(mut commands: Commands, cameras: Query<(Entity, &Camera)>) {
340    let active_camera = cameras
341        .iter()
342        .find_map(|(entity, camera)| camera.is_active.then_some(entity))
343        .expect("run condition ensures existence");
344    commands.spawn((
345        HeaderNode,
346        Node {
347            justify_self: JustifySelf::Center,
348            top: px(5),
349            ..Default::default()
350        },
351        UiTargetCamera(active_camera),
352        children![(
353            Text::default(),
354            HeaderText,
355            TextLayout::new_with_justify(Justify::Center),
356            children![
357                TextSpan::new("Primitive: "),
358                TextSpan(format!("{text}", text = PrimitiveSelected::default())),
359                TextSpan::new("\n\n"),
360                TextSpan::new(
361                    "Press 'C' to switch between 2D and 3D mode\n\
362                    Press 'Up' or 'Down' to switch to the next/previous primitive",
363                ),
364                TextSpan::new("\n\n"),
365                TextSpan::new("(If nothing is displayed, there's no rendering support yet)",),
366            ]
367        )],
368    ));
369}
370
371fn update_text(
372    primitive_state: Res<State<PrimitiveSelected>>,
373    header: Query<Entity, With<HeaderText>>,
374    mut writer: TextUiWriter,
375) {
376    let new_text = format!("{text}", text = primitive_state.get());
377    header.iter().for_each(|header_text| {
378        if let Some(mut text) = writer.get_text(header_text, 2) {
379            (*text).clone_from(&new_text);
380        };
381    });
382}
383
384fn switch_to_next_primitive(
385    current: Res<State<PrimitiveSelected>>,
386    mut next: ResMut<NextState<PrimitiveSelected>>,
387) {
388    let next_state = current.get().next();
389    next.set(next_state);
390}
391
392fn switch_to_previous_primitive(
393    current: Res<State<PrimitiveSelected>>,
394    mut next: ResMut<NextState<PrimitiveSelected>>,
395) {
396    let next_state = current.get().previous();
397    next.set(next_state);
398}
399
400fn in_mode(active: CameraActive) -> impl Fn(Res<State<CameraActive>>) -> bool {
401    move |state| *state.get() == active
402}
403
404fn draw_gizmos_2d(mut gizmos: Gizmos, state: Res<State<PrimitiveSelected>>, time: Res<Time>) {
405    const POSITION: Vec2 = Vec2::new(-LEFT_RIGHT_OFFSET_2D, 0.0);
406    let angle = time.elapsed_secs();
407    let isometry = Isometry2d::new(POSITION, Rot2::radians(angle));
408    let color = Color::WHITE;
409
410    #[expect(
411        clippy::match_same_arms,
412        reason = "Certain primitives don't have any 2D rendering support yet."
413    )]
414    match state.get() {
415        PrimitiveSelected::RectangleAndCuboid => {
416            gizmos.primitive_2d(&RECTANGLE, isometry, color);
417        }
418        PrimitiveSelected::CircleAndSphere => {
419            gizmos.primitive_2d(&CIRCLE, isometry, color);
420        }
421        PrimitiveSelected::Ellipse => drop(gizmos.primitive_2d(&ELLIPSE, isometry, color)),
422        PrimitiveSelected::Triangle => gizmos.primitive_2d(&TRIANGLE_2D, isometry, color),
423        PrimitiveSelected::Plane => gizmos.primitive_2d(&PLANE_2D, isometry, color),
424        PrimitiveSelected::Line => drop(gizmos.primitive_2d(&LINE2D, isometry, color)),
425        PrimitiveSelected::Segment => {
426            drop(gizmos.primitive_2d(&SEGMENT_2D, isometry, color));
427        }
428        PrimitiveSelected::Polyline => gizmos.primitive_2d(
429            &Polyline2d {
430                vertices: vec![
431                    Vec2::new(-BIG_2D, -SMALL_2D),
432                    Vec2::new(-SMALL_2D, SMALL_2D),
433                    Vec2::new(SMALL_2D, -SMALL_2D),
434                    Vec2::new(BIG_2D, SMALL_2D),
435                ],
436            },
437            isometry,
438            color,
439        ),
440        PrimitiveSelected::Polygon => gizmos.primitive_2d(
441            &Polygon {
442                vertices: vec![
443                    Vec2::new(-BIG_2D, -SMALL_2D),
444                    Vec2::new(BIG_2D, -SMALL_2D),
445                    Vec2::new(BIG_2D, SMALL_2D),
446                    Vec2::new(0.0, 0.0),
447                    Vec2::new(-BIG_2D, SMALL_2D),
448                ],
449            },
450            isometry,
451            color,
452        ),
453        PrimitiveSelected::RegularPolygon => {
454            gizmos.primitive_2d(&REGULAR_POLYGON, isometry, color);
455        }
456        PrimitiveSelected::Capsule => gizmos.primitive_2d(&CAPSULE_2D, isometry, color),
457        PrimitiveSelected::Cylinder => {}
458        PrimitiveSelected::Cone => {}
459        PrimitiveSelected::ConicalFrustum => {}
460        PrimitiveSelected::Torus => drop(gizmos.primitive_2d(&ANNULUS, isometry, color)),
461        PrimitiveSelected::Tetrahedron => {}
462        PrimitiveSelected::Arc => gizmos.primitive_2d(&ARC, isometry, color),
463        PrimitiveSelected::CircularSector => {
464            gizmos.primitive_2d(&CIRCULAR_SECTOR, isometry, color);
465        }
466        PrimitiveSelected::CircularSegment => {
467            gizmos.primitive_2d(&CIRCULAR_SEGMENT, isometry, color);
468        }
469    }
470}
471
472/// Marker for primitive meshes to record in which state they should be visible in
473#[derive(Debug, Clone, Component, Default, Reflect)]
474pub struct PrimitiveData {
475    camera_mode: CameraActive,
476    primitive_state: PrimitiveSelected,
477}
478
479/// Marker for meshes of 2D primitives
480#[derive(Debug, Clone, Component, Default)]
481pub struct MeshDim2;
482
483/// Marker for meshes of 3D primitives
484#[derive(Debug, Clone, Component, Default)]
485pub struct MeshDim3;
486
487fn spawn_primitive_2d(
488    mut commands: Commands,
489    mut materials: ResMut<Assets<ColorMaterial>>,
490    mut meshes: ResMut<Assets<Mesh>>,
491) {
492    const POSITION: Vec3 = Vec3::new(LEFT_RIGHT_OFFSET_2D, 0.0, 0.0);
493    let material: Handle<ColorMaterial> = materials.add(Color::WHITE);
494    let camera_mode = CameraActive::Dim2;
495    [
496        Some(RECTANGLE.mesh().build()),
497        Some(CIRCLE.mesh().build()),
498        Some(ELLIPSE.mesh().build()),
499        Some(TRIANGLE_2D.mesh().build()),
500        None, // plane
501        None, // line
502        None, // segment
503        None, // polyline
504        None, // polygon
505        Some(REGULAR_POLYGON.mesh().build()),
506        Some(CAPSULE_2D.mesh().build()),
507        None, // cylinder
508        None, // cone
509        None, // conical frustum
510        Some(ANNULUS.mesh().build()),
511        None, // tetrahedron
512    ]
513    .into_iter()
514    .zip(PrimitiveSelected::ALL)
515    .for_each(|(maybe_mesh, state)| {
516        if let Some(mesh) = maybe_mesh {
517            commands.spawn((
518                MeshDim2,
519                PrimitiveData {
520                    camera_mode,
521                    primitive_state: state,
522                },
523                Mesh2d(meshes.add(mesh)),
524                MeshMaterial2d(material.clone()),
525                Transform::from_translation(POSITION),
526            ));
527        }
528    });
529}
530
531fn spawn_primitive_3d(
532    mut commands: Commands,
533    mut materials: ResMut<Assets<StandardMaterial>>,
534    mut meshes: ResMut<Assets<Mesh>>,
535) {
536    const POSITION: Vec3 = Vec3::new(-LEFT_RIGHT_OFFSET_3D, 0.0, 0.0);
537    let material: Handle<StandardMaterial> = materials.add(Color::WHITE);
538    let camera_mode = CameraActive::Dim3;
539    [
540        Some(CUBOID.mesh().build()),
541        Some(SPHERE.mesh().build()),
542        None, // ellipse
543        Some(TRIANGLE_3D.mesh().build()),
544        Some(PLANE_3D.mesh().build()),
545        None, // line
546        None, // segment
547        None, // polyline
548        None, // polygon
549        None, // regular polygon
550        Some(CAPSULE_3D.mesh().build()),
551        Some(CYLINDER.mesh().build()),
552        None, // cone
553        None, // conical frustum
554        Some(TORUS.mesh().build()),
555        Some(TETRAHEDRON.mesh().build()),
556    ]
557    .into_iter()
558    .zip(PrimitiveSelected::ALL)
559    .for_each(|(maybe_mesh, state)| {
560        if let Some(mesh) = maybe_mesh {
561            commands.spawn((
562                MeshDim3,
563                PrimitiveData {
564                    camera_mode,
565                    primitive_state: state,
566                },
567                Mesh3d(meshes.add(mesh)),
568                MeshMaterial3d(material.clone()),
569                Transform::from_translation(POSITION),
570            ));
571        }
572    });
573}
574
575fn update_primitive_meshes(
576    camera_state: Res<State<CameraActive>>,
577    primitive_state: Res<State<PrimitiveSelected>>,
578    mut primitives: Query<(&mut Visibility, &PrimitiveData)>,
579) {
580    primitives.iter_mut().for_each(|(mut vis, primitive)| {
581        let visible = primitive.camera_mode == *camera_state.get()
582            && primitive.primitive_state == *primitive_state.get();
583        *vis = if visible {
584            Visibility::Inherited
585        } else {
586            Visibility::Hidden
587        };
588    });
589}
590
591fn rotate_primitive_2d_meshes(
592    mut primitives_2d: Query<
593        (&mut Transform, &ViewVisibility),
594        (With<PrimitiveData>, With<MeshDim2>),
595    >,
596    time: Res<Time>,
597) {
598    let rotation_2d = Quat::from_mat3(&Mat3::from_angle(time.elapsed_secs()));
599    primitives_2d
600        .iter_mut()
601        .filter(|(_, vis)| vis.get())
602        .for_each(|(mut transform, _)| {
603            transform.rotation = rotation_2d;
604        });
605}
606
607fn rotate_primitive_3d_meshes(
608    mut primitives_3d: Query<
609        (&mut Transform, &ViewVisibility),
610        (With<PrimitiveData>, With<MeshDim3>),
611    >,
612    time: Res<Time>,
613) {
614    let rotation_3d = Quat::from_rotation_arc(
615        Vec3::Z,
616        Vec3::new(
617            ops::sin(time.elapsed_secs()),
618            ops::cos(time.elapsed_secs()),
619            ops::sin(time.elapsed_secs()) * 0.5,
620        )
621        .try_normalize()
622        .unwrap_or(Vec3::Z),
623    );
624    primitives_3d
625        .iter_mut()
626        .filter(|(_, vis)| vis.get())
627        .for_each(|(mut transform, _)| {
628            transform.rotation = rotation_3d;
629        });
630}
631
632fn draw_gizmos_3d(mut gizmos: Gizmos, state: Res<State<PrimitiveSelected>>, time: Res<Time>) {
633    const POSITION: Vec3 = Vec3::new(LEFT_RIGHT_OFFSET_3D, 0.0, 0.0);
634    let rotation = Quat::from_rotation_arc(
635        Vec3::Z,
636        Vec3::new(
637            ops::sin(time.elapsed_secs()),
638            ops::cos(time.elapsed_secs()),
639            ops::sin(time.elapsed_secs()) * 0.5,
640        )
641        .try_normalize()
642        .unwrap_or(Vec3::Z),
643    );
644    let isometry = Isometry3d::new(POSITION, rotation);
645    let color = Color::WHITE;
646    let resolution = 10;
647
648    #[expect(
649        clippy::match_same_arms,
650        reason = "Certain primitives don't have any 3D rendering support yet."
651    )]
652    match state.get() {
653        PrimitiveSelected::RectangleAndCuboid => {
654            gizmos.primitive_3d(&CUBOID, isometry, color);
655        }
656        PrimitiveSelected::CircleAndSphere => drop(
657            gizmos
658                .primitive_3d(&SPHERE, isometry, color)
659                .resolution(resolution),
660        ),
661        PrimitiveSelected::Ellipse => {}
662        PrimitiveSelected::Triangle => gizmos.primitive_3d(&TRIANGLE_3D, isometry, color),
663        PrimitiveSelected::Plane => drop(gizmos.primitive_3d(&PLANE_3D, isometry, color)),
664        PrimitiveSelected::Line => gizmos.primitive_3d(&LINE3D, isometry, color),
665        PrimitiveSelected::Segment => gizmos.primitive_3d(&SEGMENT_3D, isometry, color),
666        PrimitiveSelected::Polyline => gizmos.primitive_3d(
667            &Polyline3d {
668                vertices: vec![
669                    Vec3::new(-BIG_3D, -SMALL_3D, -SMALL_3D),
670                    Vec3::new(SMALL_3D, SMALL_3D, 0.0),
671                    Vec3::new(-SMALL_3D, -SMALL_3D, 0.0),
672                    Vec3::new(BIG_3D, SMALL_3D, SMALL_3D),
673                ],
674            },
675            isometry,
676            color,
677        ),
678        PrimitiveSelected::Polygon => {}
679        PrimitiveSelected::RegularPolygon => {}
680        PrimitiveSelected::Capsule => drop(
681            gizmos
682                .primitive_3d(&CAPSULE_3D, isometry, color)
683                .resolution(resolution),
684        ),
685        PrimitiveSelected::Cylinder => drop(
686            gizmos
687                .primitive_3d(&CYLINDER, isometry, color)
688                .resolution(resolution),
689        ),
690        PrimitiveSelected::Cone => drop(
691            gizmos
692                .primitive_3d(&CONE, isometry, color)
693                .resolution(resolution),
694        ),
695        PrimitiveSelected::ConicalFrustum => {
696            gizmos.primitive_3d(&CONICAL_FRUSTUM, isometry, color);
697        }
698
699        PrimitiveSelected::Torus => drop(
700            gizmos
701                .primitive_3d(&TORUS, isometry, color)
702                .minor_resolution(resolution)
703                .major_resolution(resolution),
704        ),
705        PrimitiveSelected::Tetrahedron => {
706            gizmos.primitive_3d(&TETRAHEDRON, isometry, color);
707        }
708
709        PrimitiveSelected::Arc => {}
710        PrimitiveSelected::CircularSector => {}
711        PrimitiveSelected::CircularSegment => {}
712    }
713}