Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drawing Primitives with Gizmos #11072

Merged
merged 38 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8448ab9
feat(circles): generalize circles in gizmos to ellipses
RobWalt Dec 22, 2023
7ae9aa0
feat(primitives): first feature implementation
RobWalt Dec 23, 2023
5596ab6
feat: add gizmo trait impl for all 2D primitives
RobWalt Dec 26, 2023
5462a1f
feat: add gizmo trait impl for all 3D primitives
RobWalt Dec 26, 2023
d7cab7c
chore: docs + self-review
RobWalt Jan 5, 2024
b50a5d6
chore(gizmo-primitives): impl Default for new struct
RobWalt Jan 8, 2024
d4b6de6
fix(primitives): adjust segments to look more uniform
RobWalt Jan 8, 2024
ef089f9
feat(arc3d): explore different approach
RobWalt Jan 9, 2024
bde2496
refactor(primitives): factor out common code
RobWalt Jan 9, 2024
0f836e1
chore(primitives): cleanup
RobWalt Jan 9, 2024
9d48eb5
chore(examples): add gizmo primitives examples
RobWalt Jan 9, 2024
24a6fc8
refactor(primitives): builder pattern
RobWalt Jan 11, 2024
aee01fd
refactor(primitives): builder pattern 3d
RobWalt Jan 16, 2024
be378eb
fix(lifetimes): fix lifetime issues
RobWalt Jan 25, 2024
90c660e
chore(cleanup): remove the nix stuff
RobWalt Jan 25, 2024
3e1848d
fix(rebase): fixup gizmo changes after rebase
RobWalt Jan 26, 2024
2d2e556
refactor(arc3d): redesign arc3d API
RobWalt Jan 26, 2024
f46ac68
chore(cleanup): cleanup arc changes
RobWalt Jan 26, 2024
3b37891
fix(review): make API consistent
RobWalt Jan 27, 2024
bfbf12c
fix(review): add rotation for ellipses
RobWalt Jan 27, 2024
40df6d4
fix(review): apply review suggestions (see details)
RobWalt Jan 28, 2024
43c9e91
chore(cleanup): remove development comments
RobWalt Jan 28, 2024
3bac10d
fix(gizmos): switch mental model from Z-up to Y-up
RobWalt Jan 29, 2024
12b4d2c
fix(gizmos): center cone and conical frustrum rendering
RobWalt Jan 29, 2024
85d245f
fix(gizmo-examples): add user facing explanations
RobWalt Jan 29, 2024
10f3df0
fix(cleanup): capsule3d changes
RobWalt Jan 31, 2024
1f68958
chore(cleanup): implement minor review comments
RobWalt Jan 31, 2024
cf383e4
feat(capsule2d): add gizmo impl for capusle 2d
RobWalt Jan 31, 2024
a6dd10e
fix(prelude): auto export new gizmo traits in prelude
RobWalt Jan 31, 2024
0fd2c46
chore(exmaples): clean up examples
RobWalt Jan 31, 2024
be9b746
fix(gizmos): implement review suggestions
RobWalt Jan 31, 2024
15c68e1
fix(tests): fix doc tests + half_size consistency
RobWalt Jan 31, 2024
edf5e4f
chore(cleanup): rename and move systems around
RobWalt Jan 31, 2024
30c620a
chore(fix-docs): doc links were broken
RobWalt Jan 31, 2024
00aefe0
chore(CI): fix error from `bevy_internal` CI lint
RobWalt Jan 31, 2024
7c64e9c
chore(typo): fixes via spell checking
RobWalt Feb 1, 2024
d3df91f
chore(examples): consistent user facing texts
RobWalt Feb 1, 2024
1531339
docs: increase amount of documentation
RobWalt Feb 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/bevy_gizmos/src/arcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
///
/// # Builder methods
/// The number of segments of the arc (i.e. the level of detail) can be adjusted with the
/// `.segements(...)` method.
/// `.segments(...)` method.
///
/// # Example
/// ```
Expand Down Expand Up @@ -190,7 +190,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
///
/// # Builder methods
/// The number of segments of the arc (i.e. the level of detail) can be adjusted with the
/// `.segements(...)` method.
/// `.segments(...)` method.
///
/// # Examples
/// ```
Expand Down Expand Up @@ -236,7 +236,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
///
/// # Builder methods
/// The number of segments of the arc (i.e. the level of detail) can be adjusted with the
/// `.segements(...)` method.
/// `.segments(...)` method.
///
/// # Examples
/// ```
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_gizmos/src/arrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl<T: GizmoConfigGroup> Drop for ArrowBuilder<'_, '_, '_, T> {
}

impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
/// Draw an arrow in 3D, from `start` to `end`. Has four tips for convienent viewing from any direction.
/// Draw an arrow in 3D, from `start` to `end`. Has four tips for convenient viewing from any direction.
///
/// This should be called for each frame the arrow needs to be rendered.
///
Expand Down
138 changes: 111 additions & 27 deletions crates/bevy_gizmos/src/circles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,98 @@
//! and assorted support items.

use crate::prelude::{GizmoConfigGroup, Gizmos};
use bevy_math::Mat2;
use bevy_math::{primitives::Direction3d, Quat, Vec2, Vec3};
use bevy_render::color::Color;
use std::f32::consts::TAU;

pub(crate) const DEFAULT_CIRCLE_SEGMENTS: usize = 32;

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

impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
/// Draw an ellipse in 3D at `position` with the flat side facing `normal`.
///
/// This should be called for each frame the ellipse needs to be rendered.
///
/// # Example
/// ```
/// # use bevy_gizmos::prelude::*;
/// # use bevy_render::prelude::*;
/// # use bevy_math::prelude::*;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.ellipse(Vec3::ZERO, Quat::IDENTITY, Vec2::new(1., 2.), Color::GREEN);
///
/// // Ellipses have 32 line-segments by default.
/// // You may want to increase this for larger ellipses.
/// gizmos
/// .ellipse(Vec3::ZERO, Quat::IDENTITY, Vec2::new(5., 1.), Color::RED)
/// .segments(64);
/// }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
#[inline]
pub fn ellipse(
&mut self,
position: Vec3,
rotation: Quat,
half_size: Vec2,
color: Color,
) -> EllipseBuilder<'_, 'w, 's, T> {
EllipseBuilder {
gizmos: self,
position,
rotation,
half_size,
color,
segments: DEFAULT_CIRCLE_SEGMENTS,
}
}

/// Draw an ellipse in 2D.
///
/// This should be called for each frame the ellipse needs to be rendered.
///
/// # Example
/// ```
/// # use bevy_gizmos::prelude::*;
/// # use bevy_render::prelude::*;
/// # use bevy_math::prelude::*;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.ellipse_2d(Vec2::ZERO, 180.0_f32.to_radians(), Vec2::new(2., 1.), Color::GREEN);
///
/// // Ellipses have 32 line-segments by default.
/// // You may want to increase this for larger ellipses.
/// gizmos
/// .ellipse_2d(Vec2::ZERO, 180.0_f32.to_radians(), Vec2::new(5., 1.), Color::RED)
/// .segments(64);
/// }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
#[inline]
pub fn ellipse_2d(
&mut self,
position: Vec2,
angle: f32,
half_size: Vec2,
color: Color,
) -> Ellipse2dBuilder<'_, 'w, 's, T> {
Ellipse2dBuilder {
gizmos: self,
position,
rotation: Mat2::from_angle(angle),
half_size,
color,
segments: DEFAULT_CIRCLE_SEGMENTS,
}
}

