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