1#![cfg_attr(docsrs, feature(doc_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
7extern crate self as bevy_gizmos;
24
25#[derive(SystemSet, Clone, Debug, Hash, PartialEq, Eq)]
27pub enum GizmoRenderSystems {
28 #[cfg(feature = "bevy_sprite_render")]
30 QueueLineGizmos2d,
31 #[cfg(feature = "bevy_pbr")]
33 QueueLineGizmos3d,
34}
35
36#[deprecated(since = "0.17.0", note = "Renamed to `GizmoRenderSystems`.")]
38pub type GizmoRenderSystem = GizmoRenderSystems;
39
40#[cfg(feature = "bevy_render")]
41pub mod aabb;
42pub mod arcs;
43pub mod arrows;
44pub mod circles;
45pub mod config;
46pub mod cross;
47pub mod curves;
48pub mod gizmos;
49pub mod grid;
50pub mod primitives;
51pub mod retained;
52pub mod rounded_box;
53
54#[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))]
55pub mod light;
56
57#[cfg(all(feature = "bevy_sprite_render", feature = "bevy_render"))]
58mod pipeline_2d;
59#[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))]
60mod pipeline_3d;
61
62pub 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
85use bevy_app::{App, FixedFirst, FixedLast, Last, Plugin, RunFixedMainLoop};
86use bevy_asset::{Asset, AssetApp, Assets, Handle};
87use bevy_ecs::{
88 resource::Resource,
89 schedule::{IntoScheduleConfigs, SystemSet},
90 system::{Res, ResMut},
91};
92use bevy_reflect::TypePath;
93
94#[cfg(all(
95 feature = "bevy_render",
96 any(feature = "bevy_pbr", feature = "bevy_sprite_render")
97))]
98use {crate::config::GizmoMeshConfig, bevy_mesh::VertexBufferLayout};
99
100use crate::{config::ErasedGizmoConfigGroup, gizmos::GizmoBuffer};
101
102#[cfg(feature = "bevy_render")]
103use {
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_render"),
135))]
136use bevy_render::render_resource::{VertexAttribute, VertexStepMode};
137use bevy_time::Fixed;
138use bevy_utils::TypeIdMap;
139#[cfg(feature = "bevy_render")]
140use config::GizmoLineJoint;
141use config::{DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore};
142use core::{any::TypeId, marker::PhantomData, mem};
143use gizmos::{GizmoStorage, Swap};
144#[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))]
145use light::LightGizmoPlugin;
146
147#[derive(Default)]
151pub struct GizmoPlugin;
152
153impl 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 .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_render")]
187 if app.is_plugin_added::<bevy_sprite_render::SpriteRenderPlugin>() {
188 app.add_plugins(pipeline_2d::LineGizmo2dPlugin);
189 } else {
190 tracing::warn!("bevy_sprite_render feature is enabled but bevy_sprite_render::SpriteRenderPlugin was not detected. Are you sure you loaded GizmoPlugin after SpriteRenderPlugin?");
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
204pub trait AppGizmoBuilder {
206 fn init_gizmo_group<Config: GizmoConfigGroup>(&mut self) -> &mut Self;
210
211 fn insert_gizmo_config<Config: GizmoConfigGroup>(
215 &mut self,
216 group: Config,
217 config: GizmoConfig,
218 ) -> &mut Self;
219}
220
221impl 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 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#[derive(Resource, Default)]
285struct GizmoHandles {
286 handles: TypeIdMap<Option<Handle<GizmoAsset>>>,
287}
288
289pub 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
304pub 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
320pub 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
332pub fn clear_gizmo_context<Config, Clear>(mut context: ResMut<GizmoStorage<Config, Clear>>)
334where
335 Config: GizmoConfigGroup,
336 Clear: 'static + Send + Sync,
337{
338 context.clear();
339}
340
341pub 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#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
356pub struct GizmoMeshSystems;
357
358#[deprecated(since = "0.17.0", note = "Renamed to `GizmoMeshSystems`.")]
360pub type UpdateGizmoMeshes = GizmoMeshSystems;
361
362fn 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")]
399fn 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")]
417fn 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_render"))]
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 MainEntity::from(Entity::PLACEHOLDER),
483 TemporaryRenderEntity,
484 ));
485 }
486}
487
488#[cfg(feature = "bevy_render")]
489#[derive(Component, ShaderType, Clone, Copy)]
490struct LineGizmoUniform {
491 world_from_local: [Vec4; 3],
492 line_width: f32,
493 depth_bias: f32,
494 joints_resolution: u32,
496 gap_scale: f32,
498 line_scale: f32,
499 #[cfg(feature = "webgl")]
501 _padding: bevy_math::Vec3,
502}
503
504#[derive(Asset, Debug, Clone, TypePath)]
508pub struct GizmoAsset {
509 buffer: GizmoBuffer<ErasedGizmoConfigGroup, ()>,
511 config_ty: TypeId,
512}
513
514impl GizmoAsset {
515 pub fn new() -> Self {
517 GizmoAsset {
518 buffer: GizmoBuffer::default(),
519 config_ty: TypeId::of::<ErasedGizmoConfigGroup>(),
520 }
521 }
522
523 pub fn config_typeid(&self) -> TypeId {
525 self.config_ty
526 }
527}
528
529impl Default for GizmoAsset {
530 fn default() -> Self {
531 GizmoAsset::new()
532 }
533}
534
535#[cfg(feature = "bevy_render")]
536#[derive(Debug, Clone)]
537struct 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")]
547impl 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)]
594struct LineGizmoUniformBindgroupLayout {
595 layout: BindGroupLayout,
596}
597
598#[cfg(feature = "bevy_render")]
599#[derive(Resource)]
600struct LineGizmoUniformBindgroup {
601 bindgroup: BindGroup,
602}
603
604#[cfg(feature = "bevy_render")]
605fn 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")]
623struct SetLineGizmoBindGroup<const I: usize>;
624#[cfg(feature = "bevy_render")]
625impl<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")]
651struct DrawLineGizmo<const STRIP: bool>;
652#[cfg(all(
653 feature = "bevy_render",
654 any(feature = "bevy_pbr", feature = "bevy_sprite_render")
655))]
656impl<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")]
714struct DrawLineJointGizmo;
715#[cfg(all(
716 feature = "bevy_render",
717 any(feature = "bevy_pbr", feature = "bevy_sprite_render")
718))]
719impl<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 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 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 pass.set_vertex_buffer(2, line_gizmo.strip_position_buffer.slice(item_size * 2..));
762
763 let item_size = VertexFormat::Float32x4.size();
765 let buffer_size = line_gizmo.strip_color_buffer.size() - item_size;
766 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_render")
791))]
792fn 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_render")
849))]
850fn 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}