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