Skip to content

Commit

Permalink
Contextually clearing gizmos (#10973)
Browse files Browse the repository at this point in the history
# Objective
Allow `Gizmos` to work in `FixedUpdate` without any changes needed. This
changes `Gizmos` from being a purely immediate mode api, but allows the
user to use it as if it were an immediate mode API regardless of
schedule context.

Also allows for extending by other custom schedules by adding their own
`GizmoStorage<Clear>` and the requisite systems:
- `propagate_gizmos::<Clear>` before `update_gizmo_meshes`
- `stash_default_gizmos` when starting a clear context
- `pop_default_gizmos` when ending a clear context
- `collect_default_gizmos` when grabbing the requested gizmos 
- `clear_gizmos` for clearing the context's gizmos

## Solution
Adds a generic to `Gizmos` that defaults to `Update` (the current way
gizmos works). When entering a new clear context the default `Gizmos`
gets swapped out for that context's duration so the context can collect
the gizmos requested.

Prior work: #9153

## To do
- [x] `FixedUpdate` should probably get its own First, Pre, Update,
Post, Last system sets for this. Otherwise users will need to make sure
to order their systems before `clear_gizmos`. This could alternatively
be fixed by moving the setup of this to `bevy_time::fixed`?
   PR to fix this issue: #10977
- [x] use mem::take internally for the swaps?
- [x] Better name for the `Context` generic on gizmos? `Clear`?

---

## Changelog
- Gizmos drawn in `FixedMain` now last until the next `FixedMain`
iteration runs.
  • Loading branch information
Aceeri authored Apr 23, 2024
1 parent fcd87b2 commit b1ab036
Show file tree
Hide file tree
Showing 10 changed files with 787 additions and 209 deletions.
1 change: 1 addition & 0 deletions crates/bevy_gizmos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.14.0-dev" }
bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.14.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.14.0-dev" }
bevy_gizmos_macros = { path = "macros", version = "0.14.0-dev" }
bevy_time = { path = "../bevy_time", version = "0.14.0-dev" }

bytemuck = "1.0"

Expand Down
62 changes: 47 additions & 15 deletions crates/bevy_gizmos/src/arcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ use std::f32::consts::TAU;

// === 2D ===

impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// Draw an arc, which is a part of the circumference of a circle, in 2D.
///
/// This should be called for each frame the arc needs to be rendered.
Expand Down Expand Up @@ -50,7 +54,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
arc_angle: f32,
radius: f32,
color: impl Into<Color>,
) -> Arc2dBuilder<'_, 'w, 's, T> {
) -> Arc2dBuilder<'_, 'w, 's, Config, Clear> {
Arc2dBuilder {
gizmos: self,
position,
Expand All @@ -64,8 +68,12 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
}

/// A builder returned by [`Gizmos::arc_2d`].
pub struct Arc2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
gizmos: &'a mut Gizmos<'w, 's, T>,
pub struct Arc2dBuilder<'a, 'w, 's, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
position: Vec2,
direction_angle: f32,
arc_angle: f32,
Expand All @@ -74,15 +82,23 @@ pub struct Arc2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
segments: Option<usize>,
}

impl<T: GizmoConfigGroup> Arc2dBuilder<'_, '_, '_, T> {
impl<Config, Clear> Arc2dBuilder<'_, '_, '_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// Set the number of line-segments for this arc.
pub fn segments(mut self, segments: usize) -> Self {
self.segments.replace(segments);
self
}
}

