Skip to content

Commit 041731b

Browse files
RobWaltnothendev
andauthored
Drawing Primitives with Gizmos (#11072)
The PR is in a reviewable state now in the sense that the basic implementations are there. There are still some ToDos that I'm aware of: - [x] docs for all the new structs and traits - [x] implement `Default` and derive other useful traits for the new structs - [x] Take a look at the notes again (Do this after a first round of reviews) - [x] Take care of the repetition in the circle drawing functions --- # Objective - TLDR: This PR enables us to quickly draw all the newly added primitives from `bevy_math` in immediate mode with gizmos - Addresses #10571 ## Solution - This implements the first design idea I had that covered everything that was mentioned in the Issue #10571 (comment) --- ## Caveats - I added the `Primitive(2/3)d` impls for `Direction(2/3)d` to make them work with the current solution. We could impose less strict requirements for the gizmoable objects and remove the impls afterwards if the community doesn't like the current approach. --- ## Changelog - implement capabilities to draw ellipses on the gizmo in general (this was required to have some code which is able to draw the ellipse primitive) - refactored circle drawing code to use the more general ellipse drawing code to keep code duplication low - implement `Primitive2d` for `Direction2d` and impl `Primitive3d` for `Direction3d` - implement trait to draw primitives with specialized details with gizmos - `GizmoPrimitive2d` for all the 2D primitives - `GizmoPrimitive3d` for all the 3D primitives - (question while writing this: Does it actually matter if we split this in 2D and 3D? I guess it could be useful in the future if we do something based on the main rendering mode even though atm it's kinda useless) --- --------- Co-authored-by: nothendev <[email protected]>
1 parent e2916fb commit 041731b

File tree

12 files changed

+2104
-43
lines changed

12 files changed

+2104
-43
lines changed

crates/bevy_gizmos/src/arcs.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
134134
///
135135
/// # Builder methods
136136
/// The number of segments of the arc (i.e. the level of detail) can be adjusted with the
137-
/// `.segements(...)` method.
137+
/// `.segments(...)` method.
138138
///
139139
/// # Example
140140
/// ```
@@ -190,7 +190,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
190190
///
191191
/// # Builder methods
192192
/// The number of segments of the arc (i.e. the level of detail) can be adjusted with the
193-
/// `.segements(...)` method.
193+
/// `.segments(...)` method.
194194
///
195195
/// # Examples
196196
/// ```
@@ -236,7 +236,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
236236
///
237237
/// # Builder methods
238238
/// The number of segments of the arc (i.e. the level of detail) can be adjusted with the
239-
/// `.segements(...)` method.
239+
/// `.segments(...)` method.
240240
///
241241
/// # Examples
242242
/// ```

crates/bevy_gizmos/src/arrows.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ impl<T: GizmoConfigGroup> Drop for ArrowBuilder<'_, '_, '_, T> {
6767
}
6868

6969
impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
70-
/// Draw an arrow in 3D, from `start` to `end`. Has four tips for convienent viewing from any direction.
70+
/// Draw an arrow in 3D, from `start` to `end`. Has four tips for convenient viewing from any direction.
7171
///
7272
/// This should be called for each frame the arrow needs to be rendered.
7373
///

crates/bevy_gizmos/src/circles.rs

Lines changed: 111 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,98 @@
44
//! and assorted support items.
55
66
use crate::prelude::{GizmoConfigGroup, Gizmos};
7+
use bevy_math::Mat2;
78
use bevy_math::{primitives::Direction3d, Quat, Vec2, Vec3};
89
use bevy_render::color::Color;
910
use std::f32::consts::TAU;
1011

1112
pub(crate) const DEFAULT_CIRCLE_SEGMENTS: usize = 32;
1213

