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 resource::Resource,
14 system::{
15 Deferred, ReadOnlySystemParam, Res, SystemAccess, SystemBuffer, SystemMeta, SystemParam,
16 SystemParamValidationError,
17 },
18 world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
19};
20use bevy_math::{Isometry2d, Isometry3d, Vec2, Vec3};
21use bevy_reflect::{std_traits::ReflectDefault, Reflect};
22use bevy_shape::Aabb3d;
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 system_access: &mut SystemAccess,
213 world: &mut World,
214 ) {
215 GizmosState::<Config, Clear>::init_access(&state.state, system_meta, system_access, world);
216 }
217
218 fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
219 GizmosState::<Config, Clear>::apply(&mut state.state, system_meta, world);
220 }
221
222 fn queue(state: &mut Self::State, system_meta: &SystemMeta, world: DeferredWorld) {
223 GizmosState::<Config, Clear>::queue(&mut state.state, system_meta, world);
224 }
225
226 #[inline]
227 unsafe fn get_param<'w, 's>(
228 state: &'s mut Self::State,
229 system_meta: &SystemMeta,
230 world: UnsafeWorldCell<'w>,
231 change_tick: Tick,
232 ) -> Result<Self::Item<'w, 's>, SystemParamValidationError> {
233 // SAFETY: Delegated to existing `SystemParam` implementations.
234 let (mut f0, f1) = unsafe {
235 GizmosState::<Config, Clear>::get_param(
236 &mut state.state,
237 system_meta,
238 world,
239 change_tick,
240 )?
241 };
242
243 // Accessing the GizmoConfigStore in every API call reduces performance significantly.
244 // Implementing SystemParam manually allows us to cache whether the config is currently enabled.
245 // Having this available allows for cheap early returns when gizmos are disabled.
246 //
247 // We use `get_config` instead of `config` to accommodate `Option<Gizmos>`:
248 // the user may decide not to initialize a gizmo group, so its config will not exist.
249 let (config, config_ext) = f1.into_inner().get_config::<Config>().ok_or_else(|| {
250 SystemParamValidationError::invalid::<Self>(
251 format!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?",
252 Config::type_path()))
253 })?;
254 f0.enabled = config.enabled;
255
256 Ok(Gizmos {
257 buffer: f0,
258 config,
259 config_ext,
260 })
261 }
262}
263
264#[expect(
265 unsafe_code,
266 reason = "We cannot implement ReadOnlySystemParam without using unsafe code."
267)]
268// Safety: Each field is `ReadOnlySystemParam`, and Gizmos SystemParam does not mutate world
269unsafe impl<'w, 's, Config, Clear> ReadOnlySystemParam for Gizmos<'w, 's, Config, Clear>
270where
271 Config: GizmoConfigGroup,
272 Clear: 'static + Send + Sync,
273 Deferred<'s, GizmoBuffer<Config, Clear>>: ReadOnlySystemParam,
274 Res<'w, GizmoConfigStore>: ReadOnlySystemParam,
275{
276}
277
278/// Buffer for gizmo vertex data.
279#[derive(Debug, Clone, Reflect)]
280#[reflect(Default)]
281pub struct GizmoBuffer<Config, Clear>
282where
283 Config: GizmoConfigGroup,
284 Clear: 'static + Send + Sync,
285{
286 pub(crate) enabled: bool,
287 /// The positions of line segment endpoints.
288 pub list_positions: Vec<Vec3>,
289 /// The colors of line segment endpoints.
290 pub list_colors: Vec<LinearRgba>,
291 /// The positions of line strip vertices.
292 pub strip_positions: Vec<Vec3>,
293 /// The colors of line strip vertices.
294 pub strip_colors: Vec<LinearRgba>,
295 #[reflect(ignore, clone)]
296 pub(crate) marker: PhantomData<(Config, Clear)>,
297}
298
299impl<Config, Clear> Default for GizmoBuffer<Config, Clear>
300where
301 Config: GizmoConfigGroup,
302 Clear: 'static + Send + Sync,
303{
304 fn default() -> Self {
305 GizmoBuffer::new()
306 }
307}
308
309impl<Config, Clear> GizmoBuffer<Config, Clear>
310where
311 Config: GizmoConfigGroup,
312 Clear: 'static + Send + Sync,
313{
314 /// Constructs an empty `GizmoBuffer`.
315 pub const fn new() -> Self {
316 GizmoBuffer {
317 enabled: true,
318 list_positions: Vec::new(),
319 list_colors: Vec::new(),
320 strip_positions: Vec::new(),
321 strip_colors: Vec::new(),
322 marker: PhantomData,
323 }
324 }
325}
326
327/// Read-only view into [`GizmoBuffer`] data.
328pub struct GizmoBufferView<'a> {
329 /// Vertex positions for line-list topology.
330 pub list_positions: &'a Vec<Vec3>,
331 /// Vertex colors for line-list topology.
332 pub list_colors: &'a Vec<LinearRgba>,
333 /// Vertex positions for line-strip topology.
334 pub strip_positions: &'a Vec<Vec3>,
335 /// Vertex colors for line-strip topology.
336 pub strip_colors: &'a Vec<LinearRgba>,
337}
338
339impl<Config, Clear> SystemBuffer for GizmoBuffer<Config, Clear>
340where
341 Config: GizmoConfigGroup,
342 Clear: 'static + Send + Sync,
343{
344 fn queue(&mut self, _system_meta: &SystemMeta, mut world: DeferredWorld) {
345 if let Some(mut storage) = world.get_resource_mut::<GizmoStorage<Config, Clear>>() {
346 storage.list_positions.append(&mut self.list_positions);
347 storage.list_colors.append(&mut self.list_colors);
348 storage.strip_positions.append(&mut self.strip_positions);
349 storage.strip_colors.append(&mut self.strip_colors);
350 } else {
351 // Prevent the buffer from growing indefinitely if GizmoStorage
352 // for the config group has not been initialized
353 self.list_positions.clear();
354 self.list_colors.clear();
355 self.strip_positions.clear();
356 self.strip_colors.clear();
357 }
358 }
359}
360
361impl<Config, Clear> GizmoBuffer<Config, Clear>
362where
363 Config: GizmoConfigGroup,
364 Clear: 'static + Send + Sync,
365{
366 /// Clear all data.
367 pub fn clear(&mut self) {
368 self.list_positions.clear();
369 self.list_colors.clear();
370 self.strip_positions.clear();
371 self.strip_colors.clear();
372 }
373
374 /// Read-only view into the buffers data.
375 pub fn buffer(&self) -> GizmoBufferView<'_> {
376 let GizmoBuffer {
377 list_positions,
378 list_colors,
379 strip_positions,
380 strip_colors,
381 ..
382 } = self;
383 GizmoBufferView {
384 list_positions,
385 list_colors,
386 strip_positions,
387 strip_colors,
388 }
389 }
390 /// Draw a line in 3D from `start` to `end`.
391 ///
392 /// # Example
393 /// ```
394 /// # use bevy_gizmos::prelude::*;
395 /// # use bevy_math::prelude::*;
396 /// # use bevy_color::palettes::basic::GREEN;
397 /// fn system(mut gizmos: Gizmos) {
398 /// gizmos.line(Vec3::ZERO, Vec3::X, GREEN);
399 /// }
400 /// # bevy_ecs::system::assert_is_system(system);
401 /// ```
402 #[inline]
403 pub fn line(&mut self, start: Vec3, end: Vec3, color: impl Into<Color>) {
404 if !self.enabled {
405 return;
406 }
407 self.extend_list_positions([start, end]);
408 self.add_list_color(color, 2);
409 }
410
411 /// Draw a line in 3D with a color gradient from `start` to `end`.
412 ///
413 /// # Example
414 /// ```
415 /// # use bevy_gizmos::prelude::*;
416 /// # use bevy_math::prelude::*;
417 /// # use bevy_color::palettes::basic::{RED, GREEN};
418 /// fn system(mut gizmos: Gizmos) {
419 /// gizmos.line_gradient(Vec3::ZERO, Vec3::X, GREEN, RED);
420 /// }
421 /// # bevy_ecs::system::assert_is_system(system);
422 /// ```
423 #[inline]
424 pub fn line_gradient<C: Into<Color>>(
425 &mut self,
426 start: Vec3,
427 end: Vec3,
428 start_color: C,
429 end_color: C,
430 ) {
431 if !self.enabled {
432 return;
433 }
434 self.extend_list_positions([start, end]);
435 self.extend_list_colors([start_color, end_color]);
436 }
437
438 /// Draw a line in 3D from `start` to `start + vector`.
439 ///
440 /// # Example
441 /// ```
442 /// # use bevy_gizmos::prelude::*;
443 /// # use bevy_math::prelude::*;
444 /// # use bevy_color::palettes::basic::GREEN;
445 /// fn system(mut gizmos: Gizmos) {
446 /// gizmos.ray(Vec3::Y, Vec3::X, GREEN);
447 /// }
448 /// # bevy_ecs::system::assert_is_system(system);
449 /// ```
450 #[inline]
451 pub fn ray(&mut self, start: Vec3, vector: Vec3, color: impl Into<Color>) {
452 if !self.enabled {
453 return;
454 }
455 self.line(start, start + vector, color);
456 }
457
458 /// Draw a line in 3D with a color gradient from `start` to `start + vector`.
459 ///
460 /// # Example
461 /// ```
462 /// # use bevy_gizmos::prelude::*;
463 /// # use bevy_math::prelude::*;
464 /// # use bevy_color::palettes::basic::{RED, GREEN};
465 /// fn system(mut gizmos: Gizmos) {
466 /// gizmos.ray_gradient(Vec3::Y, Vec3::X, GREEN, RED);
467 /// }
468 /// # bevy_ecs::system::assert_is_system(system);
469 /// ```
470 #[inline]
471 pub fn ray_gradient<C: Into<Color>>(
472 &mut self,
473 start: Vec3,
474 vector: Vec3,
475 start_color: C,
476 end_color: C,
477 ) {
478 if !self.enabled {
479 return;
480 }
481 self.line_gradient(start, start + vector, start_color, end_color);
482 }
483
484 /// Draw a line in 3D made of straight segments between the points.
485 ///
486 /// # Example
487 /// ```
488 /// # use bevy_gizmos::prelude::*;
489 /// # use bevy_math::prelude::*;
490 /// # use bevy_color::palettes::basic::GREEN;
491 /// fn system(mut gizmos: Gizmos) {
492 /// gizmos.linestrip([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);
493 /// }
494 /// # bevy_ecs::system::assert_is_system(system);
495 /// ```
496 #[inline]
497 pub fn linestrip(
498 &mut self,
499 positions: impl IntoIterator<Item = Vec3>,
500 color: impl Into<Color>,
501 ) {
502 if !self.enabled {
503 return;
504 }
505 self.extend_strip_positions(positions);
506 let len = self.strip_positions.len();
507 let linear_color = LinearRgba::from(color.into());
508 self.strip_colors.resize(len - 1, linear_color);
509 self.strip_colors.push(LinearRgba::NAN);
510 }
511
512 /// Draw a line in 3D made of straight segments between the points, with the first and last connected.
513 ///
514 /// # Example
515 /// ```
516 /// # use bevy_gizmos::prelude::*;
517 /// # use bevy_math::prelude::*;
518 /// # use bevy_color::palettes::basic::GREEN;
519 /// fn system(mut gizmos: Gizmos) {
520 /// gizmos.lineloop([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);
521 /// }
522 /// # bevy_ecs::system::assert_is_system(system);
523 /// ```
524 #[inline]
525 pub fn lineloop(&mut self, positions: impl IntoIterator<Item = Vec3>, color: impl Into<Color>) {
526 if !self.enabled {
527 return;
528 }
529
530 // Loop back to the start; second is needed to ensure that
531 // the joint on the first corner is drawn.
532 let mut positions = positions.into_iter();
533 let first = positions.next();
534 let second = positions.next();
535
536 self.linestrip(
537 first
538 .into_iter()
539 .chain(second)
540 .chain(positions)
541 .chain(first)
542 .chain(second),
543 color,
544 );
545 }
546
547 /// Draw a line in 3D made of straight segments between the points, with a color gradient.
548 ///
549 /// # Example
550 /// ```
551 /// # use bevy_gizmos::prelude::*;
552 /// # use bevy_math::prelude::*;
553 /// # use bevy_color::palettes::basic::{BLUE, GREEN, RED};
554 /// fn system(mut gizmos: Gizmos) {
555 /// gizmos.linestrip_gradient([
556 /// (Vec3::ZERO, GREEN),
557 /// (Vec3::X, RED),
558 /// (Vec3::Y, BLUE)
559 /// ]);
560 /// }
561 /// # bevy_ecs::system::assert_is_system(system);
562 /// ```
563 #[inline]
564 pub fn linestrip_gradient<C: Into<Color>>(
565 &mut self,
566 points: impl IntoIterator<Item = (Vec3, C)>,
567 ) {
568 if !self.enabled {
569 return;
570 }
571 let points = points.into_iter();
572
573 let GizmoBuffer {
574 strip_positions,
575 strip_colors,
576 ..
577 } = self;
578
579 let (min, _) = points.size_hint();
580 strip_positions.reserve(min);
581 strip_colors.reserve(min);
582
583 for (position, color) in points {
584 strip_positions.push(position);
585 strip_colors.push(LinearRgba::from(color.into()));
586 }
587
588 strip_positions.push(Vec3::NAN);
589 strip_colors.push(LinearRgba::NAN);
590 }
591
592 /// Draw a wireframe rectangle in 3D with the given `isometry` applied.
593 ///
594 /// If `isometry == Isometry3d::IDENTITY` then
595 ///
596 /// - the center is at `Vec3::ZERO`
597 /// - the sizes are aligned with the `Vec3::X` and `Vec3::Y` axes.
598 ///
599 /// # Example
600 /// ```
601 /// # use bevy_gizmos::prelude::*;
602 /// # use bevy_math::prelude::*;
603 /// # use bevy_color::palettes::basic::GREEN;
604 /// fn system(mut gizmos: Gizmos) {
605 /// gizmos.rect(Isometry3d::IDENTITY, Vec2::ONE, GREEN);
606 /// }
607 /// # bevy_ecs::system::assert_is_system(system);
608 /// ```
609 #[inline]
610 pub fn rect(&mut self, isometry: impl Into<Isometry3d>, size: Vec2, color: impl Into<Color>) {
611 if !self.enabled {
612 return;
613 }
614 let isometry = isometry.into();
615 let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2.extend(0.));
616 self.lineloop([tl, tr, br, bl], color);
617 }
618
619 /// Draw a wireframe cube in 3D.
620 ///
621 /// # Example
622 /// ```
623 /// # use bevy_gizmos::prelude::*;
624 /// # use bevy_transform::prelude::*;
625 /// # use bevy_color::palettes::basic::GREEN;
626 /// fn system(mut gizmos: Gizmos) {
627 /// gizmos.cube(Transform::IDENTITY, GREEN);
628 /// }
629 /// # bevy_ecs::system::assert_is_system(system);
630 /// ```
631 #[inline]
632 pub fn cube(&mut self, transform: impl TransformPoint, color: impl Into<Color>) {
633 let polymorphic_color: Color = color.into();
634 if !self.enabled {
635 return;
636 }
637 let rect = rect_inner(Vec2::ONE);
638 // Front
639 let [tlf, trf, brf, blf] = rect.map(|vec2| transform.transform_point(vec2.extend(0.5)));
640 // Back
641 let [tlb, trb, brb, blb] = rect.map(|vec2| transform.transform_point(vec2.extend(-0.5)));
642
643 let strip_positions = [
644 tlf, trf, brf, blf, tlf, // Front
645 tlb, trb, brb, blb, tlb, // Back
646 ];
647 self.linestrip(strip_positions, polymorphic_color);
648
649 let list_positions = [
650 trf, trb, brf, brb, blf, blb, // Front to back
651 ];
652 self.extend_list_positions(list_positions);
653
654 self.add_list_color(polymorphic_color, 6);
655 }
656
657 /// Draw a wireframe aabb in 3D.
658 ///
659 /// # Example
660 /// ```
661 /// # use bevy_gizmos::prelude::*;
662 /// # use bevy_transform::prelude::*;
663 /// # use bevy_math::Vec3;
664 /// # use bevy_shape::Aabb3d;
665 /// # use bevy_color::palettes::basic::GREEN;
666 /// fn system(mut gizmos: Gizmos) {
667 /// gizmos.aabb_3d(Aabb3d::new(Vec3::ZERO, Vec3::ONE), Transform::IDENTITY, GREEN);
668 /// }
669 /// # bevy_ecs::system::assert_is_system(system);
670 /// ```
671 #[inline]
672 pub fn aabb_3d(
673 &mut self,
674 aabb: impl Into<Aabb3d>,
675 transform: impl TransformPoint,
676 color: impl Into<Color>,
677 ) {
678 let polymorphic_color: Color = color.into();
679 if !self.enabled {
680 return;
681 }
682 let aabb = aabb.into();
683 let [tlf, trf, brf, blf, tlb, trb, brb, blb] = [
684 Vec3::new(aabb.min.x, aabb.max.y, aabb.max.z),
685 Vec3::new(aabb.max.x, aabb.max.y, aabb.max.z),
686 Vec3::new(aabb.max.x, aabb.min.y, aabb.max.z),
687 Vec3::new(aabb.min.x, aabb.min.y, aabb.max.z),
688 Vec3::new(aabb.min.x, aabb.max.y, aabb.min.z),
689 Vec3::new(aabb.max.x, aabb.max.y, aabb.min.z),
690 Vec3::new(aabb.max.x, aabb.min.y, aabb.min.z),
691 Vec3::new(aabb.min.x, aabb.min.y, aabb.min.z),
692 ]
693 .map(|v| transform.transform_point(v));
694
695 let strip_positions = [
696 tlf, trf, brf, blf, tlf, // Front
697 tlb, trb, brb, blb, tlb, // Back
698 ];
699 self.linestrip(strip_positions, polymorphic_color);
700
701 let list_positions = [
702 trf, trb, brf, brb, blf, blb, // Front to back
703 ];
704 self.extend_list_positions(list_positions);
705
706 self.add_list_color(polymorphic_color, 6);
707 }
708
709 /// Draw a line in 2D from `start` to `end`.
710 ///
711 /// # Example
712 /// ```
713 /// # use bevy_gizmos::prelude::*;
714 /// # use bevy_math::prelude::*;
715 /// # use bevy_color::palettes::basic::GREEN;
716 /// fn system(mut gizmos: Gizmos) {
717 /// gizmos.line_2d(Vec2::ZERO, Vec2::X, GREEN);
718 /// }
719 /// # bevy_ecs::system::assert_is_system(system);
720 /// ```
721 #[inline]
722 pub fn line_2d(&mut self, start: Vec2, end: Vec2, color: impl Into<Color>) {
723 if !self.enabled {
724 return;
725 }
726 self.line(start.extend(0.), end.extend(0.), color);
727 }
728
729 /// Draw a line in 2D with a color gradient from `start` to `end`.
730 ///
731 /// # Example
732 /// ```
733 /// # use bevy_gizmos::prelude::*;
734 /// # use bevy_math::prelude::*;
735 /// # use bevy_color::palettes::basic::{RED, GREEN};
736 /// fn system(mut gizmos: Gizmos) {
737 /// gizmos.line_gradient_2d(Vec2::ZERO, Vec2::X, GREEN, RED);
738 /// }
739 /// # bevy_ecs::system::assert_is_system(system);
740 /// ```
741 #[inline]
742 pub fn line_gradient_2d<C: Into<Color>>(
743 &mut self,
744 start: Vec2,
745 end: Vec2,
746 start_color: C,
747 end_color: C,
748 ) {
749 if !self.enabled {
750 return;
751 }
752 self.line_gradient(start.extend(0.), end.extend(0.), start_color, end_color);
753 }
754
755 /// Draw a line in 2D made of straight segments between the points.
756 ///
757 /// # Example
758 /// ```
759 /// # use bevy_gizmos::prelude::*;
760 /// # use bevy_math::prelude::*;
761 /// # use bevy_color::palettes::basic::GREEN;
762 /// fn system(mut gizmos: Gizmos) {
763 /// gizmos.linestrip_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);
764 /// }
765 /// # bevy_ecs::system::assert_is_system(system);
766 /// ```
767 #[inline]
768 pub fn linestrip_2d(
769 &mut self,
770 positions: impl IntoIterator<Item = Vec2>,
771 color: impl Into<Color>,
772 ) {
773 if !self.enabled {
774 return;
775 }
776 self.linestrip(positions.into_iter().map(|vec2| vec2.extend(0.)), color);
777 }
778
779 /// Draw a line in 2D made of straight segments between the points, with the first and last connected.
780 ///
781 /// # Example
782 /// ```
783 /// # use bevy_gizmos::prelude::*;
784 /// # use bevy_math::prelude::*;
785 /// # use bevy_color::palettes::basic::GREEN;
786 /// fn system(mut gizmos: Gizmos) {
787 /// gizmos.lineloop_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);
788 /// }
789 /// # bevy_ecs::system::assert_is_system(system);
790 /// ```
791 #[inline]
792 pub fn lineloop_2d(
793 &mut self,
794 positions: impl IntoIterator<Item = Vec2>,
795 color: impl Into<Color>,
796 ) {
797 if !self.enabled {
798 return;
799 }
800 self.lineloop(positions.into_iter().map(|vec2| vec2.extend(0.)), color);
801 }
802
803 /// Draw a line in 2D made of straight segments between the points, with a color gradient.
804 ///
805 /// # Example
806 /// ```
807 /// # use bevy_gizmos::prelude::*;
808 /// # use bevy_math::prelude::*;
809 /// # use bevy_color::palettes::basic::{RED, GREEN, BLUE};
810 /// fn system(mut gizmos: Gizmos) {
811 /// gizmos.linestrip_gradient_2d([
812 /// (Vec2::ZERO, GREEN),
813 /// (Vec2::X, RED),
814 /// (Vec2::Y, BLUE)
815 /// ]);
816 /// }
817 /// # bevy_ecs::system::assert_is_system(system);
818 /// ```
819 #[inline]
820 pub fn linestrip_gradient_2d<C: Into<Color>>(
821 &mut self,
822 positions: impl IntoIterator<Item = (Vec2, C)>,
823 ) {
824 if !self.enabled {
825 return;
826 }
827 self.linestrip_gradient(
828 positions
829 .into_iter()
830 .map(|(vec2, color)| (vec2.extend(0.), color)),
831 );
832 }
833
834 /// Draw a line in 2D from `start` to `start + vector`.
835 ///
836 /// # Example
837 /// ```
838 /// # use bevy_gizmos::prelude::*;
839 /// # use bevy_math::prelude::*;
840 /// # use bevy_color::palettes::basic::GREEN;
841 /// fn system(mut gizmos: Gizmos) {
842 /// gizmos.ray_2d(Vec2::Y, Vec2::X, GREEN);
843 /// }
844 /// # bevy_ecs::system::assert_is_system(system);
845 /// ```
846 #[inline]
847 pub fn ray_2d(&mut self, start: Vec2, vector: Vec2, color: impl Into<Color>) {
848 if !self.enabled {
849 return;
850 }
851 self.line_2d(start, start + vector, color);
852 }
853
854 /// Draw a line in 2D with a color gradient from `start` to `start + vector`.
855 ///
856 /// # Example
857 /// ```
858 /// # use bevy_gizmos::prelude::*;
859 /// # use bevy_math::prelude::*;
860 /// # use bevy_color::palettes::basic::{RED, GREEN};
861 /// fn system(mut gizmos: Gizmos) {
862 /// gizmos.line_gradient(Vec3::Y, Vec3::X, GREEN, RED);
863 /// }
864 /// # bevy_ecs::system::assert_is_system(system);
865 /// ```
866 #[inline]
867 pub fn ray_gradient_2d<C: Into<Color>>(
868 &mut self,
869 start: Vec2,
870 vector: Vec2,
871 start_color: C,
872 end_color: C,
873 ) {
874 if !self.enabled {
875 return;
876 }
877 self.line_gradient_2d(start, start + vector, start_color, end_color);
878 }
879
880 /// Draw a wireframe rectangle in 2D with the given `isometry` applied.
881 ///
882 /// If `isometry == Isometry2d::IDENTITY` then
883 ///
884 /// - the center is at `Vec2::ZERO`
885 /// - the sizes are aligned with the `Vec2::X` and `Vec2::Y` axes.
886 ///
887 /// # Example
888 /// ```
889 /// # use bevy_gizmos::prelude::*;
890 /// # use bevy_math::prelude::*;
891 /// # use bevy_color::palettes::basic::GREEN;
892 /// fn system(mut gizmos: Gizmos) {
893 /// gizmos.rect_2d(Isometry2d::IDENTITY, Vec2::ONE, GREEN);
894 /// }
895 /// # bevy_ecs::system::assert_is_system(system);
896 /// ```
897 #[inline]
898 pub fn rect_2d(
899 &mut self,
900 isometry: impl Into<Isometry2d>,
901 size: Vec2,
902 color: impl Into<Color>,
903 ) {
904 if !self.enabled {
905 return;
906 }
907 let isometry = isometry.into();
908 let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2);
909 self.lineloop_2d([tl, tr, br, bl], color);
910 }
911
912 #[inline]
913 fn extend_list_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
914 self.list_positions.extend(positions);
915 }
916
917 #[inline]
918 fn extend_list_colors(&mut self, colors: impl IntoIterator<Item = impl Into<Color>>) {
919 self.list_colors.extend(
920 colors
921 .into_iter()
922 .map(|color| LinearRgba::from(color.into())),
923 );
924 }
925
926 #[inline]
927 fn add_list_color(&mut self, color: impl Into<Color>, count: usize) {
928 let polymorphic_color: Color = color.into();
929 let linear_color = LinearRgba::from(polymorphic_color);
930
931 self.list_colors.extend(iter::repeat_n(linear_color, count));
932 }
933
934 #[inline]
935 fn extend_strip_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
936 self.strip_positions.extend(positions);
937 self.strip_positions.push(Vec3::NAN);
938 }
939}
940
941fn rect_inner(size: Vec2) -> [Vec2; 4] {
942 let half_size = size / 2.;
943 let tl = Vec2::new(-half_size.x, half_size.y);
944 let tr = Vec2::new(half_size.x, half_size.y);
945 let bl = Vec2::new(-half_size.x, -half_size.y);
946 let br = Vec2::new(half_size.x, -half_size.y);
947 [tl, tr, br, bl]
948}