impl<T: GizmoConfigGroup> Drop for Arc2dBuilder<'_, '_, '_, T> {
impl<Config, Clear> Drop for Arc2dBuilder<'_, '_, '_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
fn drop(&mut self) {
if !self.gizmos.enabled {
return;
Expand Down Expand Up @@ -114,7 +130,11 @@ fn arc_2d_inner(

// === 3D ===

impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// Draw an arc, which is a part of the circumference of a circle, in 3D. For default values
/// this is drawing a standard arc. A standard arc is defined as
///
Expand Down Expand Up @@ -169,7 +189,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
position: Vec3,
rotation: Quat,
color: impl Into<Color>,
) -> Arc3dBuilder<'_, 'w, 's, T> {
) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> {
Arc3dBuilder {
gizmos: self,
start_vertex: Vec3::X,
Expand Down Expand Up @@ -226,7 +246,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
from: Vec3,
to: Vec3,
color: impl Into<Color>,
) -> Arc3dBuilder<'_, 'w, 's, T> {
) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> {
self.arc_from_to(center, from, to, color, |x| x)
}

Expand Down Expand Up @@ -273,7 +293,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
from: Vec3,
to: Vec3,
color: impl Into<Color>,
) -> Arc3dBuilder<'_, 'w, 's, T> {
) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> {
self.arc_from_to(center, from, to, color, |angle| {
if angle > 0.0 {
TAU - angle
Expand All @@ -293,7 +313,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
to: Vec3,
color: impl Into<Color>,
angle_fn: impl Fn(f32) -> f32,
) -> Arc3dBuilder<'_, 'w, 's, T> {
) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> {
// `from` and `to` can be the same here since in either case nothing gets rendered and the
// orientation ambiguity of `up` doesn't matter
let from_axis = (from - center).normalize_or_zero();
Expand All @@ -320,8 +340,12 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
}

/// A builder returned by [`Gizmos::arc_2d`].
pub struct Arc3dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
gizmos: &'a mut Gizmos<'w, 's, T>,
pub struct Arc3dBuilder<'a, 'w, 's, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
// this is the vertex the arc starts on in the XZ plane. For the normal arc_3d method this is
// always starting at Vec3::X. For the short/long arc methods we actually need a way to start
// at the from position and this is where this internal field comes into play. Some implicit
Expand All @@ -340,15 +364,23 @@ pub struct Arc3dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
segments: Option<usize>,
}

impl<T: GizmoConfigGroup> Arc3dBuilder<'_, '_, '_, T> {
impl<Config, Clear> Arc3dBuilder<'_, '_, '_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// Set the number of line-segments for this arc.
pub fn segments(mut self, segments: usize) -> Self {
self.segments.replace(segments);
self
}
}

impl<T: GizmoConfigGroup> Drop for Arc3dBuilder<'_, '_, '_, T> {
impl<Config, Clear> Drop for Arc3dBuilder<'_, '_, '_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
fn drop(&mut self) {
if !self.gizmos.enabled {
return;
Expand Down
36 changes: 28 additions & 8 deletions crates/bevy_gizmos/src/arrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,24 @@ use bevy_math::{Quat, Vec2, Vec3};
use bevy_transform::TransformPoint;

/// A builder returned by [`Gizmos::arrow`] and [`Gizmos::arrow_2d`]
pub struct ArrowBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
gizmos: &'a mut Gizmos<'w, 's, T>,
pub struct ArrowBuilder<'a, 'w, 's, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
start: Vec3,
end: Vec3,
color: Color,
double_ended: bool,
tip_length: f32,
}

impl<T: GizmoConfigGroup> ArrowBuilder<'_, '_, '_, T> {
impl<Config, Clear> ArrowBuilder<'_, '_, '_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// Change the length of the tips to be `length`.
/// The default tip length is [length of the arrow]/10.
///
Expand Down Expand Up @@ -51,7 +59,11 @@ impl<T: GizmoConfigGroup> ArrowBuilder<'_, '_, '_, T> {
}
}

impl<T: GizmoConfigGroup> Drop for ArrowBuilder<'_, '_, '_, T> {
impl<Config, Clear> Drop for ArrowBuilder<'_, '_, '_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// Draws the arrow, by drawing lines with the stored [`Gizmos`]
fn drop(&mut self) {
if !self.gizmos.enabled {
Expand Down Expand Up @@ -90,7 +102,11 @@ impl<T: GizmoConfigGroup> Drop for ArrowBuilder<'_, '_, '_, T> {
}
}

impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// 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 All @@ -111,7 +127,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
start: Vec3,
end: Vec3,
color: impl Into<Color>,
) -> ArrowBuilder<'_, 'w, 's, T> {
) -> ArrowBuilder<'_, 'w, 's, Config, Clear> {
let length = (end - start).length();
ArrowBuilder {
gizmos: self,
Expand Down Expand Up @@ -143,12 +159,16 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
start: Vec2,
end: Vec2,
color: impl Into<Color>,
) -> ArrowBuilder<'_, 'w, 's, T> {
) -> ArrowBuilder<'_, 'w, 's, Config, Clear> {
self.arrow(start.extend(0.), end.extend(0.), color)
}
}

impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// Draw a set of axes local to the given transform (`transform`), with length scaled by a factor
/// of `base_length`.
///
Expand Down
55 changes: 42 additions & 13 deletions crates/bevy_gizmos/src/circles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ fn ellipse_inner(half_size: Vec2, segments: usize) -> impl Iterator<Item = Vec2>
})
}

impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// 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.
Expand Down Expand Up @@ -48,7 +52,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
rotation: Quat,
half_size: Vec2,
color: impl Into<Color>,
) -> EllipseBuilder<'_, 'w, 's, T> {
) -> EllipseBuilder<'_, 'w, 's, Config, Clear> {
EllipseBuilder {
gizmos: self,
position,
Expand Down Expand Up @@ -87,7 +91,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
angle: f32,
half_size: Vec2,
color: impl Into<Color>,
) -> Ellipse2dBuilder<'_, 'w, 's, T> {
) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> {
Ellipse2dBuilder {
gizmos: self,
position,
Expand Down Expand Up @@ -126,7 +130,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
normal: Dir3,
radius: f32,
color: impl Into<Color>,
) -> EllipseBuilder<'_, 'w, 's, T> {
) -> EllipseBuilder<'_, 'w, 's, Config, Clear> {
EllipseBuilder {
gizmos: self,
position,
Expand Down Expand Up @@ -164,7 +168,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
position: Vec2,
radius: f32,
color: impl Into<Color>,
) -> Ellipse2dBuilder<'_, 'w, 's, T> {
) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> {
Ellipse2dBuilder {
gizmos: self,
position,
Expand All @@ -177,24 +181,36 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
}

/// A builder returned by [`Gizmos::ellipse`].
pub struct EllipseBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
gizmos: &'a mut Gizmos<'w, 's, T>,
pub struct EllipseBuilder<'a, 'w, 's, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
position: Vec3,
rotation: Quat,
half_size: Vec2,
color: Color,
segments: usize,
}

impl<T: GizmoConfigGroup> EllipseBuilder<'_, '_, '_, T> {
impl<Config, Clear> EllipseBuilder<'_, '_, '_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// 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 EllipseBuilder<'_, '_, '_, T> {
impl<Config, Clear> Drop for EllipseBuilder<'_, '_, '_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
fn drop(&mut self) {
if !self.gizmos.enabled {
return;
Expand All @@ -208,24 +224,37 @@ impl<T: GizmoConfigGroup> Drop for EllipseBuilder<'_, '_, '_, T> {
}

/// A builder returned by [`Gizmos::ellipse_2d`].
pub struct Ellipse2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
gizmos: &'a mut Gizmos<'w, 's, T>,
pub struct Ellipse2dBuilder<'a, 'w, 's, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
position: Vec2,
rotation: Mat2,
half_size: Vec2,
color: Color,
segments: usize,
}

impl<T: GizmoConfigGroup> Ellipse2dBuilder<'_, '_, '_, T> {
impl<Config, Clear> Ellipse2dBuilder<'_, '_, '_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// 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 Ellipse2dBuilder<'_, '_, '_, T> {
impl<Config, Clear> Drop for Ellipse2dBuilder<'_, '_, '_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// Set the number of line-segments for this ellipse.
fn drop(&mut self) {
if !self.gizmos.enabled {
return;
Expand Down
Loading

0 comments on commit b1ab036

Please sign in to comment.