/// Draw a circle in 3D at `position` with the flat side facing `normal`.
///
/// This should be called for each frame the circle needs to be rendered.
Expand Down Expand Up @@ -45,12 +123,12 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
normal: Direction3d,
radius: f32,
color: Color,
) -> CircleBuilder<'_, 'w, 's, T> {
CircleBuilder {
) -> EllipseBuilder<'_, 'w, 's, T> {
EllipseBuilder {
gizmos: self,
position,
normal,
radius,
rotation: Quat::from_rotation_arc(Vec3::Z, *normal),
half_size: Vec2::splat(radius),
color,
segments: DEFAULT_CIRCLE_SEGMENTS,
}
Expand Down Expand Up @@ -82,70 +160,76 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
position: Vec2,
radius: f32,
color: Color,
) -> Circle2dBuilder<'_, 'w, 's, T> {
Circle2dBuilder {
) -> Ellipse2dBuilder<'_, 'w, 's, T> {
Ellipse2dBuilder {
gizmos: self,
position,
radius,
rotation: Mat2::IDENTITY,
half_size: Vec2::splat(radius),
color,
segments: DEFAULT_CIRCLE_SEGMENTS,
}
}
}

/// A builder returned by [`Gizmos::circle`].
pub struct CircleBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
/// A builder returned by [`Gizmos::ellipse`].
pub struct EllipseBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
gizmos: &'a mut Gizmos<'w, 's, T>,
position: Vec3,
normal: Direction3d,
radius: f32,
rotation: Quat,
half_size: Vec2,
color: Color,
segments: usize,
}

impl<T: GizmoConfigGroup> CircleBuilder<'_, '_, '_, T> {
/// Set the number of line-segments for this circle.
impl<T: GizmoConfigGroup> EllipseBuilder<'_, '_, '_, T> {
/// Set the number of line-segments for this ellipse.
pub fn segments(mut self, segments: usize) -> Self {
self.segments = segments;
self
}
}

impl<T: GizmoConfigGroup> Drop for CircleBuilder<'_, '_, '_, T> {
impl<T: GizmoConfigGroup> Drop for EllipseBuilder<'_, '_, '_, T> {
fn drop(&mut self) {
if !self.gizmos.enabled {
return;
}
let rotation = Quat::from_rotation_arc(Vec3::Z, *self.normal);
let positions = circle_inner(self.radius, self.segments)
.map(|vec2| self.position + rotation * vec2.extend(0.));

let positions = ellipse_inner(self.half_size, self.segments)
.map(|vec2| self.rotation * vec2.extend(0.))
.map(|vec3| vec3 + self.position);
self.gizmos.linestrip(positions, self.color);
}
}

/// A builder returned by [`Gizmos::circle_2d`].
pub struct Circle2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
/// A builder returned by [`Gizmos::ellipse_2d`].
pub struct Ellipse2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
gizmos: &'a mut Gizmos<'w, 's, T>,
position: Vec2,
radius: f32,
rotation: Mat2,
half_size: Vec2,
color: Color,
segments: usize,
}

impl<T: GizmoConfigGroup> Circle2dBuilder<'_, '_, '_, T> {
/// Set the number of line-segments for this circle.
impl<T: GizmoConfigGroup> Ellipse2dBuilder<'_, '_, '_, T> {
/// Set the number of line-segments for this ellipse.
pub fn segments(mut self, segments: usize) -> Self {
self.segments = segments;
self
}
}

impl<T: GizmoConfigGroup> Drop for Circle2dBuilder<'_, '_, '_, T> {
impl<T: GizmoConfigGroup> Drop for Ellipse2dBuilder<'_, '_, '_, T> {
fn drop(&mut self) {
if !self.gizmos.enabled {
return;
}
let positions = circle_inner(self.radius, self.segments).map(|vec2| vec2 + self.position);
};

let positions = ellipse_inner(self.half_size, self.segments)
.map(|vec2| self.rotation * vec2)
.map(|vec2| vec2 + self.position);
self.gizmos.linestrip_2d(positions, self.color);
}
}
2 changes: 2 additions & 0 deletions crates/bevy_gizmos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod arrows;
pub mod circles;
pub mod config;
pub mod gizmos;
pub mod primitives;

#[cfg(feature = "bevy_sprite")]
mod pipeline_2d;
Expand All @@ -45,6 +46,7 @@ pub mod prelude {
aabb::{AabbGizmoConfigGroup, ShowAabbGizmo},
config::{DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore},
gizmos::Gizmos,
primitives::{dim2::GizmoPrimitive2d, dim3::GizmoPrimitive3d},
AppGizmoBuilder,
};
}
Expand Down
Loading