Skip to main content

fyrox_impl/scene/
debug.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Debug drawing module.
22//!
23//! For more info see [`SceneDrawingContext`]
24
25use crate::core::{
26    algebra::{Matrix4, Point3, UnitQuaternion, Vector2, Vector3},
27    color::{Color, Hsl},
28    math::{aabb::AxisAlignedBoundingBox, frustum::Frustum, Matrix4Ext},
29};
30use rapier2d::math::Vector;
31use rapier2d::pipeline::{DebugColor, DebugRenderObject};
32use std::ops::Range;
33
34/// Colored line between two points.
35#[derive(Clone, Debug)]
36pub struct Line {
37    /// Beginning of the line.
38    pub begin: Vector3<f32>,
39    /// End of the line.
40    pub end: Vector3<f32>,
41    /// Color of the line.
42    pub color: Color,
43}
44
45/// Drawing context for simple graphics, it allows you to draw simple figures using a set of lines. Most
46/// common use of the context is to draw some debug geometry in your game, draw physics info (contacts,
47/// meshes, shapes, etc.), draw temporary geometry in editor and so on.
48///
49/// This drawing context is meant to be used only for debugging purposes, it draws everything as set of lines
50/// and no solid faces supported.
51///
52/// It should be noted that the actual drawing is not immediate, provided methods just populate internal array
53/// of lines and it will be drawn on special render stage.
54///
55/// # Example
56///
57/// The usage of the drawing context is a bit unusual, at the beginning of the frame you should clear the
58/// contents of the context and only then call "drawing" methods. Otherwise, the internal buffer will increase
59/// in size to values which will take lots of time draw and the FPS will significantly drop with every frame
60/// until it reaches zero.
61///
62/// So typical usage would be:
63///
64/// ```
65/// # use fyrox_impl::scene::debug::SceneDrawingContext;
66/// # use fyrox_impl::core::algebra::Matrix4;
67/// # use fyrox_impl::core::color::Color;
68///
69/// fn draw_debug_objects(ctx: &mut SceneDrawingContext) {
70///     // Clear at the beginning of the frame.
71///     ctx.clear_lines();
72///
73///     // Draw something.
74///     ctx.draw_cone(20, 1.0, 2.0, Matrix4::identity(), Color::WHITE, true);
75/// }
76///
77/// ```
78///
79/// You could avoid calling `clear_lines` in specific cases where your debug geometry is not changing, then
80/// the context could be populated once and rendered multiple times without any issues. Another case when
81/// you could not call `clear_lines` each frame, is "tracing" scenario - for example you may need to trace
82/// moving objects. In this case call `clear_lines` once in a few seconds, and you'll see the "track" of
83/// moving objects.
84///
85/// # Rendering performance
86///
87/// The engine renders the entire set of lines in a single draw call, so it very fast - you should be able to draw
88/// up to few millions of lines without any significant performance issues.
89#[derive(Default, Clone, Debug)]
90pub struct SceneDrawingContext {
91    /// List of lines to draw.
92    pub lines: Vec<Line>,
93}
94
95impl rapier2d::pipeline::DebugRenderBackend for SceneDrawingContext {
96    fn draw_line(&mut self, _object: DebugRenderObject, a: Vector, b: Vector, color: DebugColor) {
97        self.add_line(Line {
98            begin: Vector3::new(a.x, a.y, 0.0),
99            end: Vector3::new(b.x, b.y, 0.0),
100            color: Color::from(Hsl::new(color[0], color[1], color[2])),
101        })
102    }
103}
104
105impl rapier3d::pipeline::DebugRenderBackend for SceneDrawingContext {
106    fn draw_line(
107        &mut self,
108        _object: rapier3d::pipeline::DebugRenderObject,
109        a: rapier3d::math::Vector,
110        b: rapier3d::math::Vector,
111        color: rapier3d::pipeline::DebugColor,
112    ) {
113        self.add_line(Line {
114            begin: a.into(),
115            end: b.into(),
116            color: Color::from(Hsl::new(color[0], color[1], color[2])),
117        })
118    }
119}
120
121impl SceneDrawingContext {
122    /// Draws frustum with given color.
123    pub fn draw_frustum(&mut self, frustum: &Frustum, color: Color) {
124        let left_top_front = frustum.left_top_front_corner();
125        let left_bottom_front = frustum.left_bottom_front_corner();
126        let right_bottom_front = frustum.right_bottom_front_corner();
127        let right_top_front = frustum.right_top_front_corner();
128
129        let left_top_back = frustum.left_top_back_corner();
130        let left_bottom_back = frustum.left_bottom_back_corner();
131        let right_bottom_back = frustum.right_bottom_back_corner();
132        let right_top_back = frustum.right_top_back_corner();
133
134        // Front face
135        self.add_line(Line {
136            begin: left_top_front,
137            end: right_top_front,
138            color,
139        });
140        self.add_line(Line {
141            begin: right_top_front,
142            end: right_bottom_front,
143            color,
144        });
145        self.add_line(Line {
146            begin: right_bottom_front,
147            end: left_bottom_front,
148            color,
149        });
150        self.add_line(Line {
151            begin: left_bottom_front,
152            end: left_top_front,
153            color,
154        });
155
156        // Back face
157        self.add_line(Line {
158            begin: left_top_back,
159            end: right_top_back,
160            color,
161        });
162        self.add_line(Line {
163            begin: right_top_back,
164            end: right_bottom_back,
165            color,
166        });
167        self.add_line(Line {
168            begin: right_bottom_back,
169            end: left_bottom_back,
170            color,
171        });
172        self.add_line(Line {
173            begin: left_bottom_back,
174            end: left_top_back,
175            color,
176        });
177
178        // Edges
179        self.add_line(Line {
180            begin: left_top_front,
181            end: left_top_back,
182            color,
183        });
184        self.add_line(Line {
185            begin: right_top_front,
186            end: right_top_back,
187            color,
188        });
189        self.add_line(Line {
190            begin: right_bottom_front,
191            end: right_bottom_back,
192            color,
193        });
194        self.add_line(Line {
195            begin: left_bottom_front,
196            end: left_bottom_back,
197            color,
198        });
199    }
200
201    /// Draws axis-aligned bounding box with given color.
202    pub fn draw_aabb(&mut self, aabb: &AxisAlignedBoundingBox, color: Color) {
203        let left_bottom_front = Vector3::new(aabb.min.x, aabb.min.y, aabb.max.z);
204        let left_top_front = Vector3::new(aabb.min.x, aabb.max.y, aabb.max.z);
205        let right_top_front = Vector3::new(aabb.max.x, aabb.max.y, aabb.max.z);
206        let right_bottom_front = Vector3::new(aabb.max.x, aabb.min.y, aabb.max.z);
207
208        let left_bottom_back = Vector3::new(aabb.min.x, aabb.min.y, aabb.min.z);
209        let left_top_back = Vector3::new(aabb.min.x, aabb.max.y, aabb.min.z);
210        let right_top_back = Vector3::new(aabb.max.x, aabb.max.y, aabb.min.z);
211        let right_bottom_back = Vector3::new(aabb.max.x, aabb.min.y, aabb.min.z);
212
213        // Front face
214        self.add_line(Line {
215            begin: left_top_front,
216            end: right_top_front,
217            color,
218        });
219        self.add_line(Line {
220            begin: right_top_front,
221            end: right_bottom_front,
222            color,
223        });
224        self.add_line(Line {
225            begin: right_bottom_front,
226            end: left_bottom_front,
227            color,
228        });
229        self.add_line(Line {
230            begin: left_bottom_front,
231            end: left_top_front,
232            color,
233        });
234
235        // Back face
236        self.add_line(Line {
237            begin: left_top_back,
238            end: right_top_back,
239            color,
240        });
241        self.add_line(Line {
242            begin: right_top_back,
243            end: right_bottom_back,
244            color,
245        });
246        self.add_line(Line {
247            begin: right_bottom_back,
248            end: left_bottom_back,
249            color,
250        });
251        self.add_line(Line {
252            begin: left_bottom_back,
253            end: left_top_back,
254            color,
255        });
256
257        // Edges
258        self.add_line(Line {
259            begin: left_top_front,
260            end: left_top_back,
261            color,
262        });
263        self.add_line(Line {
264            begin: right_top_front,
265            end: right_top_back,
266            color,
267        });
268        self.add_line(Line {
269            begin: right_bottom_front,
270            end: right_bottom_back,
271            color,
272        });
273        self.add_line(Line {
274            begin: left_bottom_front,
275            end: left_bottom_back,
276            color,
277        });
278    }
279
280    /// Draws object-oriented bounding box with given color.
281    pub fn draw_oob(
282        &mut self,
283        aabb: &AxisAlignedBoundingBox,
284        transform: Matrix4<f32>,
285        color: Color,
286    ) {
287        let left_bottom_front = transform
288            .transform_point(&Point3::new(aabb.min.x, aabb.min.y, aabb.max.z))
289            .coords;
290        let left_top_front = transform
291            .transform_point(&Point3::new(aabb.min.x, aabb.max.y, aabb.max.z))
292            .coords;
293        let right_top_front = transform
294            .transform_point(&Point3::new(aabb.max.x, aabb.max.y, aabb.max.z))
295            .coords;
296        let right_bottom_front = transform
297            .transform_point(&Point3::new(aabb.max.x, aabb.min.y, aabb.max.z))
298            .coords;
299
300        let left_bottom_back = transform
301            .transform_point(&Point3::new(aabb.min.x, aabb.min.y, aabb.min.z))
302            .coords;
303        let left_top_back = transform
304            .transform_point(&Point3::new(aabb.min.x, aabb.max.y, aabb.min.z))
305            .coords;
306        let right_top_back = transform
307            .transform_point(&Point3::new(aabb.max.x, aabb.max.y, aabb.min.z))
308            .coords;
309        let right_bottom_back = transform
310            .transform_point(&Point3::new(aabb.max.x, aabb.min.y, aabb.min.z))
311            .coords;
312
313        // Front face
314        self.add_line(Line {
315            begin: left_top_front,
316            end: right_top_front,
317            color,
318        });
319        self.add_line(Line {
320            begin: right_top_front,
321            end: right_bottom_front,
322            color,
323        });
324        self.add_line(Line {
325            begin: right_bottom_front,
326            end: left_bottom_front,
327            color,
328        });
329        self.add_line(Line {
330            begin: left_bottom_front,
331            end: left_top_front,
332            color,
333        });
334
335        // Back face
336        self.add_line(Line {
337            begin: left_top_back,
338            end: right_top_back,
339            color,
340        });
341        self.add_line(Line {
342            begin: right_top_back,
343            end: right_bottom_back,
344            color,
345        });
346        self.add_line(Line {
347            begin: right_bottom_back,
348            end: left_bottom_back,
349            color,
350        });
351        self.add_line(Line {
352            begin: left_bottom_back,
353            end: left_top_back,
354            color,
355        });
356
357        // Edges
358        self.add_line(Line {
359            begin: left_top_front,
360            end: left_top_back,
361            color,
362        });
363        self.add_line(Line {
364            begin: right_top_front,
365            end: right_top_back,
366            color,
367        });
368        self.add_line(Line {
369            begin: right_bottom_front,
370            end: right_bottom_back,
371            color,
372        });
373        self.add_line(Line {
374            begin: left_bottom_front,
375            end: left_bottom_back,
376            color,
377        });
378    }
379
380    /// Draws transform as basis vectors.
381    pub fn draw_transform(&mut self, matrix: Matrix4<f32>) {
382        let x = matrix.transform_vector(&Vector3::x());
383        let y = matrix.transform_vector(&Vector3::y());
384        let z = matrix.transform_vector(&Vector3::z());
385        let origin = matrix.position();
386        self.add_line(Line {
387            begin: origin,
388            end: origin + x,
389            color: Color::RED,
390        });
391        self.add_line(Line {
392            begin: origin,
393            end: origin + y,
394            color: Color::GREEN,
395        });
396        self.add_line(Line {
397            begin: origin,
398            end: origin + z,
399            color: Color::BLUE,
400        });
401    }
402
403    /// Draws a triangle by given points.
404    pub fn draw_triangle(
405        &mut self,
406        a: Vector3<f32>,
407        b: Vector3<f32>,
408        c: Vector3<f32>,
409        color: Color,
410    ) {
411        self.add_line(Line {
412            begin: a,
413            end: b,
414            color,
415        });
416        self.add_line(Line {
417            begin: b,
418            end: c,
419            color,
420        });
421        self.add_line(Line {
422            begin: c,
423            end: a,
424            color,
425        });
426    }
427
428    /// Draws a pyramid by given points.
429    pub fn draw_pyramid(
430        &mut self,
431        top: Vector3<f32>,
432        a: Vector3<f32>,
433        b: Vector3<f32>,
434        c: Vector3<f32>,
435        d: Vector3<f32>,
436        color: Color,
437        transform: Matrix4<f32>,
438    ) {
439        let top = transform.position() + transform.transform_vector(&top);
440        let a = transform.position() + transform.transform_vector(&a);
441        let b = transform.position() + transform.transform_vector(&b);
442        let c = transform.position() + transform.transform_vector(&c);
443        let d = transform.position() + transform.transform_vector(&d);
444        self.draw_triangle(top, a, b, color);
445        self.draw_triangle(top, b, c, color);
446        self.draw_triangle(top, c, d, color);
447        self.draw_triangle(top, d, a, color);
448    }
449
450    /// Draws a sphere as a set of three circles around each axes.
451    pub fn draw_wire_sphere(
452        &mut self,
453        position: Vector3<f32>,
454        radius: f32,
455        segments: usize,
456        color: Color,
457    ) {
458        let translation = Matrix4::new_translation(&position);
459        self.draw_circle(Default::default(), radius, segments, translation, color);
460        self.draw_circle(
461            Default::default(),
462            radius,
463            segments,
464            translation
465                * UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 90.0f32.to_radians())
466                    .to_homogeneous(),
467            color,
468        );
469        self.draw_circle(
470            Default::default(),
471            radius,
472            segments,
473            translation
474                * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 90.0f32.to_radians())
475                    .to_homogeneous(),
476            color,
477        );
478    }
479
480    /// Draws a circle at given world-space position with given radius. `segments` could be used
481    /// to control quality of the circle.
482    pub fn draw_circle(
483        &mut self,
484        position: Vector3<f32>,
485        radius: f32,
486        segments: usize,
487        transform: Matrix4<f32>,
488        color: Color,
489    ) {
490        let d_phi = 2.0 * std::f32::consts::PI / segments as f32;
491        for i in 0..segments {
492            let x1 = position.x + radius * (d_phi * i as f32).cos();
493            let y1 = position.y + radius * (d_phi * i as f32).sin();
494            let x2 = position.x + radius * (d_phi * (i + 1) as f32).cos();
495            let y2 = position.y + radius * (d_phi * (i + 1) as f32).sin();
496
497            self.add_line(Line {
498                begin: transform.transform_point(&Point3::new(x1, y1, 0.0)).coords,
499                end: transform.transform_point(&Point3::new(x2, y2, 0.0)).coords,
500                color,
501            })
502        }
503    }
504
505    /// Draws a circle segment between two given angles. Center of the segment is defined by `position`,
506    /// `segments` defines quality of the shape.
507    pub fn draw_circle_segment(
508        &mut self,
509        position: Vector3<f32>,
510        radius: f32,
511        segments: usize,
512        begin_angle: f32,
513        end_angle: f32,
514        transform: Matrix4<f32>,
515        color: Color,
516    ) {
517        let d_angle = 2.0 * std::f32::consts::PI / segments as f32;
518        let mut angle = begin_angle;
519        while angle < end_angle {
520            let x1 = position.x + radius * (angle).cos();
521            let y1 = position.y + radius * (angle).sin();
522            let x2 = position.x + radius * (angle + d_angle).cos();
523            let y2 = position.y + radius * (angle + d_angle).sin();
524
525            self.add_line(Line {
526                begin: transform.transform_point(&Point3::new(x1, y1, 0.0)).coords,
527                end: transform.transform_point(&Point3::new(x2, y2, 0.0)).coords,
528                color,
529            });
530
531            angle += d_angle;
532        }
533    }
534
535    /// Draws a rectangle with given width and height.
536    pub fn draw_rectangle(
537        &mut self,
538        half_width: f32,
539        half_height: f32,
540        transform: Matrix4<f32>,
541        color: Color,
542    ) {
543        let a = transform
544            .transform_point(&Point3::new(-half_width, half_height, 0.0))
545            .coords;
546        let b = transform
547            .transform_point(&Point3::new(half_width, half_height, 0.0))
548            .coords;
549        let c = transform
550            .transform_point(&Point3::new(half_width, -half_height, 0.0))
551            .coords;
552        let d = transform
553            .transform_point(&Point3::new(-half_width, -half_height, 0.0))
554            .coords;
555        self.add_line(Line {
556            begin: a,
557            end: b,
558            color,
559        });
560        self.add_line(Line {
561            begin: b,
562            end: c,
563            color,
564        });
565        self.add_line(Line {
566            begin: c,
567            end: d,
568            color,
569        });
570        self.add_line(Line {
571            begin: d,
572            end: a,
573            color,
574        });
575    }
576
577    /// Draws a wire sphere with given parameters.
578    pub fn draw_sphere(
579        &mut self,
580        position: Vector3<f32>,
581        slices: usize,
582        stacks: usize,
583        radius: f32,
584        color: Color,
585    ) {
586        let d_theta = std::f32::consts::PI / slices as f32;
587        let d_phi = 2.0 * std::f32::consts::PI / stacks as f32;
588
589        for i in 0..stacks {
590            for j in 0..slices {
591                let nj = j + 1;
592                let ni = i + 1;
593
594                let k0 = radius * (d_theta * i as f32).sin();
595                let k1 = (d_phi * j as f32).cos();
596                let k2 = (d_phi * j as f32).sin();
597                let k3 = radius * (d_theta * i as f32).cos();
598
599                let k4 = radius * (d_theta * ni as f32).sin();
600                let k5 = (d_phi * nj as f32).cos();
601                let k6 = (d_phi * nj as f32).sin();
602                let k7 = radius * (d_theta * ni as f32).cos();
603
604                if i != (stacks - 1) {
605                    self.draw_triangle(
606                        position + Vector3::new(k0 * k1, k0 * k2, k3),
607                        position + Vector3::new(k4 * k1, k4 * k2, k7),
608                        position + Vector3::new(k4 * k5, k4 * k6, k7),
609                        color,
610                    );
611                }
612
613                if i != 0 {
614                    self.draw_triangle(
615                        position + Vector3::new(k4 * k5, k4 * k6, k7),
616                        position + Vector3::new(k0 * k5, k0 * k6, k3),
617                        position + Vector3::new(k0 * k1, k0 * k2, k3),
618                        color,
619                    );
620                }
621            }
622        }
623    }
624
625    /// Draws a wire sphere with given parameters.
626    pub fn draw_sphere_section(
627        &mut self,
628        radius: f32,
629        theta_range: Range<f32>,
630        theta_steps: usize,
631        phi_range: Range<f32>,
632        phi_steps: usize,
633        transform: Matrix4<f32>,
634        color: Color,
635    ) {
636        assert!(theta_range.start < theta_range.end);
637        assert!(phi_range.start < phi_range.end);
638
639        assert_ne!(phi_steps, 0);
640        assert_ne!(theta_steps, 0);
641
642        let theta_step = (theta_range.end - theta_range.start) / theta_steps as f32;
643        let phi_step = (phi_range.end - phi_range.start) / phi_steps as f32;
644
645        fn spherical_to_cartesian(radius: f32, theta: f32, phi: f32) -> Vector3<f32> {
646            Vector3::new(
647                radius * theta.sin() * phi.cos(),
648                radius * theta.cos(),
649                radius * theta.sin() * phi.sin(),
650            )
651        }
652
653        let mut theta = theta_range.start;
654        while theta < theta_range.end {
655            let mut phi = phi_range.start;
656            while phi < phi_range.end {
657                let p0 = transform
658                    .transform_point(&Point3::from(spherical_to_cartesian(radius, theta, phi)))
659                    .coords;
660                let p1 = transform
661                    .transform_point(&Point3::from(spherical_to_cartesian(
662                        radius,
663                        theta,
664                        phi + phi_step,
665                    )))
666                    .coords;
667                let p2 = transform
668                    .transform_point(&Point3::from(spherical_to_cartesian(
669                        radius,
670                        theta + theta_step,
671                        phi + phi_step,
672                    )))
673                    .coords;
674                let p3 = transform
675                    .transform_point(&Point3::from(spherical_to_cartesian(
676                        radius,
677                        theta + theta_step,
678                        phi,
679                    )))
680                    .coords;
681
682                self.draw_triangle(p0, p1, p2, color);
683                self.draw_triangle(p0, p2, p3, color);
684
685                phi += phi_step;
686            }
687            theta += theta_step;
688        }
689    }
690
691    /// Draws a wire Y oriented cone where the tip is on +Y with given parameters.
692    pub fn draw_cone(
693        &mut self,
694        sides: usize,
695        r: f32,
696        h: f32,
697        transform: Matrix4<f32>,
698        color: Color,
699        cap: bool,
700    ) {
701        let d_phi = 2.0 * std::f32::consts::PI / sides as f32;
702
703        let half_height = h / 2.0;
704
705        for i in 0..sides {
706            let nx0 = (d_phi * i as f32).cos();
707            let ny0 = (d_phi * i as f32).sin();
708            let nx1 = (d_phi * (i + 1) as f32).cos();
709            let ny1 = (d_phi * (i + 1) as f32).sin();
710
711            let x0 = r * nx0;
712            let z0 = r * ny0;
713            let x1 = r * nx1;
714            let z1 = r * ny1;
715
716            // back cap
717            if cap {
718                self.draw_triangle(
719                    transform
720                        .transform_point(&Point3::new(0.0, -half_height, 0.0))
721                        .coords,
722                    transform
723                        .transform_point(&Point3::new(x0, -half_height, z0))
724                        .coords,
725                    transform
726                        .transform_point(&Point3::new(x1, -half_height, z1))
727                        .coords,
728                    color,
729                );
730            }
731
732            // sides
733            self.draw_triangle(
734                transform
735                    .transform_point(&Point3::new(0.0, half_height, 0.0))
736                    .coords,
737                transform
738                    .transform_point(&Point3::new(x1, -half_height, z1))
739                    .coords,
740                transform
741                    .transform_point(&Point3::new(x0, -half_height, z0))
742                    .coords,
743                color,
744            );
745        }
746    }
747
748    /// Draws a wire cylinder with given parameters.
749    pub fn draw_cylinder(
750        &mut self,
751        sides: usize,
752        r: f32,
753        h: f32,
754        caps: bool,
755        transform: Matrix4<f32>,
756        color: Color,
757    ) {
758        let d_phi = 2.0 * std::f32::consts::PI / sides as f32;
759
760        let half_height = h / 2.0;
761
762        for i in 0..sides {
763            let nx0 = (d_phi * i as f32).cos();
764            let ny0 = (d_phi * i as f32).sin();
765            let nx1 = (d_phi * (i + 1) as f32).cos();
766            let ny1 = (d_phi * (i + 1) as f32).sin();
767
768            let x0 = r * nx0;
769            let z0 = r * ny0;
770            let x1 = r * nx1;
771            let z1 = r * ny1;
772
773            if caps {
774                // front cap
775                self.draw_triangle(
776                    transform
777                        .transform_point(&Point3::new(x1, half_height, z1))
778                        .coords,
779                    transform
780                        .transform_point(&Point3::new(x0, half_height, z0))
781                        .coords,
782                    transform
783                        .transform_point(&Point3::new(0.0, half_height, 0.0))
784                        .coords,
785                    color,
786                );
787
788                // back cap
789                self.draw_triangle(
790                    transform
791                        .transform_point(&Point3::new(x0, -half_height, z0))
792                        .coords,
793                    transform
794                        .transform_point(&Point3::new(x1, -half_height, z1))
795                        .coords,
796                    transform
797                        .transform_point(&Point3::new(0.0, -half_height, 0.0))
798                        .coords,
799                    color,
800                );
801            }
802
803            // sides
804            self.draw_triangle(
805                transform
806                    .transform_point(&Point3::new(x0, -half_height, z0))
807                    .coords,
808                transform
809                    .transform_point(&Point3::new(x0, half_height, z0))
810                    .coords,
811                transform
812                    .transform_point(&Point3::new(x1, -half_height, z1))
813                    .coords,
814                color,
815            );
816
817            self.draw_triangle(
818                transform
819                    .transform_point(&Point3::new(x1, -half_height, z1))
820                    .coords,
821                transform
822                    .transform_point(&Point3::new(x0, half_height, z0))
823                    .coords,
824                transform
825                    .transform_point(&Point3::new(x1, half_height, z1))
826                    .coords,
827                color,
828            );
829        }
830    }
831
832    /// Draws a flat capsule with given height and radius. `segments` defines quality of the shape.
833    pub fn draw_flat_capsule(
834        &mut self,
835        radius: f32,
836        height: f32,
837        segments: usize,
838        transform: Matrix4<f32>,
839        color: Color,
840    ) {
841        self.draw_circle_segment(
842            Vector3::new(0.0, height * 0.5, 0.0),
843            radius,
844            segments,
845            0.0,
846            std::f32::consts::PI,
847            transform,
848            color,
849        );
850
851        self.draw_circle_segment(
852            Vector3::new(0.0, -height * 0.5, 0.0),
853            radius,
854            segments,
855            std::f32::consts::PI,
856            std::f32::consts::TAU,
857            transform,
858            color,
859        );
860
861        self.add_line(Line {
862            begin: transform
863                .transform_point(&Point3::new(-radius, height * 0.5, 0.0))
864                .coords,
865            end: transform
866                .transform_point(&Point3::new(-radius, -height * 0.5, 0.0))
867                .coords,
868            color,
869        });
870        self.add_line(Line {
871            begin: transform
872                .transform_point(&Point3::new(radius, height * 0.5, 0.0))
873                .coords,
874            end: transform
875                .transform_point(&Point3::new(radius, -height * 0.5, 0.0))
876                .coords,
877            color,
878        });
879    }
880
881    /// Draws vertical capsule with given radius and height and then applies given transform.
882    pub fn draw_capsule(
883        &mut self,
884        radius: f32,
885        height: f32,
886        transform: Matrix4<f32>,
887        color: Color,
888    ) {
889        // Top cap
890        self.draw_sphere_section(
891            radius,
892            0.0..std::f32::consts::FRAC_PI_2,
893            10,
894            0.0..std::f32::consts::TAU,
895            10,
896            transform * Matrix4::new_translation(&Vector3::new(0.0, height * 0.5 - radius, 0.0)),
897            color,
898        );
899
900        // Bottom cap
901        self.draw_sphere_section(
902            radius,
903            std::f32::consts::PI..std::f32::consts::PI * 1.5,
904            10,
905            0.0..std::f32::consts::TAU,
906            10,
907            transform * Matrix4::new_translation(&Vector3::new(0.0, -height * 0.5 + radius, 0.0)),
908            color,
909        );
910
911        let cylinder_height = height - 2.0 * radius;
912
913        if cylinder_height > 0.0 {
914            self.draw_cylinder(10, radius, cylinder_height, false, transform, color);
915        }
916    }
917
918    /// Draws a flat capsule between two points with given radius. `segments` defines quality of
919    /// the shape.
920    pub fn draw_segment_flat_capsule(
921        &mut self,
922        begin: Vector2<f32>,
923        end: Vector2<f32>,
924        radius: f32,
925        segments: usize,
926        transform: Matrix4<f32>,
927        color: Color,
928    ) {
929        // Draw as two circles and a rectangle
930        // TODO: Draw this correctly
931        self.draw_circle(
932            Vector3::new(begin.x, begin.y, 0.0),
933            radius,
934            segments,
935            transform,
936            color,
937        );
938        self.draw_circle(
939            Vector3::new(end.x, end.y, 0.0),
940            radius,
941            segments,
942            transform,
943            color,
944        );
945        let perp = (end - begin)
946            .try_normalize(f32::EPSILON)
947            .map(|v| Vector2::new(v.y, -v.x).scale(radius))
948            .unwrap_or_default();
949
950        self.add_line(Line {
951            begin: transform
952                .transform_point(&Point3::from((begin - perp).to_homogeneous()))
953                .coords,
954            end: transform
955                .transform_point(&Point3::from((end - perp).to_homogeneous()))
956                .coords,
957            color,
958        });
959        self.add_line(Line {
960            begin: transform
961                .transform_point(&Point3::from((begin + perp).to_homogeneous()))
962                .coords,
963            end: transform
964                .transform_point(&Point3::from((end + perp).to_homogeneous()))
965                .coords,
966            color,
967        });
968    }
969
970    /// Draws capsule between two points with given tesselation and then applies given transform to all points.
971    pub fn draw_segment_capsule(
972        &mut self,
973        begin: Vector3<f32>,
974        end: Vector3<f32>,
975        radius: f32,
976        v_segments: usize,
977        h_segments: usize,
978        transform: Matrix4<f32>,
979        color: Color,
980    ) {
981        let axis = end - begin;
982        let length = axis.norm();
983
984        let z_axis = axis.try_normalize(f32::EPSILON).unwrap_or_else(Vector3::z);
985
986        let y_axis = z_axis
987            .cross(
988                &(if z_axis.y != 0.0 || z_axis.z != 0.0 {
989                    Vector3::x()
990                } else {
991                    Vector3::y()
992                }),
993            )
994            .try_normalize(f32::EPSILON)
995            .unwrap_or_else(Vector3::y);
996
997        let x_axis = z_axis
998            .cross(&y_axis)
999            .try_normalize(f32::EPSILON)
1000            .unwrap_or_else(Vector3::x); // CHECK
1001
1002        let shaft_point = |u: f32, v: f32| -> Vector3<f32> {
1003            transform
1004                .transform_point(&Point3::from(
1005                    begin
1006                        + x_axis.scale((std::f32::consts::TAU * u).cos() * radius)
1007                        + y_axis.scale((std::f32::consts::TAU * u).sin() * radius)
1008                        + z_axis.scale(v * length),
1009                ))
1010                .coords
1011        };
1012
1013        let start_hemisphere_point = |u: f32, v: f32| -> Vector3<f32> {
1014            let latitude = std::f32::consts::FRAC_PI_2 * (v - 1.0);
1015            transform
1016                .transform_point(&Point3::from(
1017                    begin
1018                        + x_axis.scale((std::f32::consts::TAU * u).cos() * latitude.cos() * radius)
1019                        + y_axis.scale((std::f32::consts::TAU * u).sin() * latitude.cos() * radius)
1020                        + z_axis.scale(latitude.sin() * radius),
1021                ))
1022                .coords
1023        };
1024
1025        let end_hemisphere_point = |u: f32, v: f32| -> Vector3<f32> {
1026            let latitude = std::f32::consts::FRAC_PI_2 * v;
1027            transform
1028                .transform_point(&Point3::from(
1029                    end + x_axis.scale((std::f32::consts::TAU * u).cos() * latitude.cos() * radius)
1030                        + y_axis.scale((std::f32::consts::TAU * u).sin() * latitude.cos() * radius)
1031                        + z_axis.scale(latitude.sin() * radius),
1032                ))
1033                .coords
1034        };
1035
1036        let dv = 1.0 / h_segments as f32;
1037        let du = 1.0 / v_segments as f32;
1038
1039        let mut u = 0.0;
1040        while u < 1.0 {
1041            let sa = shaft_point(u, 0.0);
1042            let sb = shaft_point(u, 1.0);
1043            let sc = shaft_point(u + du, 1.0);
1044            let sd = shaft_point(u + du, 0.0);
1045
1046            self.draw_triangle(sa, sb, sc, color);
1047            self.draw_triangle(sa, sc, sd, color);
1048
1049            u += du;
1050        }
1051
1052        u = 0.0;
1053        while u < 1.0 {
1054            let mut v = 0.0;
1055            while v < 1.0 {
1056                let sa = start_hemisphere_point(u, v);
1057                let sb = start_hemisphere_point(u, v + dv);
1058                let sc = start_hemisphere_point(u + du, v + dv);
1059                let sd = start_hemisphere_point(u + du, v);
1060
1061                self.draw_triangle(sa, sb, sc, color);
1062                self.draw_triangle(sa, sc, sd, color);
1063
1064                let ea = end_hemisphere_point(u, v);
1065                let eb = end_hemisphere_point(u, v + dv);
1066                let ec = end_hemisphere_point(u + du, v + dv);
1067                let ed = end_hemisphere_point(u + du, v);
1068
1069                self.draw_triangle(ea, eb, ec, color);
1070                self.draw_triangle(ea, ec, ed, color);
1071
1072                v += dv;
1073            }
1074
1075            u += du;
1076        }
1077    }
1078
1079    /// Draws an Y+ oriented arrow with the given parameters.
1080    pub fn draw_arrow(
1081        &mut self,
1082        sides: usize,
1083        color: Color,
1084        length: f32,
1085        radius: f32,
1086        transform: Matrix4<f32>,
1087    ) {
1088        self.draw_cylinder(sides, radius, length, true, transform, color);
1089
1090        let head_radius = radius * 2.0;
1091        let head_height = radius * 4.0;
1092
1093        self.draw_cone(
1094            sides,
1095            head_radius,
1096            head_height,
1097            transform
1098                * Matrix4::new_translation(&Vector3::new(0.0, (length + head_height) * 0.5, 0.0)),
1099            color,
1100            true,
1101        );
1102    }
1103
1104    /// Adds single line into internal buffer.
1105    pub fn add_line(&mut self, line: Line) {
1106        self.lines.push(line);
1107    }
1108
1109    /// Removes all lines from internal buffer. For dynamic drawing you should call it
1110    /// every update tick of your application.
1111    pub fn clear_lines(&mut self) {
1112        self.lines.clear()
1113    }
1114}