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