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