Skip to content

Commit dc1d642

Browse files
exposed GizmoBuffer
1 parent 4acb25a commit dc1d642

File tree

3 files changed

+132
-59
lines changed

3 files changed

+132
-59
lines changed

crates/bevy_gizmos/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ webgl = []
1616
bevy_pbr = { path = "../bevy_pbr", version = "0.12.0-dev", optional = true }
1717
bevy_sprite = { path = "../bevy_sprite", version = "0.12.0-dev", optional = true }
1818
bevy_app = { path = "../bevy_app", version = "0.12.0-dev" }
19+
bevy_derive = { path = "../bevy_derive", version = "0.12.0-dev" }
1920
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0-dev" }
2021
bevy_math = { path = "../bevy_math", version = "0.12.0-dev" }
2122
bevy_asset = { path = "../bevy_asset", version = "0.12.0-dev" }

crates/bevy_gizmos/src/gizmos.rs

Lines changed: 86 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::{f32::consts::TAU, iter};
44

55
use bevy_app::FixedUpdateScheduleIsCurrentlyRunning;
6+
use bevy_derive::{Deref, DerefMut};
67
use bevy_ecs::{
78
component::Tick,
89
system::{Resource, SystemMeta, SystemParam},
@@ -34,25 +35,31 @@ pub(crate) struct GizmoStorage {
3435
}
3536

3637
/// A [`SystemParam`](bevy_ecs::system::SystemParam) for drawing gizmos.
38+
#[derive(Deref, DerefMut)]
3739
pub struct Gizmos<'s> {
38-
buffer: &'s mut <Self as SystemParam>::State,
40+
buffer: &'s mut GizmoBuffer,
41+
}
42+
43+
#[derive(Default)]
44+
pub struct GizmoBuffer {
45+
list_positions: Vec<PositionItem>,
46+
list_colors: Vec<ColorItem>,
47+
strip_positions: Vec<PositionItem>,
48+
strip_colors: Vec<ColorItem>,
3949
}
4050

