1use core::ops::{Deref, DerefMut};
4
5use bevy_asset::Handle;
6#[cfg(feature = "bevy_render")]
7use bevy_camera::visibility::RenderLayers;
8use bevy_ecs::{component::Component, reflect::ReflectComponent};
9use bevy_reflect::{std_traits::ReflectDefault, Reflect};
10use bevy_transform::components::Transform;
11
12#[cfg(feature = "bevy_render")]
13use {
14 crate::{config::GizmoLineJoint, LineGizmoUniform},
15 bevy_ecs::{
16 entity::Entity,
17 system::{Commands, Local, Query},
18 },
19 bevy_render::Extract,
20 bevy_transform::components::GlobalTransform,
21};
22
23use crate::{
24 config::{ErasedGizmoConfigGroup, GizmoLineConfig},
25 gizmos::GizmoBuffer,
26 GizmoAsset,
27};
28
29impl Deref for GizmoAsset {
30 type Target = GizmoBuffer<ErasedGizmoConfigGroup, ()>;
31
32 fn deref(&self) -> &Self::Target {
33 &self.buffer
34 }
35}
36
37impl DerefMut for GizmoAsset {
38 fn deref_mut(&mut self) -> &mut Self::Target {
39 &mut self.buffer
40 }
41}
42
43#[derive(Component, Clone, Debug, Default, Reflect)]
78#[reflect(Component, Clone, Default)]
79#[require(Transform)]
80pub struct Gizmo {
81 pub handle: Handle<GizmoAsset>,
83 pub line_config: GizmoLineConfig,
85 pub depth_bias: f32,
98}
99
100#[cfg(feature = "bevy_render")]
101pub(crate) fn extract_linegizmos(
102 mut commands: Commands,
103 mut previous_len: Local<usize>,
104 query: Extract<Query<(Entity, &Gizmo, &GlobalTransform, Option<&RenderLayers>)>>,
105) {
106 use bevy_math::Affine3;
107 use bevy_render::sync_world::{MainEntity, TemporaryRenderEntity};
108 use bevy_utils::once;
109 use tracing::warn;
110
111 use crate::config::GizmoLineStyle;
112
113 let mut values = Vec::with_capacity(*previous_len);
114 for (entity, gizmo, transform, render_layers) in &query {
115 let joints_resolution = if let GizmoLineJoint::Round(resolution) = gizmo.line_config.joints
116 {
117 resolution
118 } else {
119 0
120 };
121 let (gap_scale, line_scale) = if let GizmoLineStyle::Dashed {
122 gap_scale,
123 line_scale,
124 } = gizmo.line_config.style
125 {
126 if gap_scale <= 0.0 {
127 once!(warn!("when using gizmos with the line style `GizmoLineStyle::Dashed{{..}}` the gap scale should be greater than zero"));
128 }
129 if line_scale <= 0.0 {
130 once!(warn!("when using gizmos with the line style `GizmoLineStyle::Dashed{{..}}` the line scale should be greater than zero"));
131 }
132 (gap_scale, line_scale)
133 } else {
134 (1.0, 1.0)
135 };
136
137 values.push((
138 LineGizmoUniform {
139 world_from_local: Affine3::from(&transform.affine()).to_transpose(),
140 line_width: gizmo.line_config.width,
141 depth_bias: gizmo.depth_bias,
142 joints_resolution,
143 gap_scale,
144 line_scale,
145 #[cfg(feature = "webgl")]
146 _padding: Default::default(),
147 },
148 #[cfg(any(feature = "bevy_pbr", feature = "bevy_sprite_render"))]
149 crate::config::GizmoMeshConfig {
150 line_perspective: gizmo.line_config.perspective,
151 line_style: gizmo.line_config.style,
152 line_joints: gizmo.line_config.joints,
153 render_layers: render_layers.cloned().unwrap_or_default(),
154 handle: gizmo.handle.clone(),
155 },
156 MainEntity::from(entity),
157 TemporaryRenderEntity,
158 ));
159 }
160 *previous_len = values.len();
161 commands.spawn_batch(values);
162}