13-
fn circle_inner(radius: f32, segments: usize) -> impl Iterator<Item = Vec2> {
14+
fn ellipse_inner(half_size: Vec2, segments: usize) -> impl Iterator<Item = Vec2> {
1415
(0..segments + 1).map(move |i| {
1516
let angle = i as f32 * TAU / segments as f32;
16-
Vec2::from(angle.sin_cos()) * radius
17+
let (x, y) = angle.sin_cos();
18+
Vec2::new(x, y) * half_size
1719
})
1820
}
1921

2022
impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
23+
/// Draw an ellipse in 3D at `position` with the flat side facing `normal`.
24+
///
25+
/// This should be called for each frame the ellipse needs to be rendered.
26+
///
27+
/// # Example
28+
/// ```
29+
/// # use bevy_gizmos::prelude::*;
30+
/// # use bevy_render::prelude::*;
31+
/// # use bevy_math::prelude::*;
32+
/// fn system(mut gizmos: Gizmos) {
33+
/// gizmos.ellipse(Vec3::ZERO, Quat::IDENTITY, Vec2::new(1., 2.), Color::GREEN);
34+
///
35+
/// // Ellipses have 32 line-segments by default.
36+
/// // You may want to increase this for larger ellipses.
37+
/// gizmos
38+
/// .ellipse(Vec3::ZERO, Quat::IDENTITY, Vec2::new(5., 1.), Color::RED)
39+
/// .segments(64);
40+
/// }
41+
/// # bevy_ecs::system::assert_is_system(system);
42+
/// ```
43+
#[inline]
44+
pub fn ellipse(
45+
&mut self,
46+
position: Vec3,
47+
rotation: Quat,
48+
half_size: Vec2,
49+
color: Color,
50+
) -> EllipseBuilder<'_, 'w, 's, T> {
51+
EllipseBuilder {
52+
gizmos: self,
53+
position,
54+
rotation,
55+
half_size,
56+
color,
57+
segments: DEFAULT_CIRCLE_SEGMENTS,
58+
}
59+
}
60+
61+
/// Draw an ellipse in 2D.
62+
///
63+
/// This should be called for each frame the ellipse needs to be rendered.
64+
///
65+
/// # Example
66+
/// ```
67+
/// # use bevy_gizmos::prelude::*;
68+
/// # use bevy_render::prelude::*;
69+
/// # use bevy_math::prelude::*;
70+
/// fn system(mut gizmos: Gizmos) {
71+
/// gizmos.ellipse_2d(Vec2::ZERO, 180.0_f32.to_radians(), Vec2::new(2., 1.), Color::GREEN);
72+
///
73+
/// // Ellipses have 32 line-segments by default.
74+
/// // You may want to increase this for larger ellipses.
75+
/// gizmos
76+
/// .ellipse_2d(Vec2::ZERO, 180.0_f32.to_radians(), Vec2::new(5., 1.), Color::RED)
77+
/// .segments(64);
78+
/// }
79+
/// # bevy_ecs::system::assert_is_system(system);
80+
/// ```
81+
#[inline]
82+
pub fn ellipse_2d(
83+
&mut self,
84+
position: Vec2,
85+
angle: f32,
86+
half_size: Vec2,
87+
color: Color,
88+
) -> Ellipse2dBuilder<'_, 'w, 's, T> {
89+
Ellipse2dBuilder {
90+
gizmos: self,
91+
position,
92+
rotation: Mat2::from_angle(angle),
93+
half_size,
94+
color,
95+
segments: DEFAULT_CIRCLE_SEGMENTS,
96+
}
97+
}
98+
2199
/// Draw a circle in 3D at `position` with the flat side facing `normal`.
22100
///
23101
/// This should be called for each frame the circle needs to be rendered.
@@ -45,12 +123,12 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
45123
normal: Direction3d,
46124
radius: f32,
47125
color: Color,
48-
) -> CircleBuilder<'_, 'w, 's, T> {
49-
CircleBuilder {
126+
) -> EllipseBuilder<'_, 'w, 's, T> {
127+
EllipseBuilder {
50128
gizmos: self,
51129
position,
52-
normal,
53-
radius,
130+
rotation: Quat::from_rotation_arc(Vec3::Z, *normal),
131+
half_size: Vec2::splat(radius),
54132
color,
55133
segments: DEFAULT_CIRCLE_SEGMENTS,
56134
}
@@ -82,70 +160,76 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
82160
position: Vec2,
83161
radius: f32,
84162
color: Color,
85-
) -> Circle2dBuilder<'_, 'w, 's, T> {
86-
Circle2dBuilder {
163+
) -> Ellipse2dBuilder<'_, 'w, 's, T> {
164+
Ellipse2dBuilder {
87165
gizmos: self,
88166
position,
89-
radius,
167+
rotation: Mat2::IDENTITY,
168+
half_size: Vec2::splat(radius),
90169
color,
91170
segments: DEFAULT_CIRCLE_SEGMENTS,
92171
}
93172
}
94173
}
95174

