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