4151
// Wrap to keep GizmoBuffer hidden
4252
const _: () = {
4353
#[derive(Default)]
44-
pub struct GizmoBuffer {
54+
pub struct State {
55+
buffer: GizmoBuffer,
4556
/// Which fixed update tick this belongs to, `None` if this isn't from a fixed update.
4657
fixed_time_update: Option<u64>,
47-
list_positions: Vec<PositionItem>,
48-
list_colors: Vec<ColorItem>,
49-
strip_positions: Vec<PositionItem>,
50-
strip_colors: Vec<ColorItem>,
5158
}
5259

5360
// SAFETY: Only local state is accessed.
5461
unsafe impl SystemParam for Gizmos<'_> {
55-
type State = GizmoBuffer;
62+
type State = State;
5663
type Item<'w, 's> = Gizmos<'s>;
5764

5865
fn init_state(_: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
@@ -76,10 +83,14 @@ const _: () = {
7683
&mut storages.frame
7784
};
7885

79-
storage.list_positions.append(&mut state.list_positions);
80-
storage.list_colors.append(&mut state.list_colors);
81-
storage.strip_positions.append(&mut state.strip_positions);
82-
storage.strip_colors.append(&mut state.strip_colors);
86+
storage
87+
.list_positions
88+
.append(&mut state.buffer.list_positions);
89+
storage.list_colors.append(&mut state.buffer.list_colors);
90+
storage
91+
.strip_positions
92+
.append(&mut state.buffer.strip_positions);
93+
storage.strip_colors.append(&mut state.buffer.strip_colors);
8394
}
8495

8596
unsafe fn get_param<'w, 's>(
@@ -91,12 +102,14 @@ const _: () = {
91102
state.fixed_time_update = world
92103
.get_resource::<FixedUpdateScheduleIsCurrentlyRunning>()
93104
.map(|current| current.update);
94-
Gizmos { buffer: state }
105+
Gizmos {
106+
buffer: &mut state.buffer,
107+
}
95108
}
96109
}
97110
};
98111

99-
impl<'s> Gizmos<'s> {
112+
impl GizmoBuffer {
100113
/// Draw a line from `start` to `end`.
101114
///
102115
/// # Example
@@ -188,11 +201,10 @@ impl<'s> Gizmos<'s> {
188201
#[inline]
189202
pub fn linestrip(&mut self, positions: impl IntoIterator<Item = Vec3>, color: Color) {
190203
self.extend_strip_positions(positions.into_iter());
191-
let len = self.buffer.strip_positions.len();
192-
self.buffer
193-
.strip_colors
204+
let len = self.strip_positions.len();
205+
self.strip_colors
194206
.resize(len - 1, color.as_linear_rgba_f32());
195-
self.buffer.strip_colors.push([f32::NAN; 4]);
207+
self.strip_colors.push([f32::NAN; 4]);
196208
}
197209

198210
/// Draw lines between a list of points with a color gradient.
@@ -215,8 +227,8 @@ impl<'s> Gizmos<'s> {
215227
pub fn linestrip_gradient(&mut self, points: impl IntoIterator<Item = (Vec3, Color)>) {
216228
let points = points.into_iter();
217229

218-
let strip_positions = &mut self.buffer.strip_positions;
219-
let strip_colors = &mut self.buffer.strip_colors;
230+
let strip_positions = &mut self.strip_positions;
231+
let strip_colors = &mut self.strip_colors;
220232

221233
let (min, _) = points.size_hint();
222234
strip_positions.reserve(min);
@@ -256,9 +268,9 @@ impl<'s> Gizmos<'s> {
256268
normal: Vec3,
257269
radius: f32,
258270
color: Color,
259-
) -> CircleBuilder<'_, 's> {
271+
) -> CircleBuilder<'_> {
260272
CircleBuilder {
261-
gizmos: self,
273+
buffer: self,
262274
position,
263275
normal,
264276
radius,
@@ -292,9 +304,9 @@ impl<'s> Gizmos<'s> {
292304
rotation: Quat,
293305
radius: f32,
294306
color: Color,
295-
) -> SphereBuilder<'_, 's> {
307+
) -> SphereBuilder<'_> {
296308
SphereBuilder {
297-
gizmos: self,
309+
buffer: self,
298310
position,
299311
rotation,
300312
radius,
@@ -495,12 +507,7 @@ impl<'s> Gizmos<'s> {
495507
/// # bevy_ecs::system::assert_is_system(system);
496508
/// ```
497509
#[inline]
498-
pub fn circle_2d(
499-
&mut self,
500-
position: Vec2,
501-
radius: f32,
502-
color: Color,
503-
) -> Circle2dBuilder<'_, 's> {
510+
pub fn circle_2d(&mut self, position: Vec2, radius: f32, color: Color) -> Circle2dBuilder<'_> {
504511
Circle2dBuilder {
505512
gizmos: self,
506513
position,
@@ -543,7 +550,7 @@ impl<'s> Gizmos<'s> {
543550
arc_angle: f32,
544551
radius: f32,
545552
color: Color,
546-
) -> Arc2dBuilder<'_, 's> {
553+
) -> Arc2dBuilder<'_> {
547554
Arc2dBuilder {
548555
gizmos: self,
549556
position,
@@ -574,30 +581,50 @@ impl<'s> Gizmos<'s> {
574581
self.linestrip_2d([tl, tr, br, bl, tl], color);
575582
}
576583

584+
/// Draw all gizmos from another buffer.
585+
///
586+
/// # Example
587+
/// ```
588+
/// # use bevy_gizmos::{gizmos::GizmoBuffer, prelude::*};
589+
/// # use bevy_ecs::prelude::*;
590+
/// #[derive(Resource)]
591+
/// struct Buffer(GizmoBuffer);
592+
///
593+
/// fn system(mut gizmos: Gizmos, buffered: Res<Buffer>) {
594+
/// gizmos.submit_buffer(&buffered.0);
595+
/// }
596+
/// # bevy_ecs::system::assert_is_system(system);
597+
/// ```
598+
pub fn submit_buffer(&mut self, gizmos: &Self) {
599+
self.list_positions
600+
.extend_from_slice(&gizmos.list_positions);
601+
self.list_colors.extend_from_slice(&gizmos.list_colors);
602+
self.strip_positions
603+
.extend_from_slice(&gizmos.strip_positions);
604+
self.strip_colors.extend_from_slice(&gizmos.strip_colors);
605+
}
606+
577607
#[inline]
578608
fn extend_list_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
579-
self.buffer
580-
.list_positions
609+
self.list_positions
581610
.extend(positions.into_iter().map(|vec3| vec3.to_array()));
582611
}
583612

584613
#[inline]
585614
fn extend_list_colors(&mut self, colors: impl IntoIterator<Item = Color>) {
586-
self.buffer
587-
.list_colors
615+
self.list_colors
588616
.extend(colors.into_iter().map(|color| color.as_linear_rgba_f32()));
589617
}
590618

591619
#[inline]
592620
fn add_list_color(&mut self, color: Color, count: usize) {
593-
self.buffer
594-
.list_colors
621+
self.list_colors
595622
.extend(iter::repeat(color.as_linear_rgba_f32()).take(count));
596623
}
597624

598625
#[inline]
599626
fn extend_strip_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
600-
self.buffer.strip_positions.extend(
627+
self.strip_positions.extend(
601628
positions
602629
.into_iter()
603630
.map(|vec3| vec3.to_array())
@@ -606,88 +633,88 @@ impl<'s> Gizmos<'s> {
606633
}
607634
}
608635

609-
/// A builder returned by [`Gizmos::circle`].
610-
pub struct CircleBuilder<'a, 's> {
611-
gizmos: &'a mut Gizmos<'s>,
636+
/// A builder returned by [`GizmoBuffer::circle`].
637+
pub struct CircleBuilder<'a> {
638+
buffer: &'a mut GizmoBuffer,
612639
position: Vec3,
613640
normal: Vec3,
614641
radius: f32,
615642
color: Color,
616643
segments: usize,
617644
}
618645

619-
impl CircleBuilder<'_, '_> {
646+
impl CircleBuilder<'_> {
620647
/// Set the number of line-segments for this circle.
621648
pub fn segments(mut self, segments: usize) -> Self {
622649
self.segments = segments;
623650
self
624651
}
625652
}
626653

627-
impl Drop for CircleBuilder<'_, '_> {
654+
impl Drop for CircleBuilder<'_> {
628655
fn drop(&mut self) {
629656
let rotation = Quat::from_rotation_arc(Vec3::Z, self.normal);
630657
let positions = circle_inner(self.radius, self.segments)
631658
.map(|vec2| (self.position + rotation * vec2.extend(0.)));
632-
self.gizmos.linestrip(positions, self.color);
659+
self.buffer.linestrip(positions, self.color);
633660
}
634661
}
635662

636-
/// A builder returned by [`Gizmos::sphere`].
637-
pub struct SphereBuilder<'a, 's> {
638-
gizmos: &'a mut Gizmos<'s>,
663+
/// A builder returned by [`GizmoBuffer::sphere`].
664+
pub struct SphereBuilder<'a> {
665+
buffer: &'a mut GizmoBuffer,
639666
position: Vec3,
640667
rotation: Quat,
641668
radius: f32,
642669
color: Color,
643670
circle_segments: usize,
644671
}
645672

646-
impl SphereBuilder<'_, '_> {
673+
impl SphereBuilder<'_> {
647674
/// Set the number of line-segments per circle for this sphere.
648675
pub fn circle_segments(mut self, segments: usize) -> Self {
649676
self.circle_segments = segments;
650677
self
651678
}
652679
}
653680

654-
impl Drop for SphereBuilder<'_, '_> {
681+
impl Drop for SphereBuilder<'_> {
655682
fn drop(&mut self) {
656683
for axis in Vec3::AXES {
657-
self.gizmos
684+
self.buffer
658685
.circle(self.position, self.rotation * axis, self.radius, self.color)
659686
.segments(self.circle_segments);
660687
}
661688
}
662689
}
663690

664-
/// A builder returned by [`Gizmos::circle_2d`].
665-
pub struct Circle2dBuilder<'a, 's> {
666-
gizmos: &'a mut Gizmos<'s>,
691+
/// A builder returned by [`GizmoBuffer::circle_2d`].
692+
pub struct Circle2dBuilder<'a> {
693+
gizmos: &'a mut GizmoBuffer,
667694
position: Vec2,
668695
radius: f32,
669696
color: Color,
670697
segments: usize,
671698
}
672699

673-
impl Circle2dBuilder<'_, '_> {
700+
impl Circle2dBuilder<'_> {
674701
/// Set the number of line-segments for this circle.
675702
pub fn segments(mut self, segments: usize) -> Self {
676703
self.segments = segments;
677704
self
678705
}
679706
}
680707

681-
impl Drop for Circle2dBuilder<'_, '_> {
708+
impl Drop for Circle2dBuilder<'_> {
682709
fn drop(&mut self) {
683710
let positions = circle_inner(self.radius, self.segments).map(|vec2| (vec2 + self.position));
684711
self.gizmos.linestrip_2d(positions, self.color);
685712
}
686713
}
687714

688-
/// A builder returned by [`Gizmos::arc_2d`].
689-
pub struct Arc2dBuilder<'a, 's> {
690-
gizmos: &'a mut Gizmos<'s>,
715+
/// A builder returned by [`GizmoBuffer::arc_2d`].
716+
pub struct Arc2dBuilder<'a> {
717+
gizmos: &'a mut GizmoBuffer,
691718
position: Vec2,
692719
direction_angle: f32,
693720
arc_angle: f32,
@@ -696,15 +723,15 @@ pub struct Arc2dBuilder<'a, 's> {
696723
segments: Option<usize>,
697724
}
698725

699-
impl Arc2dBuilder<'_, '_> {
726+
impl Arc2dBuilder<'_> {
700727
/// Set the number of line-segments for this arc.
701728
pub fn segments(mut self, segments: usize) -> Self {
702729
self.segments = Some(segments);
703730
self
704731
}
705732
}
706733

707-
impl Drop for Arc2dBuilder<'_, '_> {
734+
impl Drop for Arc2dBuilder<'_> {
708735
fn drop(&mut self) {
709736
let segments = match self.segments {
710737
Some(segments) => segments,

crates/bevy_gizmos/src/lib.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,3 +564,48 @@ fn line_gizmo_vertex_buffer_layouts(strip: bool) -> Vec<VertexBufferLayout> {
564564
vec![position_layout, color_layout]
565565
}
566566
}
567+
// #[cfg(test)]
568+
mod test {
569+
use crate::{gizmos::GizmoStorages, prelude::*, GizmoPlugin};
570+
use bevy_app::{prelude::*, FixedUpdateScheduleIsCurrentlyRunning, ScheduleRunnerPlugin};
571+
use bevy_ecs::prelude::*;
572+
use bevy_math::Vec2;
573+
use bevy_render::{
574+
pipelined_rendering::PipelinedRenderingPlugin, prelude::Color, RenderPlugin,
575+
};
576+
577+
#[test]
578+
fn fixed_update() {
579+
let mut app = App::new();
580+
581+
app.add_plugins((
582+
ScheduleRunnerPlugin::run_once(),
583+
GizmoPlugin,
584+
RenderPlugin::default(),
585+
PipelinedRenderingPlugin,
586+
))
587+
.add_systems(Update, |mut gizmos: Gizmos| {
588+
gizmos.line_2d(Vec2::splat(0.), Vec2::splat(1.), Color::RED)
589+
})
590+
.run();
591+
592+
// // Gizmos have been cleared
593+
// assert!(app
594+
// .world
595+
// .resource::<GizmoStorages>()
596+
// .frame
597+
// .list_positions
598+
// .is_empty());
599+
600+
// // No gizmos have been submitted during fixed update
601+
// assert!(app
602+
// .world
603+
// .resource::<GizmoStorages>()
604+
// .fixed_update
605+
// .list_positions
606+
// .is_empty());
607+
608+
app.world
609+
.insert_resource(FixedUpdateScheduleIsCurrentlyRunning { update: 0 });
610+
}
611+
}

0 commit comments

Comments
 (0)