96-
/// A builder returned by [`Gizmos::circle`].
97-
pub struct CircleBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
175+
/// A builder returned by [`Gizmos::ellipse`].
176+
pub struct EllipseBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
98177
gizmos: &'a mut Gizmos<'w, 's, T>,
99178
position: Vec3,
100-
normal: Direction3d,
101-
radius: f32,
179+
rotation: Quat,
180+
half_size: Vec2,
102181
color: Color,
103182
segments: usize,
104183
}
105184

106-
impl<T: GizmoConfigGroup> CircleBuilder<'_, '_, '_, T> {
107-
/// Set the number of line-segments for this circle.
185+
impl<T: GizmoConfigGroup> EllipseBuilder<'_, '_, '_, T> {
186+
/// Set the number of line-segments for this ellipse.
108187
pub fn segments(mut self, segments: usize) -> Self {
109188
self.segments = segments;
110189
self
111190
}
112191
}
113192

114-
impl<T: GizmoConfigGroup> Drop for CircleBuilder<'_, '_, '_, T> {
193+
impl<T: GizmoConfigGroup> Drop for EllipseBuilder<'_, '_, '_, T> {
115194
fn drop(&mut self) {
116195
if !self.gizmos.enabled {
117196
return;
118197
}
119-
let rotation = Quat::from_rotation_arc(Vec3::Z, *self.normal);
120-
let positions = circle_inner(self.radius, self.segments)
121-
.map(|vec2| self.position + rotation * vec2.extend(0.));
198+
199+
let positions = ellipse_inner(self.half_size, self.segments)
200+
.map(|vec2| self.rotation * vec2.extend(0.))
201+
.map(|vec3| vec3 + self.position);
122202
self.gizmos.linestrip(positions, self.color);
123203
}
124204
}
125205

126-
/// A builder returned by [`Gizmos::circle_2d`].
127-
pub struct Circle2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
206+
/// A builder returned by [`Gizmos::ellipse_2d`].
207+
pub struct Ellipse2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
128208
gizmos: &'a mut Gizmos<'w, 's, T>,
129209
position: Vec2,
130-
radius: f32,
210+
rotation: Mat2,
211+
half_size: Vec2,
131212
color: Color,
132213
segments: usize,
133214
}
134215

135-
impl<T: GizmoConfigGroup> Circle2dBuilder<'_, '_, '_, T> {
136-
/// Set the number of line-segments for this circle.
216+
impl<T: GizmoConfigGroup> Ellipse2dBuilder<'_, '_, '_, T> {
217+
/// Set the number of line-segments for this ellipse.
137218
pub fn segments(mut self, segments: usize) -> Self {
138219
self.segments = segments;
139220
self
140221
}
141222
}
142223

143-
impl<T: GizmoConfigGroup> Drop for Circle2dBuilder<'_, '_, '_, T> {
224+
impl<T: GizmoConfigGroup> Drop for Ellipse2dBuilder<'_, '_, '_, T> {
144225
fn drop(&mut self) {
145226
if !self.gizmos.enabled {
146227
return;
147-
}
148-
let positions = circle_inner(self.radius, self.segments).map(|vec2| vec2 + self.position);
228+
};
229+
230+
let positions = ellipse_inner(self.half_size, self.segments)
231+
.map(|vec2| self.rotation * vec2)
232+
.map(|vec2| vec2 + self.position);
149233
self.gizmos.linestrip_2d(positions, self.color);
150234
}
151235
}

crates/bevy_gizmos/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub mod arrows;
3232
pub mod circles;
3333
pub mod config;
3434
pub mod gizmos;
35+
pub mod primitives;
3536

3637
#[cfg(feature = "bevy_sprite")]
3738
mod pipeline_2d;
@@ -45,6 +46,7 @@ pub mod prelude {
4546
aabb::{AabbGizmoConfigGroup, ShowAabbGizmo},
4647
config::{DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore},
4748
gizmos::Gizmos,
49+
primitives::{dim2::GizmoPrimitive2d, dim3::GizmoPrimitive3d},
4850
AppGizmoBuilder,
4951
};
5052
}

0 commit comments

Comments
 (0)