Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_gizmos/src/lib.rs
6595 views
1
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2
#![doc(
3
html_logo_url = "https://bevy.org/assets/icon.png",
4
html_favicon_url = "https://bevy.org/assets/icon.png"
5
)]
6
7
//! This crate adds an immediate mode drawing api to Bevy for visual debugging.
8
//!
9
//! # Example
10
//! ```
11
//! # use bevy_gizmos::prelude::*;
12
//! # use bevy_math::prelude::*;
13
//! # use bevy_color::palettes::basic::GREEN;
14
//! fn system(mut gizmos: Gizmos) {
15
//! gizmos.line(Vec3::ZERO, Vec3::X, GREEN);
16
//! }
17
//! # bevy_ecs::system::assert_is_system(system);
18
//! ```
19
//!
20
//! See the documentation on [Gizmos](crate::gizmos::Gizmos) for more examples.
21
22
// Required to make proc macros work in bevy itself.
23
extern crate self as bevy_gizmos;
24
25
/// System set label for the systems handling the rendering of gizmos.
26
#[derive(SystemSet, Clone, Debug, Hash, PartialEq, Eq)]
27
pub enum GizmoRenderSystems {
28
/// Adds gizmos to the [`Transparent2d`](bevy_core_pipeline::core_2d::Transparent2d) render phase
29
#[cfg(feature = "bevy_sprite")]
30
QueueLineGizmos2d,
31
/// Adds gizmos to the [`Transparent3d`](bevy_core_pipeline::core_3d::Transparent3d) render phase
32
#[cfg(feature = "bevy_pbr")]
33
QueueLineGizmos3d,
34
}
35
36
/// Deprecated alias for [`GizmoRenderSystems`].
37
#[deprecated(since = "0.17.0", note = "Renamed to `GizmoRenderSystems`.")]
38
pub type GizmoRenderSystem = GizmoRenderSystems;
39
40
#[cfg(feature = "bevy_render")]
41
pub mod aabb;
42
pub mod arcs;
43
pub mod arrows;
44
pub mod circles;
45
pub mod config;
46
pub mod cross;
47
pub mod curves;
48
pub mod gizmos;
49
pub mod grid;
50
pub mod primitives;
51
pub mod retained;
52
pub mod rounded_box;
53
54
#[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))]
55
pub mod light;
56
57
#[cfg(all(feature = "bevy_sprite", feature = "bevy_render"))]
58
mod pipeline_2d;
59
#[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))]
60
mod pipeline_3d;
61
62
/// The gizmos prelude.
63
///
64
/// This includes the most common types in this crate, re-exported for your convenience.
65
pub mod prelude {
66
#[cfg(feature = "bevy_render")]
67
pub use crate::aabb::{AabbGizmoConfigGroup, ShowAabbGizmo};
68
69
#[doc(hidden)]
70
pub use crate::{
71
config::{
72
DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore,
73
GizmoLineConfig, GizmoLineJoint, GizmoLineStyle,
74
},
75
gizmos::Gizmos,
76
primitives::{dim2::GizmoPrimitive2d, dim3::GizmoPrimitive3d},
77
retained::Gizmo,
78
AppGizmoBuilder, GizmoAsset,
79
};
80
81
#[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))]
82
pub use crate::light::{LightGizmoColor, LightGizmoConfigGroup, ShowLightGizmo};
83
}
84
85
use bevy_app::{App, FixedFirst, FixedLast, Last, Plugin, RunFixedMainLoop};
86
use bevy_asset::{Asset, AssetApp, Assets, Handle};
87
use bevy_ecs::{
88
resource::Resource,
89
schedule::{IntoScheduleConfigs, SystemSet},
90
system::{Res, ResMut},
91
};
92
use bevy_reflect::TypePath;
93
94
#[cfg(all(
95
feature = "bevy_render",
96
any(feature = "bevy_pbr", feature = "bevy_sprite")
97
))]
98
use {crate::config::GizmoMeshConfig, bevy_mesh::VertexBufferLayout};
99
100
use crate::{config::ErasedGizmoConfigGroup, gizmos::GizmoBuffer};
101
102
#[cfg(feature = "bevy_render")]
103
use {
104
crate::retained::extract_linegizmos,
105
bevy_asset::AssetId,
106
bevy_ecs::{
107
component::Component,
108
entity::Entity,
109
query::ROQueryItem,
110
system::{
111
lifetimeless::{Read, SRes},
112
Commands, SystemParamItem,
113
},
114
},
115
bevy_math::{Affine3, Affine3A, Vec4},
116
bevy_render::{
117
extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
118
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
119
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
120
render_resource::{
121
binding_types::uniform_buffer, BindGroup, BindGroupEntries, BindGroupLayout,
122
BindGroupLayoutEntries, Buffer, BufferInitDescriptor, BufferUsages, ShaderStages,
123
ShaderType, VertexFormat,
124
},
125
renderer::RenderDevice,
126
sync_world::{MainEntity, TemporaryRenderEntity},
127
Extract, ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
128
},
129
bytemuck::cast_slice,
130
};
131
132
#[cfg(all(
133
feature = "bevy_render",
134
any(feature = "bevy_pbr", feature = "bevy_sprite"),
135
))]
136
use bevy_render::render_resource::{VertexAttribute, VertexStepMode};
137
use bevy_time::Fixed;
138
use bevy_utils::TypeIdMap;
139
#[cfg(feature = "bevy_render")]
140
use config::GizmoLineJoint;
141
use config::{DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore};
142
use core::{any::TypeId, marker::PhantomData, mem};
143
use gizmos::{GizmoStorage, Swap};
144
#[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))]
145
use light::LightGizmoPlugin;
146
147
/// A [`Plugin`] that provides an immediate mode drawing api for visual debugging.
148
///
149
/// Requires to be loaded after [`PbrPlugin`](bevy_pbr::PbrPlugin) or [`SpritePlugin`](bevy_sprite::SpritePlugin).
150
#[derive(Default)]
151
pub struct GizmoPlugin;
152
153
impl Plugin for GizmoPlugin {
154
fn build(&self, app: &mut App) {
155
#[cfg(feature = "bevy_render")]
156
{
157
use bevy_asset::embedded_asset;
158
embedded_asset!(app, "lines.wgsl");
159
embedded_asset!(app, "line_joints.wgsl");
160
}
161
162
app.init_asset::<GizmoAsset>()
163
.init_resource::<GizmoHandles>()
164
// We insert the Resource GizmoConfigStore into the world implicitly here if it does not exist.
165
.init_gizmo_group::<DefaultGizmoConfigGroup>();
166
167
#[cfg(feature = "bevy_render")]
168
app.add_plugins(aabb::AabbGizmoPlugin)
169
.add_plugins(UniformComponentPlugin::<LineGizmoUniform>::default())
170
.add_plugins(RenderAssetPlugin::<GpuLineGizmo>::default());
171
172
#[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))]
173
app.add_plugins(LightGizmoPlugin);
174
175
#[cfg(feature = "bevy_render")]
176
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
177
render_app.add_systems(RenderStartup, init_line_gizmo_uniform_bind_group_layout);
178
179
render_app.add_systems(
180
Render,
181
prepare_line_gizmo_bind_group.in_set(RenderSystems::PrepareBindGroups),
182
);
183
184
render_app.add_systems(ExtractSchedule, (extract_gizmo_data, extract_linegizmos));
185
186
#[cfg(feature = "bevy_sprite")]
187
if app.is_plugin_added::<bevy_sprite::SpritePlugin>() {
188
app.add_plugins(pipeline_2d::LineGizmo2dPlugin);
189
} else {
190
tracing::warn!("bevy_sprite feature is enabled but bevy_sprite::SpritePlugin was not detected. Are you sure you loaded GizmoPlugin after SpritePlugin?");
191
}
192
#[cfg(feature = "bevy_pbr")]
193
if app.is_plugin_added::<bevy_pbr::PbrPlugin>() {
194
app.add_plugins(pipeline_3d::LineGizmo3dPlugin);
195
} else {
196
tracing::warn!("bevy_pbr feature is enabled but bevy_pbr::PbrPlugin was not detected. Are you sure you loaded GizmoPlugin after PbrPlugin?");
197
}
198
} else {
199
tracing::warn!("bevy_render feature is enabled but RenderApp was not detected. Are you sure you loaded GizmoPlugin after RenderPlugin?");
200
}
201
}
202
}
203
204
/// A extension trait adding `App::init_gizmo_group` and `App::insert_gizmo_config`.
205
pub trait AppGizmoBuilder {
206
/// Registers [`GizmoConfigGroup`] in the app enabling the use of [Gizmos&lt;Config&gt;](crate::gizmos::Gizmos).
207
///
208
/// Configurations can be set using the [`GizmoConfigStore`] [`Resource`].
209
fn init_gizmo_group<Config: GizmoConfigGroup>(&mut self) -> &mut Self;
210
211
/// Insert a [`GizmoConfig`] into a specific [`GizmoConfigGroup`].
212
///
213
/// This method should be preferred over [`AppGizmoBuilder::init_gizmo_group`] if and only if you need to configure fields upon initialization.
214
fn insert_gizmo_config<Config: GizmoConfigGroup>(
215
&mut self,
216
group: Config,
217
config: GizmoConfig,
218
) -> &mut Self;
219
}
220
221
impl AppGizmoBuilder for App {
222
fn init_gizmo_group<Config: GizmoConfigGroup>(&mut self) -> &mut Self {
223
if self.world().contains_resource::<GizmoStorage<Config, ()>>() {
224
return self;
225
}
226
227
self.world_mut()
228
.get_resource_or_init::<GizmoConfigStore>()
229
.register::<Config>();
230
231
let mut handles = self.world_mut().get_resource_or_init::<GizmoHandles>();
232
233
handles.handles.insert(TypeId::of::<Config>(), None);
234
235
// These handles are safe to mutate in any order
236
self.allow_ambiguous_resource::<GizmoHandles>();
237
238
self.init_resource::<GizmoStorage<Config, ()>>()
239
.init_resource::<GizmoStorage<Config, Fixed>>()
240
.init_resource::<GizmoStorage<Config, Swap<Fixed>>>()
241
.add_systems(
242
RunFixedMainLoop,
243
start_gizmo_context::<Config, Fixed>
244
.in_set(bevy_app::RunFixedMainLoopSystems::BeforeFixedMainLoop),
245
)
246
.add_systems(FixedFirst, clear_gizmo_context::<Config, Fixed>)
247
.add_systems(FixedLast, collect_requested_gizmos::<Config, Fixed>)
248
.add_systems(
249
RunFixedMainLoop,
250
end_gizmo_context::<Config, Fixed>
251
.in_set(bevy_app::RunFixedMainLoopSystems::AfterFixedMainLoop),
252
)
253
.add_systems(
254
Last,
255
(
256
propagate_gizmos::<Config, Fixed>.before(GizmoMeshSystems),
257
update_gizmo_meshes::<Config>.in_set(GizmoMeshSystems),
258
),
259
);
260
261
self
262
}
263
264
fn insert_gizmo_config<Config: GizmoConfigGroup>(
265
&mut self,
266
group: Config,
267
config: GizmoConfig,
268
) -> &mut Self {
269
self.init_gizmo_group::<Config>();
270
271
self.world_mut()
272
.get_resource_or_init::<GizmoConfigStore>()
273
.insert(config, group);
274
275
self
276
}
277
}
278
279
/// Holds handles to the line gizmos for each gizmo configuration group
280
// As `TypeIdMap` iteration order depends on the order of insertions and deletions, this uses
281
// `Option<Handle>` to be able to reserve the slot when creating the gizmo configuration group.
282
// That way iteration order is stable across executions and depends on the order of configuration
283
// group creation.
284
#[derive(Resource, Default)]
285
struct GizmoHandles {
286
handles: TypeIdMap<Option<Handle<GizmoAsset>>>,
287
}
288
289
/// Start a new gizmo clearing context.
290
///
291
/// Internally this pushes the parent default context into a swap buffer.
292
/// Gizmo contexts should be handled like a stack, so if you push a new context,
293
/// you must pop the context before the parent context ends.
294
pub fn start_gizmo_context<Config, Clear>(
295
mut swap: ResMut<GizmoStorage<Config, Swap<Clear>>>,
296
mut default: ResMut<GizmoStorage<Config, ()>>,
297
) where
298
Config: GizmoConfigGroup,
299
Clear: 'static + Send + Sync,
300
{
301
default.swap(&mut *swap);
302
}
303
304
/// End this gizmo clearing context.
305
///
306
/// Pop the default gizmos context out of the [`Swap<Clear>`] gizmo storage.
307
///
308
/// This must be called before [`GizmoMeshSystems`] in the [`Last`] schedule.
309
pub fn end_gizmo_context<Config, Clear>(
310
mut swap: ResMut<GizmoStorage<Config, Swap<Clear>>>,
311
mut default: ResMut<GizmoStorage<Config, ()>>,
312
) where
313
Config: GizmoConfigGroup,
314
Clear: 'static + Send + Sync,
315
{
316
default.clear();
317
default.swap(&mut *swap);
318
}
319
320
/// Collect the requested gizmos into a specific clear context.
321
pub fn collect_requested_gizmos<Config, Clear>(
322
mut update: ResMut<GizmoStorage<Config, ()>>,
323
mut context: ResMut<GizmoStorage<Config, Clear>>,
324
) where
325
Config: GizmoConfigGroup,
326
Clear: 'static + Send + Sync,
327
{
328
context.append_storage(&update);
329
update.clear();
330
}
331
332
/// Clear out the contextual gizmos.
333
pub fn clear_gizmo_context<Config, Clear>(mut context: ResMut<GizmoStorage<Config, Clear>>)
334
where
335
Config: GizmoConfigGroup,
336
Clear: 'static + Send + Sync,
337
{
338
context.clear();
339
}
340
341
/// Propagate the contextual gizmo into the `Update` storage for rendering.
342
///
343
/// This should be before [`GizmoMeshSystems`].
344
pub fn propagate_gizmos<Config, Clear>(
345
mut update_storage: ResMut<GizmoStorage<Config, ()>>,
346
contextual_storage: Res<GizmoStorage<Config, Clear>>,
347
) where
348
Config: GizmoConfigGroup,
349
Clear: 'static + Send + Sync,
350
{
351
update_storage.append_storage(&*contextual_storage);
352
}
353
354
/// System set for updating the rendering meshes for drawing gizmos.
355
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
356
pub struct GizmoMeshSystems;
357
358
/// Deprecated alias for [`GizmoMeshSystems`].
359
#[deprecated(since = "0.17.0", note = "Renamed to `GizmoMeshSystems`.")]
360
pub type UpdateGizmoMeshes = GizmoMeshSystems;
361
362
/// Prepare gizmos for rendering.
363
///
364
/// This also clears the default `GizmoStorage`.
365
fn update_gizmo_meshes<Config: GizmoConfigGroup>(
366
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
367
mut handles: ResMut<GizmoHandles>,
368
mut storage: ResMut<GizmoStorage<Config, ()>>,
369
) {
370
if storage.list_positions.is_empty() && storage.strip_positions.is_empty() {
371
handles.handles.insert(TypeId::of::<Config>(), None);
372
} else if let Some(handle) = handles.handles.get_mut(&TypeId::of::<Config>()) {
373
if let Some(handle) = handle {
374
let gizmo = gizmo_assets.get_mut(handle.id()).unwrap();
375
376
gizmo.buffer.list_positions = mem::take(&mut storage.list_positions);
377
gizmo.buffer.list_colors = mem::take(&mut storage.list_colors);
378
gizmo.buffer.strip_positions = mem::take(&mut storage.strip_positions);
379
gizmo.buffer.strip_colors = mem::take(&mut storage.strip_colors);
380
} else {
381
let gizmo = GizmoAsset {
382
config_ty: TypeId::of::<Config>(),
383
buffer: GizmoBuffer {
384
enabled: true,
385
list_positions: mem::take(&mut storage.list_positions),
386
list_colors: mem::take(&mut storage.list_colors),
387
strip_positions: mem::take(&mut storage.strip_positions),
388
strip_colors: mem::take(&mut storage.strip_colors),
389
marker: PhantomData,
390
},
391
};
392
393
*handle = Some(gizmo_assets.add(gizmo));
394
}
395
}
396
}
397
398
#[cfg(feature = "bevy_render")]
399
fn init_line_gizmo_uniform_bind_group_layout(
400
mut commands: Commands,
401
render_device: Res<RenderDevice>,
402
) {
403
let line_layout = render_device.create_bind_group_layout(
404
"LineGizmoUniform layout",
405
&BindGroupLayoutEntries::single(
406
ShaderStages::VERTEX,
407
uniform_buffer::<LineGizmoUniform>(true),
408
),
409
);
410
411
commands.insert_resource(LineGizmoUniformBindgroupLayout {
412
layout: line_layout,
413
});
414
}
415
416
#[cfg(feature = "bevy_render")]
417
fn extract_gizmo_data(
418
mut commands: Commands,
419
handles: Extract<Res<GizmoHandles>>,
420
config: Extract<Res<GizmoConfigStore>>,
421
) {
422
use bevy_utils::once;
423
use config::GizmoLineStyle;
424
use tracing::warn;
425
426
for (group_type_id, handle) in &handles.handles {
427
let Some((config, _)) = config.get_config_dyn(group_type_id) else {
428
continue;
429
};
430
431
if !config.enabled {
432
continue;
433
}
434
435
let Some(handle) = handle else {
436
continue;
437
};
438
439
let joints_resolution = if let GizmoLineJoint::Round(resolution) = config.line.joints {
440
resolution
441
} else {
442
0
443
};
444
445
let (gap_scale, line_scale) = if let GizmoLineStyle::Dashed {
446
gap_scale,
447
line_scale,
448
} = config.line.style
449
{
450
if gap_scale <= 0.0 {
451
once!(warn!("When using gizmos with the line style `GizmoLineStyle::Dashed{{..}}` the gap scale should be greater than zero."));
452
}
453
if line_scale <= 0.0 {
454
once!(warn!("When using gizmos with the line style `GizmoLineStyle::Dashed{{..}}` the line scale should be greater than zero."));
455
}
456
(gap_scale, line_scale)
457
} else {
458
(1.0, 1.0)
459
};
460
461
commands.spawn((
462
LineGizmoUniform {
463
world_from_local: Affine3::from(&Affine3A::IDENTITY).to_transpose(),
464
line_width: config.line.width,
465
depth_bias: config.depth_bias,
466
joints_resolution,
467
gap_scale,
468
line_scale,
469
#[cfg(feature = "webgl")]
470
_padding: Default::default(),
471
},
472
#[cfg(any(feature = "bevy_pbr", feature = "bevy_sprite"))]
473
GizmoMeshConfig {
474
line_perspective: config.line.perspective,
475
line_style: config.line.style,
476
line_joints: config.line.joints,
477
render_layers: config.render_layers.clone(),
478
handle: handle.clone(),
479
},
480
// The immediate mode API does not have a main world entity to refer to,
481
// but we do need MainEntity on this render entity for the systems to find it.
482
MainEntity::from(Entity::PLACEHOLDER),
483
TemporaryRenderEntity,
484
));
485
}
486
}
487
488
#[cfg(feature = "bevy_render")]
489
#[derive(Component, ShaderType, Clone, Copy)]
490
struct LineGizmoUniform {
491
world_from_local: [Vec4; 3],
492
line_width: f32,
493
depth_bias: f32,
494
// Only used by gizmo line t if the current configs `line_joints` is set to `GizmoLineJoint::Round(_)`
495
joints_resolution: u32,
496
// Only used if the current configs `line_style` is set to `GizmoLineStyle::Dashed{_}`
497
gap_scale: f32,
498
line_scale: f32,
499
/// WebGL2 structs must be 16 byte aligned.
500
#[cfg(feature = "webgl")]
501
_padding: bevy_math::Vec3,
502
}
503
504
/// A collection of gizmos.
505
///
506
/// Has the same gizmo drawing API as [`Gizmos`](crate::gizmos::Gizmos).
507
#[derive(Asset, Debug, Clone, TypePath)]
508
pub struct GizmoAsset {
509
/// vertex buffers.
510
buffer: GizmoBuffer<ErasedGizmoConfigGroup, ()>,
511
config_ty: TypeId,
512
}
513
514
impl GizmoAsset {
515
/// Create a new [`GizmoAsset`].
516
pub fn new() -> Self {
517
GizmoAsset {
518
buffer: GizmoBuffer::default(),
519
config_ty: TypeId::of::<ErasedGizmoConfigGroup>(),
520
}
521
}
522
523
/// The type of the gizmo's configuration group.
524
pub fn config_typeid(&self) -> TypeId {
525
self.config_ty
526
}
527
}
528
529
impl Default for GizmoAsset {
530
fn default() -> Self {
531
GizmoAsset::new()
532
}
533
}
534
535
#[cfg(feature = "bevy_render")]
536
#[derive(Debug, Clone)]
537
struct GpuLineGizmo {
538
list_position_buffer: Buffer,
539
list_color_buffer: Buffer,
540
list_vertex_count: u32,
541
strip_position_buffer: Buffer,
542
strip_color_buffer: Buffer,
543
strip_vertex_count: u32,
544
}
545
546
#[cfg(feature = "bevy_render")]
547
impl RenderAsset for GpuLineGizmo {
548
type SourceAsset = GizmoAsset;
549
type Param = SRes<RenderDevice>;
550
551
fn prepare_asset(
552
gizmo: Self::SourceAsset,
553
_: AssetId<Self::SourceAsset>,
554
render_device: &mut SystemParamItem<Self::Param>,
555
_: Option<&Self>,
556
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
557
let list_position_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
558
usage: BufferUsages::VERTEX,
559
label: Some("LineGizmo Position Buffer"),
560
contents: cast_slice(&gizmo.buffer.list_positions),
561
});
562
563
let list_color_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
564
usage: BufferUsages::VERTEX,
565
label: Some("LineGizmo Color Buffer"),
566
contents: cast_slice(&gizmo.buffer.list_colors),
567
});
568
569
let strip_position_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
570
usage: BufferUsages::VERTEX,
571
label: Some("LineGizmo Strip Position Buffer"),
572
contents: cast_slice(&gizmo.buffer.strip_positions),
573
});
574
575
let strip_color_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
576
usage: BufferUsages::VERTEX,
577
label: Some("LineGizmo Strip Color Buffer"),
578
contents: cast_slice(&gizmo.buffer.strip_colors),
579
});
580
581
Ok(GpuLineGizmo {
582
list_position_buffer,
583
list_color_buffer,
584
list_vertex_count: gizmo.buffer.list_positions.len() as u32,
585
strip_position_buffer,
586
strip_color_buffer,
587
strip_vertex_count: gizmo.buffer.strip_positions.len() as u32,
588
})
589
}
590
}
591
592
#[cfg(feature = "bevy_render")]
593
#[derive(Resource)]
594
struct LineGizmoUniformBindgroupLayout {
595
layout: BindGroupLayout,
596
}
597
598
#[cfg(feature = "bevy_render")]
599
#[derive(Resource)]
600
struct LineGizmoUniformBindgroup {
601
bindgroup: BindGroup,
602
}
603
604
#[cfg(feature = "bevy_render")]
605
fn prepare_line_gizmo_bind_group(
606
mut commands: Commands,
607
line_gizmo_uniform_layout: Res<LineGizmoUniformBindgroupLayout>,
608
render_device: Res<RenderDevice>,
609
line_gizmo_uniforms: Res<ComponentUniforms<LineGizmoUniform>>,
610
) {
611
if let Some(binding) = line_gizmo_uniforms.uniforms().binding() {
612
commands.insert_resource(LineGizmoUniformBindgroup {
613
bindgroup: render_device.create_bind_group(
614
"LineGizmoUniform bindgroup",
615
&line_gizmo_uniform_layout.layout,
616
&BindGroupEntries::single(binding),
617
),
618
});
619
}
620
}
621
622
#[cfg(feature = "bevy_render")]
623
struct SetLineGizmoBindGroup<const I: usize>;
624
#[cfg(feature = "bevy_render")]
625
impl<const I: usize, P: PhaseItem> RenderCommand<P> for SetLineGizmoBindGroup<I> {
626
type Param = SRes<LineGizmoUniformBindgroup>;
627
type ViewQuery = ();
628
type ItemQuery = Read<DynamicUniformIndex<LineGizmoUniform>>;
629
630
#[inline]
631
fn render<'w>(
632
_item: &P,
633
_view: ROQueryItem<'w, '_, Self::ViewQuery>,
634
uniform_index: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
635
bind_group: SystemParamItem<'w, '_, Self::Param>,
636
pass: &mut TrackedRenderPass<'w>,
637
) -> RenderCommandResult {
638
let Some(uniform_index) = uniform_index else {
639
return RenderCommandResult::Skip;
640
};
641
pass.set_bind_group(
642
I,
643
&bind_group.into_inner().bindgroup,
644
&[uniform_index.index()],
645
);
646
RenderCommandResult::Success
647
}
648
}
649
650
#[cfg(feature = "bevy_render")]
651
struct DrawLineGizmo<const STRIP: bool>;
652
#[cfg(all(
653
feature = "bevy_render",
654
any(feature = "bevy_pbr", feature = "bevy_sprite")
655
))]
656
impl<P: PhaseItem, const STRIP: bool> RenderCommand<P> for DrawLineGizmo<STRIP> {
657
type Param = SRes<RenderAssets<GpuLineGizmo>>;
658
type ViewQuery = ();
659
type ItemQuery = Read<GizmoMeshConfig>;
660
661
#[inline]
662
fn render<'w>(
663
_item: &P,
664
_view: ROQueryItem<'w, '_, Self::ViewQuery>,
665
config: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
666
line_gizmos: SystemParamItem<'w, '_, Self::Param>,
667
pass: &mut TrackedRenderPass<'w>,
668
) -> RenderCommandResult {
669
let Some(config) = config else {
670
return RenderCommandResult::Skip;
671
};
672
let Some(line_gizmo) = line_gizmos.into_inner().get(&config.handle) else {
673
return RenderCommandResult::Skip;
674
};
675
676
let vertex_count = if STRIP {
677
line_gizmo.strip_vertex_count
678
} else {
679
line_gizmo.list_vertex_count
680
};
681
682
if vertex_count < 2 {
683
return RenderCommandResult::Success;
684
}
685
686
let instances = if STRIP {
687
let item_size = VertexFormat::Float32x3.size();
688
let buffer_size = line_gizmo.strip_position_buffer.size() - item_size;
689
690
pass.set_vertex_buffer(0, line_gizmo.strip_position_buffer.slice(..buffer_size));
691
pass.set_vertex_buffer(1, line_gizmo.strip_position_buffer.slice(item_size..));
692
693
let item_size = VertexFormat::Float32x4.size();
694
let buffer_size = line_gizmo.strip_color_buffer.size() - item_size;
695
696
pass.set_vertex_buffer(2, line_gizmo.strip_color_buffer.slice(..buffer_size));
697
pass.set_vertex_buffer(3, line_gizmo.strip_color_buffer.slice(item_size..));
698
699
vertex_count - 1
700
} else {
701
pass.set_vertex_buffer(0, line_gizmo.list_position_buffer.slice(..));
702
pass.set_vertex_buffer(1, line_gizmo.list_color_buffer.slice(..));
703
704
vertex_count / 2
705
};
706
707
pass.draw(0..6, 0..instances);
708
709
RenderCommandResult::Success
710
}
711
}
712
713
#[cfg(feature = "bevy_render")]
714
struct DrawLineJointGizmo;
715
#[cfg(all(
716
feature = "bevy_render",
717
any(feature = "bevy_pbr", feature = "bevy_sprite")
718
))]
719
impl<P: PhaseItem> RenderCommand<P> for DrawLineJointGizmo {
720
type Param = SRes<RenderAssets<GpuLineGizmo>>;
721
type ViewQuery = ();
722
type ItemQuery = Read<GizmoMeshConfig>;
723
724
#[inline]
725
fn render<'w>(
726
_item: &P,
727
_view: ROQueryItem<'w, '_, Self::ViewQuery>,
728
config: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
729
line_gizmos: SystemParamItem<'w, '_, Self::Param>,
730
pass: &mut TrackedRenderPass<'w>,
731
) -> RenderCommandResult {
732
let Some(config) = config else {
733
return RenderCommandResult::Skip;
734
};
735
let Some(line_gizmo) = line_gizmos.into_inner().get(&config.handle) else {
736
return RenderCommandResult::Skip;
737
};
738
739
if line_gizmo.strip_vertex_count <= 2 {
740
return RenderCommandResult::Success;
741
};
742
743
if config.line_joints == GizmoLineJoint::None {
744
return RenderCommandResult::Success;
745
};
746
747
let instances = {
748
let item_size = VertexFormat::Float32x3.size();
749
// position_a
750
let buffer_size_a = line_gizmo.strip_position_buffer.size() - item_size * 2;
751
pass.set_vertex_buffer(0, line_gizmo.strip_position_buffer.slice(..buffer_size_a));
752
// position_b
753
let buffer_size_b = line_gizmo.strip_position_buffer.size() - item_size;
754
pass.set_vertex_buffer(
755
1,
756
line_gizmo
757
.strip_position_buffer
758
.slice(item_size..buffer_size_b),
759
);
760
// position_c
761
pass.set_vertex_buffer(2, line_gizmo.strip_position_buffer.slice(item_size * 2..));
762
763
// color
764
let item_size = VertexFormat::Float32x4.size();
765
let buffer_size = line_gizmo.strip_color_buffer.size() - item_size;
766
// This corresponds to the color of position_b, hence starts from `item_size`
767
pass.set_vertex_buffer(
768
3,
769
line_gizmo.strip_color_buffer.slice(item_size..buffer_size),
770
);
771
772
line_gizmo.strip_vertex_count - 2
773
};
774
775
let vertices = match config.line_joints {
776
GizmoLineJoint::None => unreachable!(),
777
GizmoLineJoint::Miter => 6,
778
GizmoLineJoint::Round(resolution) => resolution * 3,
779
GizmoLineJoint::Bevel => 3,
780
};
781
782
pass.draw(0..vertices, 0..instances);
783
784
RenderCommandResult::Success
785
}
786
}
787
788
#[cfg(all(
789
feature = "bevy_render",
790
any(feature = "bevy_pbr", feature = "bevy_sprite")
791
))]
792
fn line_gizmo_vertex_buffer_layouts(strip: bool) -> Vec<VertexBufferLayout> {
793
use VertexFormat::*;
794
let mut position_layout = VertexBufferLayout {
795
array_stride: Float32x3.size(),
796
step_mode: VertexStepMode::Instance,
797
attributes: vec![VertexAttribute {
798
format: Float32x3,
799
offset: 0,
800
shader_location: 0,
801
}],
802
};
803
804
let mut color_layout = VertexBufferLayout {
805
array_stride: Float32x4.size(),
806
step_mode: VertexStepMode::Instance,
807
attributes: vec![VertexAttribute {
808
format: Float32x4,
809
offset: 0,
810
shader_location: 2,
811
}],
812
};
813
814
if strip {
815
vec![
816
position_layout.clone(),
817
{
818
position_layout.attributes[0].shader_location = 1;
819
position_layout
820
},
821
color_layout.clone(),
822
{
823
color_layout.attributes[0].shader_location = 3;
824
color_layout
825
},
826
]
827
} else {
828
position_layout.array_stride *= 2;
829
position_layout.attributes.push(VertexAttribute {
830
format: Float32x3,
831
offset: Float32x3.size(),
832
shader_location: 1,
833
});
834
835
color_layout.array_stride *= 2;
836
color_layout.attributes.push(VertexAttribute {
837
format: Float32x4,
838
offset: Float32x4.size(),
839
shader_location: 3,
840
});
841
842
vec![position_layout, color_layout]
843
}
844
}
845
846
#[cfg(all(
847
feature = "bevy_render",
848
any(feature = "bevy_pbr", feature = "bevy_sprite")
849
))]
850
fn line_joint_gizmo_vertex_buffer_layouts() -> Vec<VertexBufferLayout> {
851
use VertexFormat::*;
852
let mut position_layout = VertexBufferLayout {
853
array_stride: Float32x3.size(),
854
step_mode: VertexStepMode::Instance,
855
attributes: vec![VertexAttribute {
856
format: Float32x3,
857
offset: 0,
858
shader_location: 0,
859
}],
860
};
861
862
let color_layout = VertexBufferLayout {
863
array_stride: Float32x4.size(),
864
step_mode: VertexStepMode::Instance,
865
attributes: vec![VertexAttribute {
866
format: Float32x4,
867
offset: 0,
868
shader_location: 3,
869
}],
870
};
871
872
vec![
873
position_layout.clone(),
874
{
875
position_layout.attributes[0].shader_location = 1;
876
position_layout.clone()
877
},
878
{
879
position_layout.attributes[0].shader_location = 2;
880
position_layout
881
},
882
color_layout.clone(),
883
]
884
}
885
886