bevy_gizmos/gizmos.rs
1//! A module for the [`Gizmos`] [`SystemParam`].
2
3use core::{
4 iter,
5 marker::PhantomData,
6 mem,
7 ops::{Deref, DerefMut},
8};
9
10use bevy_color::{Color, LinearRgba};
11use bevy_ecs::{
12 change_detection::Tick,
13 query::FilteredAccessSet,
14 resource::Resource,
15 system::{
16 Deferred, ReadOnlySystemParam, Res, SystemBuffer, SystemMeta, SystemParam,
17 SystemParamValidationError,
18 },
19 world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
20};
21use bevy_math::{bounding::Aabb3d, Isometry2d, Isometry3d, Vec2, Vec3};
22use bevy_reflect::{std_traits::ReflectDefault, Reflect};
23use bevy_transform::TransformPoint;
24use bevy_utils::default;
25
26use crate::{
27 config::{DefaultGizmoConfigGroup, GizmoConfigGroup, GizmoConfigStore},
28 prelude::GizmoConfig,
29};
30
31/// Storage of gizmo primitives.
32#[derive(Resource)]
33pub struct GizmoStorage<Config, Clear> {
34 pub(crate) list_positions: Vec<Vec3>,
35 pub(crate) list_colors: Vec<LinearRgba>,
36 pub(crate) strip_positions: Vec<Vec3>,
37 pub(crate) strip_colors: Vec<LinearRgba>,
38 marker: PhantomData<(Config, Clear)>,
39}
40
41impl<Config, Clear> Default for GizmoStorage<Config, Clear> {
42 fn default() -> Self {
43 Self {
44 list_positions: default(),
45 list_colors: default(),
46 strip_positions: default(),
47 strip_colors: default(),
48 marker: PhantomData,
49 }
50 }
51}
52
53impl<Config, Clear> GizmoStorage<Config, Clear>
54where
55 Config: GizmoConfigGroup,
56 Clear: 'static + Send + Sync,
57{
58 /// Combine the other gizmo storage with this one.
59 pub fn append_storage<OtherConfig, OtherClear>(
60 &mut self,
61 other: &GizmoStorage<OtherConfig, OtherClear>,
62 ) {
63 self.list_positions.extend(other.list_positions.iter());
64 self.list_colors.extend(other.list_colors.iter());
65 self.strip_positions.extend(other.strip_positions.iter());
66 self.strip_colors.extend(other.strip_colors.iter());
67 }
68
69 pub(crate) fn swap<OtherConfig, OtherClear>(
70 &mut self,
71 other: &mut GizmoStorage<OtherConfig, OtherClear>,
72 ) {
73 mem::swap(&mut self.list_positions, &mut other.list_positions);
74 mem::swap(&mut self.list_colors, &mut other.list_colors);
75 mem::swap(&mut self.strip_positions, &mut other.strip_positions);
76 mem::swap(&mut self.strip_colors, &mut other.strip_colors);
77 }
78
79 /// Clear this gizmo storage of any requested gizmos.
80 pub fn clear(&mut self) {
81 self.list_positions.clear();
82 self.list_colors.clear();
83 self.strip_positions.clear();
84 self.strip_colors.clear();
85 }
86}
87
88/// Swap buffer for a specific clearing context.
89///
90/// This is to stash/store the default/requested gizmos so another context can
91/// be substituted for that duration.
92pub struct Swap<Clear>(PhantomData<Clear>);
93
94/// A [`SystemParam`] for drawing gizmos.
95///
96/// They are drawn in immediate mode, which means they will be rendered only for
97/// the frames, or ticks when in [`FixedMain`](bevy_app::FixedMain), in which
98/// they are spawned.
99///
100/// A system in [`Main`](bevy_app::Main) will be cleared each rendering
101/// frame, while a system in [`FixedMain`](bevy_app::FixedMain) will be
102/// cleared each time the [`RunFixedMainLoop`](bevy_app::RunFixedMainLoop)
103/// schedule is run.
104///
105/// Gizmos should be spawned before the [`Last`](bevy_app::Last) schedule
106/// to ensure they are drawn.
107///
108/// To set up your own clearing context (useful for custom scheduling similar
109/// to [`FixedMain`](bevy_app::FixedMain)):
110///
111/// ```
112/// use bevy_gizmos::{prelude::*, *, gizmos::GizmoStorage};
113/// # use bevy_app::prelude::*;
114/// # use bevy_ecs::{schedule::ScheduleLabel, prelude::*};
115/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
116/// # struct StartOfMyContext;
117/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
118/// # struct EndOfMyContext;
119/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
120/// # struct StartOfRun;
121/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
122/// # struct EndOfRun;
123/// # struct MyContext;
124/// struct ClearContextSetup;
125/// impl Plugin for ClearContextSetup {
126/// fn build(&self, app: &mut App) {
127/// app.init_resource::<GizmoStorage<DefaultGizmoConfigGroup, MyContext>>()
128/// // Make sure this context starts/ends cleanly if inside another context. E.g. it
129/// // should start after the parent context starts and end after the parent context ends.
130/// .add_systems(StartOfMyContext, start_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)
131/// // If not running multiple times, put this with [`start_gizmo_context`].
132/// .add_systems(StartOfRun, clear_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)
133/// // If not running multiple times, put this with [`end_gizmo_context`].
134/// .add_systems(EndOfRun, collect_requested_gizmos::<DefaultGizmoConfigGroup, MyContext>)
135/// .add_systems(EndOfMyContext, end_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)
136/// .add_systems(
137/// Last,
138/// propagate_gizmos::<DefaultGizmoConfigGroup, MyContext>.before(GizmoMeshSystems),
139/// );
140/// }
141/// }
142/// ```
143pub struct Gizmos<'w, 's, Config = DefaultGizmoConfigGroup, Clear = ()>
144where
145 Config: GizmoConfigGroup,
146 Clear: 'static + Send + Sync,
147{
148 buffer: Deferred<'s, GizmoBuffer<Config, Clear>>,
149 /// The currently used [`GizmoConfig`]
150 pub config: &'w GizmoConfig,
151 /// The currently used [`GizmoConfigGroup`]
152 pub config_ext: &'w Config,
153}
154
155impl<'w, 's, Config, Clear> Deref for Gizmos<'w, 's, Config, Clear>
156where
157 Config: GizmoConfigGroup,
158 Clear: 'static + Send + Sync,
159{
160 type Target = GizmoBuffer<Config, Clear>;
161
162 fn deref(&self) -> &Self::Target {
163 &self.buffer
164 }
165}
166
167impl<'w, 's, Config, Clear> DerefMut for Gizmos<'w, 's, Config, Clear>
168where
169 Config: GizmoConfigGroup,
170 Clear: 'static + Send + Sync,
171{
172 fn deref_mut(&mut self) -> &mut Self::Target {
173 &mut self.buffer
174 }
175}
176
177type GizmosState<Config, Clear> = (
178 Deferred<'static, GizmoBuffer<Config, Clear>>,
179 Res<'static, GizmoConfigStore>,
180);
181#[doc(hidden)]
182pub struct GizmosFetchState<Config, Clear>
183where
184 Config: GizmoConfigGroup,
185 Clear: 'static + Send + Sync,
186{
187 state: <GizmosState<Config, Clear> as SystemParam>::State,
188}
189
190#[expect(
191 unsafe_code,
192 reason = "We cannot implement SystemParam without using unsafe code."
193)]
194// SAFETY: All methods are delegated to existing `SystemParam` implementations
195unsafe impl<Config, Clear> SystemParam for Gizmos<'_, '_, Config, Clear>
196where
197 Config: GizmoConfigGroup,
198 Clear: 'static + Send + Sync,
199{
200 type State = GizmosFetchState<Config, Clear>;
201 type Item<'w, 's> = Gizmos<'w, 's, Config, Clear>;
202
203 fn init_state(world: &mut World) -> Self::State {
204 GizmosFetchState {
205 state: GizmosState::<Config, Clear>::init_state(world),
206 }
207 }
208
209 fn init_access(
210 state: &Self::State,
211 system_meta: &mut SystemMeta,
212 component_access_set: &mut FilteredAccessSet,
213 world: &mut World,
214 ) {
215 GizmosState::<Config, Clear>::init_access(
216 &state.state,
217 system_meta,
218 component_access_set,
219 world,
220 );
221 }
222
223 fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
224 GizmosState::<Config, Clear>::apply(&mut state.state, system_meta, world);
225 }
226
227 fn queue(state: &mut Self::State, system_meta: &SystemMeta, world: DeferredWorld) {
228 GizmosState::<Config, Clear>::queue(&mut state.state, system_meta, world);
229 }
230
231 #[inline]
232 unsafe fn get_param<'w, 's>(
233 state: &'s mut Self::State,
234 system_meta: &SystemMeta,
235 world: UnsafeWorldCell<'w>,
236 change_tick: Tick,
237 ) -> Result<Self::Item<'w, 's>, SystemParamValidationError> {
238 // SAFETY: Delegated to existing `SystemParam` implementations.
239 let (mut f0, f1) = unsafe {
240 GizmosState::<Config, Clear>::get_param(
241 &mut state.state,
242 system_meta,
243 world,
244 change_tick,
245 )?
246 };
247
248 // Accessing the GizmoConfigStore in every API call reduces performance significantly.
249 // Implementing SystemParam manually allows us to cache whether the config is currently enabled.
250 // Having this available allows for cheap early returns when gizmos are disabled.
251 //
252 // We use `get_config` instead of `config` to accommodate `Option<Gizmos>`:
253 // the user may decide not to initialize a gizmo group, so its config will not exist.
254 let (config, config_ext) = f1.into_inner().get_config::<Config>().ok_or_else(|| {
255 SystemParamValidationError::invalid::<Self>(
256 format!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?",
257 Config::type_path()))
258 })?;
259 f0.enabled = config.enabled;
260
261 Ok(Gizmos {
262 buffer: f0,
263 config,
264 config_ext,
265 })
266 }
267}
268
269#[expect(
270 unsafe_code,
271 reason = "We cannot implement ReadOnlySystemParam without using unsafe code."
272)]
273// Safety: Each field is `ReadOnlySystemParam`, and Gizmos SystemParam does not mutate world
274unsafe impl<'w, 's, Config, Clear> ReadOnlySystemParam for Gizmos<'w, 's, Config, Clear>
275where
276 Config: GizmoConfigGroup,
277 Clear: 'static + Send + Sync,
278 Deferred<'s, GizmoBuffer<Config, Clear>>: ReadOnlySystemParam,
279 Res<'w, GizmoConfigStore>: ReadOnlySystemParam,
280{
281}
282
283/// Buffer for gizmo vertex data.
284#[derive(Debug, Clone, Reflect)]
285#[reflect(Default)]
286pub struct GizmoBuffer<Config, Clear>
287where
288 Config: GizmoConfigGroup,
289 Clear: 'static + Send + Sync,
290{
291 pub(crate) enabled: bool,
292 /// The positions of line segment endpoints.
293 pub list_positions: Vec<Vec3>,
294 /// The colors of line segment endpoints.
295 pub list_colors: Vec<LinearRgba>,
296 /// The positions of line strip vertices.
297 pub strip_positions: Vec<Vec3>,
298 /// The colors of line strip vertices.
299 pub strip_colors: Vec<LinearRgba>,
300 #[reflect(ignore, clone)]
301 pub(crate) marker: PhantomData<(Config, Clear)>,
302}
303
304impl<Config, Clear> Default for GizmoBuffer<Config, Clear>
305where
306 Config: GizmoConfigGroup,
307 Clear: 'static + Send + Sync,
308{
309 fn default() -> Self {
310 GizmoBuffer::new()
311 }
312}
313
314impl<Config, Clear> GizmoBuffer<Config, Clear>
315where
316 Config: GizmoConfigGroup,
317 Clear: 'static + Send + Sync,
318{
319 /// Constructs an empty `GizmoBuffer`.
320 pub const fn new() -> Self {
321 GizmoBuffer {
322 enabled: true,
323 list_positions: Vec::new(),
324 list_colors: Vec::new(),
325 strip_positions: Vec::new(),
326 strip_colors: Vec::new(),
327 marker: PhantomData,
328 }
329 }
330}
331
332/// Read-only view into [`GizmoBuffer`] data.
333pub struct GizmoBufferView<'a> {
334 /// Vertex positions for line-list topology.
335 pub list_positions: &'a Vec<Vec3>,
336 /// Vertex colors for line-list topology.
337 pub list_colors: &'a Vec<LinearRgba>,
338 /// Vertex positions for line-strip topology.
339 pub strip_positions: &'a Vec<Vec3>,
340 /// Vertex colors for line-strip topology.
341 pub strip_colors: &'a Vec<LinearRgba>,
342}
343
344impl<Config, Clear> SystemBuffer for GizmoBuffer<Config, Clear>
345where
346 Config: GizmoConfigGroup,
347 Clear: 'static + Send + Sync,
348{
349 fn queue(&mut self, _system_meta: &SystemMeta, mut world: DeferredWorld) {
350 if let Some(mut storage) = world.get_resource_mut::<GizmoStorage<Config, Clear>>() {
351 storage.list_positions.append(&mut self.list_positions);
352 storage.list_colors.append(&mut self.list_colors);
353 storage.strip_positions.append(&mut self.strip_positions);
354 storage.strip_colors.append(&mut self.strip_colors);
355 } else {
356 // Prevent the buffer from growing indefinitely if GizmoStorage
357 // for the config group has not been initialized
358 self.list_positions.clear();
359 self.list_colors.clear();
360 self.strip_positions.clear();
361 self.strip_colors.clear();
362 }
363 }
364}
365
366impl<Config, Clear> GizmoBuffer<Config, Clear>
367where
368 Config: GizmoConfigGroup,
369 Clear: 'static + Send + Sync,
370{
371 /// Clear all data.
372 pub fn clear(&mut self) {
373 self.list_positions.clear();
374 self.list_colors.clear();
375 self.strip_positions.clear();
376 self.strip_colors.clear();
377 }
378
379 /// Read-only view into the buffers data.
380 pub fn buffer(&self) -> GizmoBufferView<'_> {
381 let GizmoBuffer {
382 list_positions,
383 list_colors,
384 strip_positions,
385 strip_colors,
386 ..
387 } = self;
388 GizmoBufferView {
389 list_positions,
390 list_colors,
391 strip_positions,
392 strip_colors,
393 }
394 }
395 /// Draw a line in 3D from `start` to `end`.
396 ///
397 /// # Example
398 /// ```
399 /// # use bevy_gizmos::prelude::*;
400 /// # use bevy_math::prelude::*;
401 /// # use bevy_color::palettes::basic::GREEN;
402 /// fn system(mut gizmos: Gizmos) {
403 /// gizmos.line(Vec3::ZERO, Vec3::X, GREEN);
404 /// }
405 /// # bevy_ecs::system::assert_is_system(system);
406 /// ```
407 #[inline]
408 pub fn line(&mut self, start: Vec3, end: Vec3, color: impl Into<Color>) {
409 if !self.enabled {
410 return;
411 }
412 self.extend_list_positions([start, end]);
413 self.add_list_color(color, 2);
414 }
415
416 /// Draw a line in 3D with a color gradient from `start` to `end`.
417 ///
418 /// # Example
419 /// ```
420 /// # use bevy_gizmos::prelude::*;
421 /// # use bevy_math::prelude::*;
422 /// # use bevy_color::palettes::basic::{RED, GREEN};
423 /// fn system(mut gizmos: Gizmos) {
424 /// gizmos.line_gradient(Vec3::ZERO, Vec3::X, GREEN, RED);
425 /// }
426 /// # bevy_ecs::system::assert_is_system(system);
427 /// ```
428 #[inline]
429 pub fn line_gradient<C: Into<Color>>(
430 &mut self,
431 start: Vec3,
432 end: Vec3,
433 start_color: C,
434 end_color: C,
435 ) {
436 if !self.enabled {
437 return;
438 }
439 self.extend_list_positions([start, end]);
440 self.extend_list_colors([start_color, end_color]);
441 }
442
443 /// Draw a line in 3D from `start` to `start + vector`.
444 ///
445 /// # Example
446 /// ```
447 /// # use bevy_gizmos::prelude::*;
448 /// # use bevy_math::prelude::*;
449 /// # use bevy_color::palettes::basic::GREEN;
450 /// fn system(mut gizmos: Gizmos) {
451 /// gizmos.ray(Vec3::Y, Vec3::X, GREEN);
452 /// }
453 /// # bevy_ecs::system::assert_is_system(system);
454 /// ```
455 #[inline]
456 pub fn ray(&mut self, start: Vec3, vector: Vec3, color: impl Into<Color>) {
457 if !self.enabled {
458 return;
459 }
460 self.line(start, start + vector, color);
461 }
462
463 /// Draw a line in 3D with a color gradient from `start` to `start + vector`.
464 ///
465 /// # Example
466 /// ```
467 /// # use bevy_gizmos::prelude::*;
468 /// # use bevy_math::prelude::*;
469 /// # use bevy_color::palettes::basic::{RED, GREEN};
470 /// fn system(mut gizmos: Gizmos) {
471 /// gizmos.ray_gradient(Vec3::Y, Vec3::X, GREEN, RED);
472 /// }
473 /// # bevy_ecs::system::assert_is_system(system);
474 /// ```
475 #[inline]
476 pub fn ray_gradient<C: Into<Color>>(
477 &mut self,
478 start: Vec3,
479 vector: Vec3,
480 start_color: C,
481 end_color: C,
482 ) {
483 if !self.enabled {
484 return;
485 }
486 self.line_gradient(start, start + vector, start_color, end_color);
487 }
488
489 /// Draw a line in 3D made of straight segments between the points.
490 ///
491 /// # Example
492 /// ```
493 /// # use bevy_gizmos::prelude::*;
494 /// # use bevy_math::prelude::*;
495 /// # use bevy_color::palettes::basic::GREEN;
496 /// fn system(mut gizmos: Gizmos) {
497 /// gizmos.linestrip([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);
498 /// }
499 /// # bevy_ecs::system::assert_is_system(system);
500 /// ```
501 #[inline]
502 pub fn linestrip(
503 &mut self,
504 positions: impl IntoIterator<Item = Vec3>,
505 color: impl Into<Color>,
506 ) {
507 if !self.enabled {
508 return;
509 }
510 self.extend_strip_positions(positions);
511 let len = self.strip_positions.len();
512 let linear_color = LinearRgba::from(color.into());
513 self.strip_colors.resize(len - 1, linear_color);
514 self.strip_colors.push(LinearRgba::NAN);
515 }
516
517 /// Draw a line in 3D made of straight segments between the points, with the first and last connected.
518 ///
519 /// # Example
520 /// ```
521 /// # use bevy_gizmos::prelude::*;
522 /// # use bevy_math::prelude::*;
523 /// # use bevy_color::palettes::basic::GREEN;
524 /// fn system(mut gizmos: Gizmos) {
525 /// gizmos.lineloop([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);
526 /// }
527 /// # bevy_ecs::system::assert_is_system(system);
528 /// ```
529 #[inline]
530 pub fn lineloop(&mut self, positions: impl IntoIterator<Item = Vec3>, color: impl Into<Color>) {
531 if !self.enabled {
532 return;
533 }
534
535 // Loop back to the start; second is needed to ensure that
536 // the joint on the first corner is drawn.
537 let mut positions = positions.into_iter();
538 let first = positions.next();
539 let second = positions.next();
540
541 self.linestrip(
542 first
543 .into_iter()
544 .chain(second)
545 .chain(positions)
546 .chain(first)
547 .chain(second),
548 color,
549 );
550 }
551
552 /// Draw a line in 3D made of straight segments between the points, with a color gradient.
553 ///
554 /// # Example
555 /// ```
556 /// # use bevy_gizmos::prelude::*;
557 /// # use bevy_math::prelude::*;
558 /// # use bevy_color::palettes::basic::{BLUE, GREEN, RED};
559 /// fn system(mut gizmos: Gizmos) {
560 /// gizmos.linestrip_gradient([
561 /// (Vec3::ZERO, GREEN),
562 /// (Vec3::X, RED),
563 /// (Vec3::Y, BLUE)
564 /// ]);
565 /// }
566 /// # bevy_ecs::system::assert_is_system(system);
567 /// ```
568 #[inline]
569 pub fn linestrip_gradient<C: Into<Color>>(
570 &mut self,
571 points: impl IntoIterator<Item = (Vec3, C)>,
572 ) {
573 if !self.enabled {
574 return;
575 }
576 let points = points.into_iter();
577
578 let GizmoBuffer {
579 strip_positions,
580 strip_colors,
581 ..
582 } = self;
583
584 let (min, _) = points.size_hint();
585 strip_positions.reserve(min);
586 strip_colors.reserve(min);
587
588 for (position, color) in points {
589 strip_positions.push(position);
590 strip_colors.push(LinearRgba::from(color.into()));
591 }
592
593 strip_positions.push(Vec3::NAN);
594 strip_colors.push(LinearRgba::NAN);
595 }
596
597 /// Draw a wireframe rectangle in 3D with the given `isometry` applied.
598 ///
599 /// If `isometry == Isometry3d::IDENTITY` then
600 ///
601 /// - the center is at `Vec3::ZERO`
602 /// - the sizes are aligned with the `Vec3::X` and `Vec3::Y` axes.
603 ///
604 /// # Example
605 /// ```
606 /// # use bevy_gizmos::prelude::*;
607 /// # use bevy_math::prelude::*;
608 /// # use bevy_color::palettes::basic::GREEN;
609 /// fn system(mut gizmos: Gizmos) {
610 /// gizmos.rect(Isometry3d::IDENTITY, Vec2::ONE, GREEN);
611 /// }
612 /// # bevy_ecs::system::assert_is_system(system);
613 /// ```
614 #[inline]
615 pub fn rect(&mut self, isometry: impl Into<Isometry3d>, size: Vec2, color: impl Into<Color>) {
616 if !self.enabled {
617 return;
618 }
619 let isometry = isometry.into();
620 let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2.extend(0.));
621 self.lineloop([tl, tr, br, bl], color);
622 }
623
624 /// Draw a wireframe cube in 3D.
625 ///
626 /// # Example
627 /// ```
628 /// # use bevy_gizmos::prelude::*;
629 /// # use bevy_transform::prelude::*;
630 /// # use bevy_color::palettes::basic::GREEN;
631 /// fn system(mut gizmos: Gizmos) {
632 /// gizmos.cube(Transform::IDENTITY, GREEN);
633 /// }
634 /// # bevy_ecs::system::assert_is_system(system);
635 /// ```
636 #[inline]
637 pub fn cube(&mut self, transform: impl TransformPoint, color: impl Into<Color>) {
638 let polymorphic_color: Color = color.into();
639 if !self.enabled {
640 return;
641 }
642 let rect = rect_inner(Vec2::ONE);
643 // Front
644 let [tlf, trf, brf, blf] = rect.map(|vec2| transform.transform_point(vec2.extend(0.5)));
645 // Back
646 let [tlb, trb, brb, blb] = rect.map(|vec2| transform.transform_point(vec2.extend(-0.5)));
647
648 let strip_positions = [
649 tlf, trf, brf, blf, tlf, // Front
650 tlb, trb, brb, blb, tlb, // Back
651 ];
652 self.linestrip(strip_positions, polymorphic_color);
653
654 let list_positions = [
655 trf, trb, brf, brb, blf, blb, // Front to back
656 ];
657 self.extend_list_positions(list_positions);
658
659 self.add_list_color(polymorphic_color, 6);
660 }
661
662 /// Draw a wireframe aabb in 3D.
663 ///
664 /// # Example
665 /// ```
666 /// # use bevy_gizmos::prelude::*;
667 /// # use bevy_transform::prelude::*;
668 /// # use bevy_math::{bounding::Aabb3d, Vec3};
669 /// # use bevy_color::palettes::basic::GREEN;
670 /// fn system(mut gizmos: Gizmos) {
671 /// gizmos.aabb_3d(Aabb3d::new(Vec3::ZERO, Vec3::ONE), Transform::IDENTITY, GREEN);
672 /// }
673 /// # bevy_ecs::system::assert_is_system(system);
674 /// ```
675 #[inline]
676 pub fn aabb_3d(
677 &mut self,
678 aabb: impl Into<Aabb3d>,
679 transform: impl TransformPoint,
680 color: impl Into<Color>,
681 ) {
682 let polymorphic_color: Color = color.into();
683 if !self.enabled {
684 return;
685 }
686 let aabb = aabb.into();
687 let [tlf, trf, brf, blf, tlb, trb, brb, blb] = [
688 Vec3::new(aabb.min.x, aabb.max.y, aabb.max.z),
689 Vec3::new(aabb.max.x, aabb.max.y, aabb.max.z),
690 Vec3::new(aabb.max.x, aabb.min.y, aabb.max.z),
691 Vec3::new(aabb.min.x, aabb.min.y, aabb.max.z),
692 Vec3::new(aabb.min.x, aabb.max.y, aabb.min.z),
693 Vec3::new(aabb.max.x, aabb.max.y, aabb.min.z),
694 Vec3::new(aabb.max.x, aabb.min.y, aabb.min.z),
695 Vec3::new(aabb.min.x, aabb.min.y, aabb.min.z),
696 ]
697 .map(|v| transform.transform_point(v));
698
699 let strip_positions = [
700 tlf, trf, brf, blf, tlf, // Front
701 tlb, trb, brb, blb, tlb, // Back
702 ];
703 self.linestrip(strip_positions, polymorphic_color);
704
705 let list_positions = [
706 trf, trb, brf, brb, blf, blb, // Front to back
707 ];
708 self.extend_list_positions(list_positions);
709
710 self.add_list_color(polymorphic_color, 6);
711 }
712
713 /// Draw a line in 2D from `start` to `end`.
714 ///
715 /// # Example
716 /// ```
717 /// # use bevy_gizmos::prelude::*;
718 /// # use bevy_math::prelude::*;
719 /// # use bevy_color::palettes::basic::GREEN;
720 /// fn system(mut gizmos: Gizmos) {
721 /// gizmos.line_2d(Vec2::ZERO, Vec2::X, GREEN);
722 /// }
723 /// # bevy_ecs::system::assert_is_system(system);
724 /// ```
725 #[inline]
726 pub fn line_2d(&mut self, start: Vec2, end: Vec2, color: impl Into<Color>) {
727 if !self.enabled {
728 return;
729 }
730 self.line(start.extend(0.), end.extend(0.), color);
731 }
732
733 /// Draw a line in 2D with a color gradient from `start` to `end`.
734 ///
735 /// # Example
736 /// ```
737 /// # use bevy_gizmos::prelude::*;
738 /// # use bevy_math::prelude::*;
739 /// # use bevy_color::palettes::basic::{RED, GREEN};
740 /// fn system(mut gizmos: Gizmos) {
741 /// gizmos.line_gradient_2d(Vec2::ZERO, Vec2::X, GREEN, RED);
742 /// }
743 /// # bevy_ecs::system::assert_is_system(system);
744 /// ```
745 #[inline]
746 pub fn line_gradient_2d<C: Into<Color>>(
747 &mut self,
748 start: Vec2,
749 end: Vec2,
750 start_color: C,
751 end_color: C,
752 ) {
753 if !self.enabled {
754 return;
755 }
756 self.line_gradient(start.extend(0.), end.extend(0.), start_color, end_color);
757 }
758
759 /// Draw a line in 2D made of straight segments between the points.
760 ///
761 /// # Example
762 /// ```
763 /// # use bevy_gizmos::prelude::*;
764 /// # use bevy_math::prelude::*;
765 /// # use bevy_color::palettes::basic::GREEN;
766 /// fn system(mut gizmos: Gizmos) {
767 /// gizmos.linestrip_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);
768 /// }
769 /// # bevy_ecs::system::assert_is_system(system);
770 /// ```
771 #[inline]
772 pub fn linestrip_2d(
773 &mut self,
774 positions: impl IntoIterator<Item = Vec2>,
775 color: impl Into<Color>,
776 ) {
777 if !self.enabled {
778 return;
779 }
780 self.linestrip(positions.into_iter().map(|vec2| vec2.extend(0.)), color);
781 }
782
783 /// Draw a line in 2D made of straight segments between the points, with the first and last connected.
784 ///
785 /// # Example
786 /// ```
787 /// # use bevy_gizmos::prelude::*;
788 /// # use bevy_math::prelude::*;
789 /// # use bevy_color::palettes::basic::GREEN;
790 /// fn system(mut gizmos: Gizmos) {
791 /// gizmos.lineloop_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);
792 /// }
793 /// # bevy_ecs::system::assert_is_system(system);
794 /// ```
795 #[inline]
796 pub fn lineloop_2d(
797 &mut self,
798 positions: impl IntoIterator<Item = Vec2>,
799 color: impl Into<Color>,
800 ) {
801 if !self.enabled {
802 return;
803 }
804 self.lineloop(positions.into_iter().map(|vec2| vec2.extend(0.)), color);
805 }
806
807 /// Draw a line in 2D made of straight segments between the points, with a color gradient.
808 ///
809 /// # Example
810 /// ```
811 /// # use bevy_gizmos::prelude::*;
812 /// # use bevy_math::prelude::*;
813 /// # use bevy_color::palettes::basic::{RED, GREEN, BLUE};
814 /// fn system(mut gizmos: Gizmos) {
815 /// gizmos.linestrip_gradient_2d([
816 /// (Vec2::ZERO, GREEN),
817 /// (Vec2::X, RED),
818 /// (Vec2::Y, BLUE)
819 /// ]);
820 /// }
821 /// # bevy_ecs::system::assert_is_system(system);
822 /// ```
823 #[inline]
824 pub fn linestrip_gradient_2d<C: Into<Color>>(
825 &mut self,
826 positions: impl IntoIterator<Item = (Vec2, C)>,
827 ) {
828 if !self.enabled {
829 return;
830 }
831 self.linestrip_gradient(
832 positions
833 .into_iter()
834 .map(|(vec2, color)| (vec2.extend(0.), color)),
835 );
836 }
837
838 /// Draw a line in 2D from `start` to `start + vector`.
839 ///
840 /// # Example
841 /// ```
842 /// # use bevy_gizmos::prelude::*;
843 /// # use bevy_math::prelude::*;
844 /// # use bevy_color::palettes::basic::GREEN;
845 /// fn system(mut gizmos: Gizmos) {
846 /// gizmos.ray_2d(Vec2::Y, Vec2::X, GREEN);
847 /// }
848 /// # bevy_ecs::system::assert_is_system(system);
849 /// ```
850 #[inline]
851 pub fn ray_2d(&mut self, start: Vec2, vector: Vec2, color: impl Into<Color>) {
852 if !self.enabled {
853 return;
854 }
855 self.line_2d(start, start + vector, color);
856 }
857
858 /// Draw a line in 2D with a color gradient from `start` to `start + vector`.
859 ///
860 /// # Example
861 /// ```
862 /// # use bevy_gizmos::prelude::*;
863 /// # use bevy_math::prelude::*;
864 /// # use bevy_color::palettes::basic::{RED, GREEN};
865 /// fn system(mut gizmos: Gizmos) {
866 /// gizmos.line_gradient(Vec3::Y, Vec3::X, GREEN, RED);
867 /// }
868 /// # bevy_ecs::system::assert_is_system(system);
869 /// ```
870 #[inline]
871 pub fn ray_gradient_2d<C: Into<Color>>(
872 &mut self,
873 start: Vec2,
874 vector: Vec2,
875 start_color: C,
876 end_color: C,
877 ) {
878 if !self.enabled {
879 return;
880 }
881 self.line_gradient_2d(start, start + vector, start_color, end_color);
882 }
883
884 /// Draw a wireframe rectangle in 2D with the given `isometry` applied.
885 ///
886 /// If `isometry == Isometry2d::IDENTITY` then
887 ///
888 /// - the center is at `Vec2::ZERO`
889 /// - the sizes are aligned with the `Vec2::X` and `Vec2::Y` axes.
890 ///
891 /// # Example
892 /// ```
893 /// # use bevy_gizmos::prelude::*;
894 /// # use bevy_math::prelude::*;
895 /// # use bevy_color::palettes::basic::GREEN;
896 /// fn system(mut gizmos: Gizmos) {
897 /// gizmos.rect_2d(Isometry2d::IDENTITY, Vec2::ONE, GREEN);
898 /// }
899 /// # bevy_ecs::system::assert_is_system(system);
900 /// ```
901 #[inline]
902 pub fn rect_2d(
903 &mut self,
904 isometry: impl Into<Isometry2d>,
905 size: Vec2,
906 color: impl Into<Color>,
907 ) {
908 if !self.enabled {
909 return;
910 }
911 let isometry = isometry.into();
912 let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2);
913 self.lineloop_2d([tl, tr, br, bl], color);
914 }
915
916 #[inline]
917 fn extend_list_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
918 self.list_positions.extend(positions);
919 }
920
921 #[inline]
922 fn extend_list_colors(&mut self, colors: impl IntoIterator<Item = impl Into<Color>>) {
923 self.list_colors.extend(
924 colors
925 .into_iter()
926 .map(|color| LinearRgba::from(color.into())),
927 );
928 }
929
930 #[inline]
931 fn add_list_color(&mut self, color: impl Into<Color>, count: usize) {
932 let polymorphic_color: Color = color.into();
933 let linear_color = LinearRgba::from(polymorphic_color);
934
935 self.list_colors.extend(iter::repeat_n(linear_color, count));
936 }
937
938 #[inline]
939 fn extend_strip_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
940 self.strip_positions.extend(positions);
941 self.strip_positions.push(Vec3::NAN);
942 }
943}
944
945fn rect_inner(size: Vec2) -> [Vec2; 4] {
946 let half_size = size / 2.;
947 let tl = Vec2::new(-half_size.x, half_size.y);
948 let tr = Vec2::new(half_size.x, half_size.y);
949 let bl = Vec2::new(-half_size.x, -half_size.y);
950 let br = Vec2::new(half_size.x, -half_size.y);
951 [tl, tr, br, bl]
952}